diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index 48312828b1..dbd8239d5b 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -37,7 +37,7 @@ body: label: "🎲 Appwrite version" description: "What version of Appwrite are you running?" options: - - Version 1.0.0-RC1 + - Version 1.0.x - Version 0.15.x - Version 0.14.x - Version 0.13.x diff --git a/CHANGES.md b/CHANGES.md index 34bb192984..68ca4abd17 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +# Version 1.0.1 +## Bugs +- Fixed migration for abuse by migrating the `time` attribute [3839](https://github.com/appwrite/appwrite/pull/3839) + +# Version 1.0.0 ## BREAKING CHANGES - All Date values are now stored as ISO-8601 instead of UNIX timestamps [#3516](https://github.com/appwrite/appwrite/pull/3516) - Permission levels and syntax have been reworked. See the Permissions V2 section in the document for more information [#3700](https://github.com/appwrite/appwrite/pull/3700) diff --git a/README-CN.md b/README-CN.md index 5d94505dfe..82908f1425 100644 --- a/README-CN.md +++ b/README-CN.md @@ -59,7 +59,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:0.15.3 + appwrite/appwrite:1.0.1 ``` ### Windows @@ -71,7 +71,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:0.15.3 + appwrite/appwrite:1.0.1 ``` #### PowerShell @@ -81,7 +81,7 @@ docker run -it --rm , --volume /var/run/docker.sock:/var/run/docker.sock , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --entrypoint="install" , - appwrite/appwrite:0.15.3 + appwrite/appwrite:1.0.1 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index aeb3f97d92..b9a895bc6e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ English | [简体中文](README-CN.md) -[**Appwrite 0.15 has been released! Learn what's new!**](https://dev.to/appwrite/announcing-appwrite-015-with-phone-authentication-more-5cjj) +[**Appwrite 1.0 has been released! Learn what's new!**](https://appwrite.io/1.0) Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster. @@ -65,7 +65,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:0.15.3 + appwrite/appwrite:1.0.1 ``` ### Windows @@ -77,7 +77,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:0.15.3 + appwrite/appwrite:1.0.1 ``` #### PowerShell @@ -87,7 +87,7 @@ docker run -it --rm , --volume /var/run/docker.sock:/var/run/docker.sock , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --entrypoint="install" , - appwrite/appwrite:0.15.3 + appwrite/appwrite:1.0.1 ``` Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after installation completes. diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 874bea516c..034f18165b 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -196,6 +196,35 @@ App::delete('/v1/mock/tests/bar') ->action(function ($required, $default, $z) { }); +/** Endpoint to test if required headers are sent from the SDK */ +App::get('/v1/mock/tests/general/headers') + ->desc('Get headers') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'headers') + ->label('sdk.description', 'Return headers from the request') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.model', Response::MODEL_MOCK) + ->label('sdk.mock', true) + ->inject('request') + ->inject('response') + ->action(function (Request $request, Response $response) { + $res = [ + 'x-sdk-name' => $request->getHeader('x-sdk-name'), + 'x-sdk-platform' => $request->getHeader('x-sdk-platform'), + 'x-sdk-language' => $request->getHeader('x-sdk-language'), + 'x-sdk-version' => $request->getHeader('x-sdk-version'), + ]; + $res = array_map(function ($key, $value) { + return $key . ': ' . $value; + }, array_keys($res), $res); + $res = implode("; ", $res); + + $response->dynamic(new Document(['result' => $res]), Response::MODEL_MOCK); + }); + App::get('/v1/mock/tests/general/download') ->desc('Download File') ->groups(['mock']) diff --git a/app/init.php b/app/init.php index 5cdcc82dd0..3c9f785f9d 100644 --- a/app/init.php +++ b/app/init.php @@ -95,7 +95,7 @@ const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate pe const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 500; -const APP_VERSION_STABLE = '1.0.0'; +const APP_VERSION_STABLE = '1.0.1'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/app/views/console/comps/permissions-matrix.phtml b/app/views/console/comps/permissions-matrix.phtml index 836b0843ad..e912f26629 100644 --- a/app/views/console/comps/permissions-matrix.phtml +++ b/app/views/console/comps/permissions-matrix.phtml @@ -81,6 +81,8 @@ $escapedPermissions = \array_map(function ($perm) { list="types" type="text" x-model="permission.role" + @keydown.enter="prevent($event)" + @keydown="clearPermission(index)" @keyup="updatePermission(index)"/> diff --git a/docs/sdks/cli/GETTING_STARTED.md b/docs/sdks/cli/GETTING_STARTED.md index 4d0f6fcba7..564fb4d5f9 100644 --- a/docs/sdks/cli/GETTING_STARTED.md +++ b/docs/sdks/cli/GETTING_STARTED.md @@ -98,7 +98,7 @@ $ appwrite users list To create a document you can use the following command ```sh -$ appwrite database createDocument --collectionId --documentId 'unique()' --data '{ "Name": "Iron Man" }' --read role:all team:abc +$ appwrite database createDocument --collectionId --documentId 'unique()' --data '{ "Name": "Iron Man" }' --permissions 'read("any")' 'read("team:abc")' ``` ### Some Gotchas diff --git a/docs/sdks/dart/EXAMPLES.md b/docs/sdks/dart/EXAMPLES.md index a16ba88385..208cc7f782 100644 --- a/docs/sdks/dart/EXAMPLES.md +++ b/docs/sdks/dart/EXAMPLES.md @@ -3,14 +3,13 @@ Init your Appwrite client: ```dart - Client client = Client(); - - client - .setEndpoint('https://localhost/v1') // Your Appwrite Endpoint - .setProject('5e8cf4f46b5e8') // Your project ID - .setSelfSigned() // Remove in production - ; +Client client = Client(); +client + .setEndpoint('https://localhost/v1') // Your Appwrite Endpoint + .setProject('5e8cf4f46b5e8') // Your project ID + .setSelfSigned() // Remove in production +; ``` Create a new user: @@ -18,12 +17,11 @@ Create a new user: ```dart Users users = Users(client); -Response result = await users.create( +User result = await users.create( userId: '[USER_ID]', email: 'email@example.com', password: 'password', ); - ``` Fetch user profile: @@ -31,7 +29,7 @@ Fetch user profile: ```dart Users users = Users(client); -Response profile = await users.get( +User profile = await users.get( userId: '[USER_ID]', ); ``` @@ -47,8 +45,9 @@ storage.createFile( bucketId: '[BUCKET_ID]', fileId: '[FILE_ID]', // use 'unique()' to automatically generate a unique ID file: file, - read: ['role:all'], - write: [] + permissions: [ + Permission.read(Role.any()), + ], ) .then((response) { print(response); // File uploaded! diff --git a/docs/sdks/flutter-dev/EXAMPLES.md b/docs/sdks/flutter-dev/EXAMPLES.md index ea92ec6e8e..23b631900f 100644 --- a/docs/sdks/flutter-dev/EXAMPLES.md +++ b/docs/sdks/flutter-dev/EXAMPLES.md @@ -3,14 +3,13 @@ Init your Appwrite client: ```dart - Client client = Client(); - - client - .setEndpoint('https://localhost/v1') // Your Appwrite Endpoint - .setProject('5e8cf4f46b5e8') // Your project ID - .setSelfSigned() // Remove in production - ; +Client client = Client(); +client + .setEndpoint('https://localhost/v1') // Your Appwrite Endpoint + .setProject('5e8cf4f46b5e8') // Your project ID + .setSelfSigned() // Remove in production +; ``` Create a new user and session: @@ -18,9 +17,9 @@ Create a new user and session: ```dart Account account = Account(client); -Response user = await account.create(email: 'me@appwrite.io', password: 'password', name: 'My Name'); +final user = await account.create(userId: '[USER_ID]', email: 'me@appwrite.io', password: 'password', name: 'My Name'); -Response session = await account.createSession(email: 'me@appwrite.io', password: 'password'); +final session = await account.createEmailSession(email: 'me@appwrite.io', password: 'password'); ``` @@ -29,7 +28,7 @@ Fetch user profile: ```dart Account account = Account(client); -Response profile = await account.get(); +final profile = await account.get(); ``` Upload File: @@ -37,12 +36,21 @@ Upload File: ```dart Storage storage = Storage(client); -MultipartFile file = MultipartFile.fromFile('./path-to-file/image.jpg', filename: 'image.jpg'); +late InputFile file; + +if(kIsWeb) { + file = InputFile(bytes: pickedFile.bytes, filename: 'image.jpg'); +} else { + file = InputFile(path: './path-to-file/image.jpg', filename: 'image.jpg'); +} storage.createFile( + bucketId: '[BUCKET_ID]', + fileId: '[FILE_ID]', // use 'unique()' to automatically generate a unique ID file: file, - read: ['role:all'], - write: [] + permissions: [ + Permission.read(Role.any()), + ], ) .then((response) { print(response); // File uploaded! diff --git a/docs/sdks/flutter/EXAMPLES.md b/docs/sdks/flutter/EXAMPLES.md index c20544439a..23b631900f 100644 --- a/docs/sdks/flutter/EXAMPLES.md +++ b/docs/sdks/flutter/EXAMPLES.md @@ -3,14 +3,13 @@ Init your Appwrite client: ```dart - Client client = Client(); - - client - .setEndpoint('https://localhost/v1') // Your Appwrite Endpoint - .setProject('5e8cf4f46b5e8') // Your project ID - .setSelfSigned() // Remove in production - ; +Client client = Client(); +client + .setEndpoint('https://localhost/v1') // Your Appwrite Endpoint + .setProject('5e8cf4f46b5e8') // Your project ID + .setSelfSigned() // Remove in production +; ``` Create a new user and session: @@ -18,9 +17,9 @@ Create a new user and session: ```dart Account account = Account(client); -Response user = await account.create(userId: '[USER_ID]', email: 'me@appwrite.io', password: 'password', name: 'My Name'); +final user = await account.create(userId: '[USER_ID]', email: 'me@appwrite.io', password: 'password', name: 'My Name'); -Response session = await account.createSession(email: 'me@appwrite.io', password: 'password'); +final session = await account.createEmailSession(email: 'me@appwrite.io', password: 'password'); ``` @@ -29,7 +28,7 @@ Fetch user profile: ```dart Account account = Account(client); -Response profile = await account.get(); +final profile = await account.get(); ``` Upload File: @@ -40,7 +39,7 @@ Storage storage = Storage(client); late InputFile file; if(kIsWeb) { - file = InputFile(file: await MultipartFile.fromFile('file', './path-to-file/image.jpg', filename: 'image.jpg')); + file = InputFile(bytes: pickedFile.bytes, filename: 'image.jpg'); } else { file = InputFile(path: './path-to-file/image.jpg', filename: 'image.jpg'); } @@ -49,8 +48,9 @@ storage.createFile( bucketId: '[BUCKET_ID]', fileId: '[FILE_ID]', // use 'unique()' to automatically generate a unique ID file: file, - read: ['role:all'], - write: [] + permissions: [ + Permission.read(Role.any()), + ], ) .then((response) { print(response); // File uploaded! diff --git a/docs/services/databases.md b/docs/services/databases.md index 00076dc8da..0e32fa6c77 100644 --- a/docs/services/databases.md +++ b/docs/services/databases.md @@ -4,4 +4,4 @@ All data returned by the Databases service are represented as structured JSON do The Databases service can contain multiple databases, each database can contain multiple collections. A collection is a group of similarly structured documents. The accepted structure of documents is defined by [collection attributes](/docs/databases#attributes). The collection attributes help you ensure all your user-submitted data is validated and stored according to the collection structure. -Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (`role:all`). You can learn more about [how Appwrite handles permissions and access control](/docs/permissions). \ No newline at end of file +Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (`any`). You can learn more about [how Appwrite handles permissions and access control](/docs/permissions). \ No newline at end of file diff --git a/docs/services/storage.md b/docs/services/storage.md index 54df77891c..0665128a76 100644 --- a/docs/services/storage.md +++ b/docs/services/storage.md @@ -2,7 +2,7 @@ The Storage service allows you to manage your project files. Using the Storage s Files are managed using buckets. Storage buckets are similar to Collections we have in our [Databases](/docs/databases) service. The difference is, buckets also provide more power to decide what kinds of files, what sizes you want to allow in that bucket, whether or not to encrypt the files, scan with antivirus and more. -Using Appwrite permissions architecture, you can assign read or write access to each bucket or file in your project for either a specific user, team, user role, or even grant it with public access (`role:all`). You can learn more about [how Appwrite handles permissions and access control](/docs/permissions). +Using Appwrite permissions architecture, you can assign read or write access to each bucket or file in your project for either a specific user, team, user role, or even grant it with public access (`any`). You can learn more about [how Appwrite handles permissions and access control](/docs/permissions). The preview endpoint allows you to generate preview images for your files. Using the preview endpoint, you can also manipulate the resulting image so that it will fit perfectly inside your app in terms of dimensions, file size, and style. The preview endpoint also allows you to change the resulting image file format for better compression or image quality for better delivery over the network. diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index 11b2bcdfdf..4ebaa92ba3 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -4013,7 +4013,7 @@ this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePer if(existing===undefined){let newPermission={role,create:false,read:false,update:false,xdelete:false,};newPermission[type]=true;this.permissions.push(newPermission);} if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId){if(this.permissions.length>0&&!this.validate(formId,this.permissions.length-1)){return;} this.permissions.push({role:'',create:false,read:false,update:false,xdelete:false,});},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;} -const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';} +const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},clearPermission(index){let currentRole=this.permissions[index].role;this.rawPermissions=this.rawPermissions.filter(p=>{let{type,role}=this.parsePermission(p);return role!==currentRole;});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';} return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';} return key;},validate(formId,index){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input${index}`);const permission=this.permissions[index];input.setCustomValidity('');if(permission.role===''){input.setCustomValidity('Role is required');}else if(!Object.entries(permission).some(([k,v])=>!k.includes('role')&&v)){input.setCustomValidity('No permissions selected');}else if(this.permissions.some(p=>p.role===permission.role&&p!==permission)){input.setCustomValidity('Role entry already exists');} return form.reportValidity();},prevent(event){event.preventDefault();event.stopPropagation();}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index 40edce16e7..bbdda3c914 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -607,7 +607,7 @@ this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePer if(existing===undefined){let newPermission={role,create:false,read:false,update:false,xdelete:false,};newPermission[type]=true;this.permissions.push(newPermission);} if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId){if(this.permissions.length>0&&!this.validate(formId,this.permissions.length-1)){return;} this.permissions.push({role:'',create:false,read:false,update:false,xdelete:false,});},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;} -const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';} +const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},clearPermission(index){let currentRole=this.permissions[index].role;this.rawPermissions=this.rawPermissions.filter(p=>{let{type,role}=this.parsePermission(p);return role!==currentRole;});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';} return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';} return key;},validate(formId,index){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input${index}`);const permission=this.permissions[index];input.setCustomValidity('');if(permission.role===''){input.setCustomValidity('Role is required');}else if(!Object.entries(permission).some(([k,v])=>!k.includes('role')&&v)){input.setCustomValidity('No permissions selected');}else if(this.permissions.some(p=>p.role===permission.role&&p!==permission)){input.setCustomValidity('Role entry already exists');} return form.reportValidity();},prevent(event){event.preventDefault();event.stopPropagation();}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();} diff --git a/public/scripts/permissions-matrix.js b/public/scripts/permissions-matrix.js index 81a869a8f4..5196e1cbc1 100644 --- a/public/scripts/permissions-matrix.js +++ b/public/scripts/permissions-matrix.js @@ -75,6 +75,14 @@ }); }); }, + clearPermission(index) { + let currentRole = this.permissions[index].role; + this.rawPermissions = this.rawPermissions.filter(p => { + let {type, role} = this.parsePermission(p); + + return role !== currentRole; + }); + }, removePermission(index) { let row = this.permissions.splice(index, 1); if (row.length === 1) { diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index a17a962471..c35f5c5df3 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -42,7 +42,8 @@ abstract class Migration */ public static array $versions = [ '1.0.0-RC1' => 'V15', - '1.0.0' => 'V15' + '1.0.0' => 'V15', + '1.0.1' => 'V15', ]; /** diff --git a/src/Appwrite/Migration/Version/V15.php b/src/Appwrite/Migration/Version/V15.php index aa52bf70dd..6c7e59228f 100644 --- a/src/Appwrite/Migration/Version/V15.php +++ b/src/Appwrite/Migration/Version/V15.php @@ -491,6 +491,7 @@ class V15 extends Migration $this->createPermissionsColumn($id); $this->migrateDateTimeAttribute($id, '_createdAt'); $this->migrateDateTimeAttribute($id, '_updatedAt'); + $this->migrateDateTimeAttribute($id, 'time'); break; case 'attributes':