Merge remote-tracking branch 'origin/1.8.x' into feat-expose-sequence

This commit is contained in:
Jake Barnby
2025-05-27 13:40:01 +12:00
30 changed files with 314 additions and 141 deletions
+3 -3
View File
@@ -72,7 +72,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:1.7.2
appwrite/appwrite:1.7.4
```
### Windows
@@ -84,7 +84,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:1.7.2
appwrite/appwrite:1.7.4
```
#### PowerShell
@@ -94,7 +94,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:1.7.2
appwrite/appwrite:1.7.4
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
+3 -3
View File
@@ -78,7 +78,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:1.7.2
appwrite/appwrite:1.7.4
```
### Windows
@@ -90,7 +90,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:1.7.2
appwrite/appwrite:1.7.4
```
#### PowerShell
@@ -100,7 +100,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:1.7.2
appwrite/appwrite:1.7.4
```
Once the Docker installation is complete, 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 completing the installation.
+1 -2
View File
@@ -284,6 +284,5 @@ $cli
$cli->shutdown()->action(fn () => Timer::clearAll());
// Enable coroutines, but disable TCP hooks. These don't work until we use `\Utopia\Cache\Adapter\Pool` and `\Utopia\Database\Adapter\Pool`.
Runtime::enableCoroutine(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP);
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
run($cli->run(...));
+24 -1
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.7.0",
"version": "1.7.3",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -4490,6 +4490,29 @@
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"auth": {
"Key": []
},
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/components\/schemas\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
+30 -1
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.7.0",
"version": "1.7.3",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -8049,6 +8049,29 @@
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"auth": {
"Key": []
},
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/components\/schemas\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
@@ -36564,6 +36587,12 @@
"description": "Default value for attribute when not provided. Cannot be set when attribute is required.",
"x-example": "default",
"nullable": true
},
"encrypt": {
"type": "boolean",
"description": "Defines whether this attribute is encrypted or not.",
"x-example": false,
"nullable": true
}
},
"required": [
+30 -1
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.7.0",
"version": "1.7.3",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -7530,6 +7530,29 @@
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"auth": {
"Key": []
},
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/components\/schemas\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
@@ -26532,6 +26555,12 @@
"description": "Default value for attribute when not provided. Cannot be set when attribute is required.",
"x-example": "default",
"nullable": true
},
"encrypt": {
"type": "boolean",
"description": "Defines whether this attribute is encrypted or not.",
"x-example": false,
"nullable": true
}
},
"required": [
+24 -1
View File
@@ -1,7 +1,7 @@
{
"swagger": "2.0",
"info": {
"version": "1.7.0",
"version": "1.7.3",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -4636,6 +4636,29 @@
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"auth": {
"Key": []
},
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/definitions\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
+30 -1
View File
@@ -1,7 +1,7 @@
{
"swagger": "2.0",
"info": {
"version": "1.7.0",
"version": "1.7.3",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -8178,6 +8178,29 @@
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"auth": {
"Key": []
},
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/definitions\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
@@ -36793,6 +36816,12 @@
"description": "Default value for attribute when not provided. Cannot be set when attribute is required.",
"x-example": "default",
"x-nullable": true
},
"encrypt": {
"type": "boolean",
"description": "Defines whether this attribute is encrypted or not.",
"x-example": false,
"x-nullable": true
}
},
"required": [
+30 -1
View File
@@ -1,7 +1,7 @@
{
"swagger": "2.0",
"info": {
"version": "1.7.0",
"version": "1.7.3",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -7649,6 +7649,29 @@
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"auth": {
"Key": []
},
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/definitions\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
}
],
"auth": {
@@ -26828,6 +26851,12 @@
"description": "Default value for attribute when not provided. Cannot be set when attribute is required.",
"x-example": "default",
"x-nullable": true
},
"encrypt": {
"type": "boolean",
"description": "Defines whether this attribute is encrypted or not.",
"x-example": false,
"x-nullable": true
}
},
"required": [
+6
View File
@@ -1352,6 +1352,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
if ($encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string attributes are not available on your plan. Please upgrade to create encrypted string attributes.');
}
if ($encrypt && $size < APP_DATABASE_ENCRYPT_SIZE_MIN) {
throw new Exception(
Exception::GENERAL_BAD_REQUEST,
"Size too small. Encrypted strings require a minimum size of " . APP_DATABASE_ENCRYPT_SIZE_MIN . " characters."
);
}
// Ensure attribute default is within required size
$validator = new Text($size, 0);
if (!is_null($default) && !$validator->isValid($default)) {
+3 -6
View File
@@ -845,18 +845,15 @@ App::get('/v1/health/storage')
$checkStart = \microtime(true);
foreach ($devices as $device) {
$uniqueFileName = \uniqid('health', true);
$filePath = $device->getPath($uniqueFileName);
if (!$device->write($filePath, 'test', 'text/plain')) {
if (!$device->write($device->getPath('health.txt'), 'test', 'text/plain')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed writing test file to ' . $device->getRoot());
}
if ($device->read($filePath) !== 'test') {
if ($device->read($device->getPath('health.txt')) !== 'test') {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed reading test file from ' . $device->getRoot());
}
if (!$device->delete($filePath)) {
if (!$device->delete($device->getPath('health.txt'))) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed deleting test file from ' . $device->getRoot());
}
}
+1 -1
View File
@@ -324,7 +324,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
// VCS commit preview
if (!empty($providerCommitHash)) {
$domain = "commit-{$providerCommitHash}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}";
$domain = "commit-{$providerCommitHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
+3 -2
View File
@@ -37,8 +37,8 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4319;
const APP_VERSION_STABLE = '1.7.2';
const APP_CACHE_BUSTER = 4320;
const APP_VERSION_STABLE = '1.7.4';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@@ -51,6 +51,7 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds
const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
const APP_DATABASE_QUERY_MAX_VALUES = 500;
const APP_DATABASE_ENCRYPT_SIZE_MIN = 150;
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_SITES = '/storage/sites';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
+6 -15
View File
@@ -77,21 +77,12 @@ Database::addFilter(
]);
foreach ($attributes as $attribute) {
$attributeType = $attribute->getAttribute('type');
switch ($attributeType) {
case Database::VAR_RELATIONSHIP:
$options = $attribute->getAttribute('options');
foreach ($options as $key => $value) {
$attribute->setAttribute($key, $value);
}
$attribute->removeAttribute('options');
break;
case Database::VAR_STRING:
$filters = $attribute->getAttribute('filters', []);
$attribute->setAttribute('encrypt', in_array('encrypt', $filters));
break;
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
$options = $attribute->getAttribute('options');
foreach ($options as $key => $value) {
$attribute->setAttribute($key, $value);
}
$attribute->removeAttribute('options');
}
}
+1 -1
View File
@@ -177,7 +177,7 @@ $image = $this->getParam('image', '');
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:6.0.8
image: <?php echo $organization; ?>/console:6.0.13
restart: unless-stopped
networks:
- appwrite
+3 -3
View File
@@ -52,9 +52,9 @@
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/detector": "0.1.*",
"utopia-php/database": "0.71.*",
"utopia-php/domains": "0.8.0",
"utopia-php/detector": "0.1.*",
"utopia-php/domains": "0.8.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.4.*",
@@ -86,7 +86,7 @@
},
"require-dev": {
"ext-fileinfo": "*",
"appwrite/sdk-generator": "0.40.*",
"appwrite/sdk-generator": "0.41.*",
"phpunit/phpunit": "9.*",
"swoole/ide-helper": "5.1.2",
"phpstan/phpstan": "1.8.*",
Generated
+40 -40
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5027a541e6377eb2c87357b69f11daea",
"content-hash": "34142c56876c7ee1e2a798616b8ff114",
"packages": [
{
"name": "adhocore/jwt",
@@ -2486,16 +2486,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.5.1",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"shasum": ""
},
"require": {
@@ -2508,7 +2508,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
"dev-main": "3.6-dev"
}
},
"autoload": {
@@ -2533,7 +2533,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -2549,7 +2549,7 @@
"type": "tidelift"
}
],
"time": "2024-09-25T14:20:29+00:00"
"time": "2024-09-25T14:21:43+00:00"
},
{
"name": "symfony/http-client",
@@ -2648,16 +2648,16 @@
},
{
"name": "symfony/http-client-contracts",
"version": "v3.5.2",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
"reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645"
"reference": "75d7043853a42837e68111812f4d964b01e5101c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645",
"reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c",
"reference": "75d7043853a42837e68111812f4d964b01e5101c",
"shasum": ""
},
"require": {
@@ -2670,7 +2670,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
"dev-main": "3.6-dev"
}
},
"autoload": {
@@ -2706,7 +2706,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2"
"source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -2722,7 +2722,7 @@
"type": "tidelift"
}
],
"time": "2024-12-07T08:49:48+00:00"
"time": "2025-04-29T11:18:49+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@@ -2883,16 +2883,16 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.5.1",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0"
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
"reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
"shasum": ""
},
"require": {
@@ -2910,7 +2910,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
"dev-main": "3.6-dev"
}
},
"autoload": {
@@ -2946,7 +2946,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.5.1"
"source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -2962,7 +2962,7 @@
"type": "tidelift"
}
],
"time": "2024-09-25T14:20:29+00:00"
"time": "2025-04-25T09:37:31+00:00"
},
{
"name": "tbachert/spi",
@@ -3999,16 +3999,16 @@
},
{
"name": "utopia-php/migration",
"version": "0.10.0",
"version": "0.10.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "0b0e94c4b5243c5566b9634b07ba2ec5f7990914"
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/0b0e94c4b5243c5566b9634b07ba2ec5f7990914",
"reference": "0b0e94c4b5243c5566b9634b07ba2ec5f7990914",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ea1c585df7ec5f346f061a11581fc9a91679966f",
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f",
"shasum": ""
},
"require": {
@@ -4049,9 +4049,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.10.0"
"source": "https://github.com/utopia-php/migration/tree/0.10.1"
},
"time": "2025-05-23T07:40:24+00:00"
"time": "2025-05-26T15:29:19+00:00"
},
{
"name": "utopia-php/orchestration",
@@ -4378,16 +4378,16 @@
},
{
"name": "utopia-php/storage",
"version": "0.18.12",
"version": "0.18.13",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97"
"reference": "3d8ce53ae042173bf230445e996056c5f65ded22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97",
"reference": "9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/3d8ce53ae042173bf230445e996056c5f65ded22",
"reference": "3d8ce53ae042173bf230445e996056c5f65ded22",
"shasum": ""
},
"require": {
@@ -4430,9 +4430,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.18.12"
"source": "https://github.com/utopia-php/storage/tree/0.18.13"
},
"time": "2025-05-15T07:55:58+00:00"
"time": "2025-05-26T13:10:35+00:00"
},
{
"name": "utopia-php/swoole",
@@ -4816,16 +4816,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.40.18",
"version": "0.41.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "38de4b9c58112d7e83eb75955994c8412a401093"
"reference": "96316272a3cee1a3abf5b9f05ae49ebbff03725e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/38de4b9c58112d7e83eb75955994c8412a401093",
"reference": "38de4b9c58112d7e83eb75955994c8412a401093",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/96316272a3cee1a3abf5b9f05ae49ebbff03725e",
"reference": "96316272a3cee1a3abf5b9f05ae49ebbff03725e",
"shasum": ""
},
"require": {
@@ -4861,9 +4861,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.18"
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.0"
},
"time": "2025-05-21T14:14:47+00:00"
"time": "2025-05-26T09:47:45+00:00"
},
{
"name": "doctrine/annotations",
+1 -1
View File
@@ -213,7 +213,7 @@ services:
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:6.0.8
image: appwrite/console:6.0.13
restart: unless-stopped
networks:
- appwrite
+2
View File
@@ -87,6 +87,8 @@ abstract class Migration
'1.7.0' => 'V22',
'1.7.1' => 'V22',
'1.7.2' => 'V22',
'1.7.3' => 'V22',
'1.7.4' => 'V22',
];
/**
+26 -8
View File
@@ -12,6 +12,7 @@ use Utopia\Database\Exception\Conflict;
use Utopia\Database\Exception\Structure;
use Utopia\Database\Exception\Timeout;
use Utopia\Database\Query;
use Utopia\System\System;
class V22 extends Migration
{
@@ -324,7 +325,9 @@ class V22 extends Migration
4. Fill "trigger" with "manual"
5. Fill "deploymentResourceType". If "resourceType" is "function", set "deploymentResourceType" to "function"
6. Fill "search" with "{$id} {domain}"
7. Fill "deploymentId" and "deploymentInternalId". If "deploymentResourceType" is "function", get project DB, and find function with ID "resourceId". Then fill rule's "deploymentId" with function's "deployment", and "deploymentId" as backup
7. Set "region" to project region
8. Fill "owner" with "Appwrite" if "domain" ends with "functions" or "sites"
9. Fill "deploymentId" and "deploymentInternalId". If "deploymentResourceType" is "function", get project DB, and find function with ID "resourceId". Then fill rule's "deploymentId" with function's "deployment", and "deploymentId" as backup
*/
$deploymentResourceType = null;
@@ -346,14 +349,29 @@ class V22 extends Migration
->setAttribute('deploymentResourceType', $document->getAttribute('deploymentResourceType', $deploymentResourceType))
->setAttribute('search', \implode(' ', [$document->getId(), $document->getAttribute('domain', '')]));
$project = $this->dbForProject->getDocument('projects', $document->getAttribute('projectId'));
if ($project->isEmpty()) {
Console::warning("Project \"{$document->getAttribute('projectId')}\" not found for rule \"{$document->getId()}\"");
$document->setAttribute('region', System::getEnv('_APP_REGION', 'default'));
break;
}
$document->setAttribute('region', $project->getAttribute('region', System::getEnv('_APP_REGION', 'default')));
$domain = $document->getAttribute('domain', '');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$owner = $document->getAttribute('owner', '');
if (
empty($owner) &&
(!empty($functionsDomain) && \str_ends_with($domain, $functionsDomain)) ||
(!empty($sitesDomain) && \str_ends_with($domain, $sitesDomain))
) {
$document->setAttribute('owner', 'Appwrite');
}
if ($deploymentResourceType === 'function') {
$project = $this->dbForProject->getDocument('projects', $document->getAttribute('projectId'));
if ($project->isEmpty()) {
Console::warning("Project \"{$document->getAttribute('projectId')}\" not found for rule \"{$document->getId()}\"");
break;
}
$dbForOwnerProject = ($this->getProjectDB)($project);
$function = $dbForOwnerProject->getDocument('functions', $resourceId);
@@ -89,8 +89,6 @@ class Update extends Base
throw new Exception(Exception::BUILD_NOT_READY);
}
$oldDeploymentInternalId = $function->getAttribute('deploymentInternalId', '');
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'deploymentInternalId' => $deployment->getSequence(),
'deploymentId' => $deployment->getId(),
@@ -106,18 +104,13 @@ class Update extends Base
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
$queries = [
Query::equal('trigger', 'manual'),
Query::equal('trigger', ['manual']),
Query::equal("type", ["deployment"]),
Query::equal("deploymentResourceType", ["function"]),
Query::equal("deploymentResourceInternalId", [$function->getSequence()]),
Query::equal("deploymentVcsProviderBranch", [""]),
];
if (empty($oldDeploymentInternalId)) {
$queries[] = Query::equal("deploymentInternalId", [""]);
} else {
$queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]);
}
$this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) {
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
@@ -57,6 +57,7 @@ class Builds extends Action
{
$this
->desc('Builds worker')
->groups(['builds'])
->inject('message')
->inject('project')
->inject('dbForPlatform')
@@ -1074,8 +1075,6 @@ class Builds extends Action
$resource->setAttribute('live', true);
switch ($resource->getCollection()) {
case 'functions':
$oldDeploymentInternalId = $resource->getAttribute('deploymentInternalId', '');
$resource->setAttribute('deploymentId', $deployment->getId());
$resource->setAttribute('deploymentInternalId', $deployment->getSequence());
$resource->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt());
@@ -1087,14 +1086,9 @@ class Builds extends Action
Query::equal("deploymentResourceInternalId", [$resource->getSequence()]),
Query::equal('deploymentResourceType', ['function']),
Query::equal('trigger', ['manual']),
Query::equal('deploymentVcsProviderBranch', ['']),
];
if (empty($oldDeploymentInternalId)) {
$queries[] = Query::equal("deploymentInternalId", [""]);
} else {
$queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]);
}
$rulesUpdated = false;
$this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment, &$rulesUpdated) {
$rulesUpdated = true;
@@ -1105,8 +1099,6 @@ class Builds extends Action
});
break;
case 'sites':
$oldDeploymentInternalId = $resource->getAttribute('deploymentInternalId', '');
$resource->setAttribute('deploymentId', $deployment->getId());
$resource->setAttribute('deploymentInternalId', $deployment->getSequence());
$resource->setAttribute('deploymentScreenshotDark', $deployment->getAttribute('screenshotDark', ''));
@@ -1119,14 +1111,9 @@ class Builds extends Action
Query::equal("deploymentResourceInternalId", [$resource->getSequence()]),
Query::equal('deploymentResourceType', ['site']),
Query::equal('trigger', ['manual']),
Query::equal('deploymentVcsProviderBranch', ['']),
];
if (empty($oldDeploymentInternalId)) {
$queries[] = Query::equal("deploymentInternalId", [""]);
} else {
$queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]);
}
$this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) {
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
@@ -347,10 +347,16 @@ class Create extends Action
'domain' => $domain,
'type' => 'deployment',
'trigger' => 'deployment',
'value' => $deployment->getId(),
'deploymentId' => $deployment->isEmpty() ? '' : $deployment->getId(),
'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getSequence(),
'deploymentResourceType' => 'site',
'deploymentResourceId' => $site->getId(),
'deploymentResourceInternalId' => $site->getSequence(),
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
} else {
@@ -86,8 +86,6 @@ class Update extends Base
throw new Exception(Exception::BUILD_NOT_READY);
}
$oldDeploymentInternalId = $site->getAttribute('deploymentInternalId', '');
$site = $dbForProject->updateDocument('sites', $site->getId(), new Document(array_merge($site->getArrayCopy(), [
'deploymentInternalId' => $deployment->getSequence(),
'deploymentId' => $deployment->getId(),
@@ -97,18 +95,13 @@ class Update extends Base
])));
$queries = [
Query::equal('trigger', 'manual'),
Query::equal('trigger', ['manual']),
Query::equal("type", ["deployment"]),
Query::equal("deploymentResourceType", ["site"]),
Query::equal("deploymentResourceInternalId", [$site->getSequence()]),
Query::equal("deploymentVcsProviderBranch", [""]),
];
if (empty($oldDeploymentInternalId)) {
$queries[] = Query::equal("deploymentInternalId", [""]);
} else {
$queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]);
}
$this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) {
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
+5 -5
View File
@@ -79,16 +79,16 @@ abstract class ScheduleBase extends Action
// start with "0" to load all active documents.
$lastSyncUpdate = "0";
$this->collectSchedules($pools, $dbForPlatform, $getProjectDB, $lastSyncUpdate);
$this->collectSchedules($dbForPlatform, $getProjectDB, $lastSyncUpdate);
Console::success("Starting timers at " . DateTime::now());
/**
* The timer synchronize $schedules copy with database collection.
*/
Timer::tick(static::UPDATE_TIMER * 1000, function () use ($pools, $dbForPlatform, $getProjectDB, &$lastSyncUpdate) {
Timer::tick(static::UPDATE_TIMER * 1000, function () use ($dbForPlatform, $getProjectDB, &$lastSyncUpdate) {
$time = DateTime::now();
Console::log("Sync tick: Running at $time");
$this->collectSchedules($pools, $dbForPlatform, $getProjectDB, $lastSyncUpdate);
$this->collectSchedules($dbForPlatform, $getProjectDB, $lastSyncUpdate);
});
while (true) {
@@ -103,7 +103,7 @@ abstract class ScheduleBase extends Action
}
}
private function collectSchedules(Group $pools, Database $dbForPlatform, callable $getProjectDB, string &$lastSyncUpdate): void
private function collectSchedules(Database $dbForPlatform, callable $getProjectDB, string &$lastSyncUpdate): void
{
// If we haven't synced yet, load all active schedules
$initialLoad = $lastSyncUpdate === "0";
@@ -115,7 +115,7 @@ abstract class ScheduleBase extends Action
* @throws Exception
* @var Document $schedule
*/
$getSchedule = function (Document $schedule) use ($pools, $dbForPlatform, $getProjectDB): array {
$getSchedule = function (Document $schedule) use ($dbForPlatform, $getProjectDB): array {
$project = $dbForPlatform->getDocument('projects', $schedule->getAttribute('projectId'));
$resource = $getProjectDB($project)->getDocument(
@@ -76,7 +76,7 @@ class ScheduleFunctions extends ScheduleBase
}
foreach ($delayedExecutions as $delay => $schedules) {
\go(function () use ($delay, $schedules, $pools, $dbForPlatform) {
\go(function () use ($delay, $schedules, $dbForPlatform) {
\sleep($delay); // in seconds
foreach ($schedules as $delayConfig) {
@@ -40,7 +40,7 @@ class ScheduleMessages extends ScheduleBase
continue;
}
\go(function () use ($schedule, $scheduledAt, $pools, $dbForPlatform) {
\go(function () use ($schedule, $scheduledAt, $dbForPlatform) {
$queueForMessaging = new Messaging($this->publisher);
$this->updateProjectAccess($schedule['project'], $dbForPlatform);
+9 -6
View File
@@ -36,6 +36,7 @@ class Comment
$this->builds[$id] = [
'projectName' => $project->getAttribute('name'),
'projectId' => $project->getId(),
'region' => $project->getAttribute('region', 'default'),
'resourceName' => $resource->getAttribute('name'),
'resourceId' => $resource->getId(),
'resourceType' => $resourceType,
@@ -66,6 +67,7 @@ class Comment
if ($build['resourceType'] === 'site') {
$projects[$build['projectId']]['site'][$build['resourceId']] = [
'name' => $build['resourceName'],
'region' => $build['region'],
'status' => $build['buildStatus'],
'deploymentId' => $build['deploymentId'],
'action' => $build['action'],
@@ -74,6 +76,7 @@ class Comment
} elseif ($build['resourceType'] === 'function') {
$projects[$build['projectId']]['function'][$build['resourceId']] = [
'name' => $build['resourceName'],
'region' => $build['region'],
'status' => $build['buildStatus'],
'deploymentId' => $build['deploymentId'],
'action' => $build['action'],
@@ -114,7 +117,7 @@ class Comment
};
if ($site['action']['type'] === 'logs') {
$action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $projectId . '/sites/site-' . $siteId . '/deployments/deployment-' . $site['deploymentId'] . ')';
$action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $site['region'] . '-' . $projectId . '/sites/site-' . $siteId . '/deployments/deployment-' . $site['deploymentId'] . ')';
} else {
$action = '[Authorize](' . $site['action']['url'] . ')';
}
@@ -146,12 +149,12 @@ class Comment
$text .= "| :- | :- | :- | :- |\n";
foreach ($project['function'] as $functionId => $function) {
$extension = $site['status'] === 'building' ? 'gif' : 'png';
$extension = $function['status'] === 'building' ? 'gif' : 'png';
$pathLight = '/images/vcs/status-' . $site['status'] . '-light.' . $extension;
$pathDark = '/images/vcs/status-' . $site['status'] . '-dark.' . $extension;
$pathLight = '/images/vcs/status-' . $function['status'] . '-light.' . $extension;
$pathDark = '/images/vcs/status-' . $function['status'] . '-dark.' . $extension;
$status = match ($site['status']) {
$status = match ($function['status']) {
'waiting' => $this->generatImage($pathLight, $pathDark, 'Queued', 85) . ' _Queued_',
'processing' => $this->generatImage($pathLight, $pathDark, 'Processing', 85) . ' _Processing_',
'building' => $this->generatImage($pathLight, $pathDark, 'Building', 85) . ' _Building_',
@@ -160,7 +163,7 @@ class Comment
};
if ($function['action']['type'] === 'logs') {
$action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $projectId . '/functions/function-' . $functionId . '/deployment-' . $function['deploymentId'] . ')';
$action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $function['region'] . '-' . $projectId . '/functions/function-' . $functionId . '/deployment-' . $function['deploymentId'] . ')';
} else {
$action = '[Authorize](' . $function['action']['url'] . ')';
}
@@ -641,10 +641,10 @@ class DatabasesCustomServerTest extends Scope
/**
* @depends testListCollections
* @group cl-ignore
*/
public function testCreateEncryptedAttribute(array $data): void
{
$databaseId = $data['databaseId'];
/**
@@ -674,7 +674,6 @@ class DatabasesCustomServerTest extends Scope
/**
* Test for creating encrypted attributes
*/
$attributesPath = '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/attributes';
$firstName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
@@ -687,6 +686,19 @@ class DatabasesCustomServerTest extends Scope
'required' => true,
]);
// checking size test
$lastName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'lastName',
'size' => 149,
'required' => true,
'encrypt' => true
]);
$this->assertEquals("Size too small. Encrypted strings require a minimum size of " . APP_DATABASE_ENCRYPT_SIZE_MIN . " characters.", $lastName['body']['message']);
$lastName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -698,12 +710,15 @@ class DatabasesCustomServerTest extends Scope
'encrypt' => true
]);
$this->assertTrue($lastName['body']['encrypt']);
sleep(1);
$response = $this->client->call(Client::METHOD_GET, $attributesPath . '/lastName', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]));
$this->assertTrue($response['body']['encrypt']);
/**
Binary file not shown.