Merge branch '1.8.x' into ci/remove-stave-state

This commit is contained in:
Luke B. Silver 2026-02-13 20:54:56 +00:00 committed by GitHub
commit a151959903
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 192 additions and 128 deletions

3
.env
View file

@ -133,4 +133,5 @@ _APP_PROJECT_REGIONS=default
_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000
_APP_STATS_USAGE_DUAL_WRITING_DBS=database_db_main
_APP_TRUSTED_HEADERS=x-forwarded-for
_APP_POOL_ADAPTER=stack
_APP_POOL_ADAPTER=stack
_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite

View file

@ -3,6 +3,7 @@ concurrency:
group: '${{ github.workflow }}-${{ github.ref }}'
cancel-in-progress: true
env:
COMPOSE_FILE: docker-compose.yml
IMAGE: appwrite-dev
CACHE_KEY: 'appwrite-dev-${{ github.event.pull_request.head.sha }}'
'on':
@ -13,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Docker Buildx
@ -28,6 +29,7 @@ jobs:
cache-from: type=gha
cache-to: 'type=gha,mode=max'
outputs: 'type=docker,dest=/tmp/${{ env.IMAGE }}.tar'
target: development
build-args: |
DEBUG=false
TESTING=true
@ -45,7 +47,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
with:
@ -97,7 +99,7 @@ jobs:
echo "| 200 | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json) | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark-latest.json) | " >> benchmark.txt
echo "| P99 | $(jq -r '.latencyPercentiles.p99' benchmark.json ) | $(jq -r '.latencyPercentiles.p99' benchmark-latest.json ) | " >> benchmark.txt
- name: Save results
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
if: '${{ !cancelled() }}'
with:
name: benchmark.json

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Cleanup
run: |

View file

@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.

View file

@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 2
@ -20,9 +20,9 @@ jobs:
- name: Validate composer.json and composer.lock
run: |
docker run --rm -v $PWD:/app composer sh -c \
docker run --rm -v $PWD:/app composer:2.8 sh -c \
"composer validate"
- name: Run Linter
run: |
docker run --rm -v $PWD:/app composer sh -c \
docker run --rm -v $PWD:/app composer:2.8 sh -c \
"composer install --profile --ignore-platform-reqs && composer lint"

View file

@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
- name: Build the Docker image
run: docker build . -t appwrite_image:latest
run: DOCKER_BUILDKIT=1 docker build . --target production -t appwrite_image:latest
- name: Run Trivy vulnerability scanner on image
uses: aquasecurity/trivy-action@0.20.0
with:
@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Run Trivy vulnerability scanner on filesystem
uses: aquasecurity/trivy-action@0.20.0
with:

View file

@ -11,19 +11,20 @@ jobs:
pull-requests: write
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
submodules: 'recursive'
- name: Build the Docker image
uses: docker/build-push-action@v5
with:
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: pr_image:${{ github.sha }}
target: production
- name: Run Trivy vulnerability scanner on image
uses: aquasecurity/trivy-action@0.20.0
@ -44,7 +45,7 @@ jobs:
- name: Process Trivy scan results
id: process-results
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');

View file

@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 2
submodules: recursive
@ -38,7 +38,7 @@ jobs:
type=ref,event=tag
- name: Build & Publish to DockerHub
uses: docker/build-push-action@v4
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64

View file

@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@ -42,7 +42,7 @@ jobs:
type=semver,pattern={{major}}
- name: Build and push
uses: docker/build-push-action@v4
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64

View file

@ -1,5 +1,8 @@
name: "SDK Preview"
env:
COMPOSE_FILE: docker-compose.yml
on:
pull_request:
paths:
@ -19,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set SDK type
id: set-sdk

View file

@ -8,11 +8,11 @@ jobs:
steps:
- name: Check out the repo
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Run CodeQL
run: |
docker run --rm -v $PWD:/app composer:2.6 sh -c \
docker run --rm -v $PWD:/app composer:2.8 sh -c \
"composer install --profile --ignore-platform-reqs && composer check"
- name: Run Locale check

View file

@ -5,6 +5,7 @@ concurrency:
cancel-in-progress: true
env:
COMPOSE_FILE: docker-compose.yml
IMAGE: appwrite-dev
CACHE_KEY: appwrite-dev-${{ github.event.pull_request.head.sha }}
@ -26,7 +27,7 @@ jobs:
database_changed: ${{ steps.check.outputs.database_changed }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Fetch base branch
run: git fetch origin ${{ github.event.pull_request.base.ref }}
@ -48,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
@ -65,6 +66,7 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max
outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar
target: development
build-args: |
DEBUG=false
TESTING=true
@ -86,7 +88,7 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
@ -132,7 +134,7 @@ jobs:
pull-requests: write
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
@ -217,7 +219,7 @@ jobs:
]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
@ -332,7 +334,7 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
@ -397,7 +399,7 @@ jobs:
pull-requests: write
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
@ -457,7 +459,7 @@ jobs:
]
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
@ -515,7 +517,7 @@ jobs:
pull-requests: write
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4
@ -576,7 +578,7 @@ jobs:
]
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Load Cache
uses: actions/cache@v4

View file

@ -30,9 +30,6 @@ use Utopia\Pools\Group;
use Utopia\Queue\Broker\Pool as BrokerPool;
use Utopia\Queue\Publisher;
use Utopia\Registry\Registry;
use Utopia\Span\Exporter;
use Utopia\Span\Span;
use Utopia\Span\Storage;
use Utopia\System\System;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
@ -340,6 +337,5 @@ $cli
$cli->shutdown()->action(fn () => Timer::clearAll());
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
Span::setStorage(new Storage\Coroutine());
Span::addExporter(new Exporter\Stdout());
require_once __DIR__ . '/init/span.php';
run($cli->run(...));

View file

@ -52,6 +52,7 @@ use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Logger\Logger;
use Utopia\Platform\Service;
use Utopia\Span\Span;
use Utopia\System\System;
use Utopia\Validator;
use Utopia\Validator\Text;
@ -1245,17 +1246,7 @@ Http::error()
$trace = $error->getTrace();
if (php_sapi_name() === 'cli') {
Console::error('[Error] Timestamp: ' . date('c', time()));
if ($route) {
Console::error('[Error] Method: ' . $route->getMethod());
Console::error('[Error] URL: ' . $route->getPath());
}
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $message);
Console::error('[Error] File: ' . $file);
Console::error('[Error] Line: ' . $line);
Span::error($error);
}
switch ($class) {

View file

@ -1,6 +1,7 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/init/span.php';
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
@ -31,6 +32,7 @@ use Utopia\Http\Http;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Pools\Group;
use Utopia\Span\Span;
use Utopia\System\System;
const DOMAIN_SYNC_TIMER = 30; // 30 seconds
@ -167,7 +169,6 @@ $http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) use (&$fil
$files = new Files();
$files->load(__DIR__ . '/../public');
}
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$http->on(Constant::EVENT_WORKER_STOP, function ($server, $workerId) {
@ -207,17 +208,18 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
}
}
Console::success("[Setup] - $dbName database init started...");
Span::init("database.setup");
Span::add('database.name', $dbName);
// Attempt to create the database
try {
Console::info(" └── Creating database: $dbName...");
$database->create();
} catch (\Exception $e) {
Console::info(" └── Skip: metadata table already exists");
Span::add('database.exists', true);
}
// Process collections
$collectionsCreated = 0;
foreach ($collections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
@ -227,8 +229,6 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
continue;
}
Console::info(" └── Creating collection: {$collection['$id']}...");
$attributes = array_map(fn ($attr) => new Document([
'$id' => ID::custom($attr['$id']),
'type' => $attr['type'],
@ -250,14 +250,19 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
]), $collection['indexes']);
$database->createCollection($key, $attributes, $indexes);
$collectionsCreated++;
}
Span::add('database.collections_created', $collectionsCreated);
if ($extraSetup) {
$extraSetup($database);
}
Span::current()?->finish();
}
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $totalWorkers, $register) {
$app = new Http('UTC');
go(function () use ($register, $app) {
@ -282,7 +287,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
}
if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty()) {
Console::info(" └── Creating default bucket...");
$dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('default'),
'$collection' => ID::custom('buckets'),
@ -305,7 +309,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$bucket = $dbForPlatform->getDocument('buckets', 'default');
Console::info(" └── Creating files collection for default bucket...");
$files = $collections['buckets']['files'] ?? [];
if (empty($files)) {
throw new Exception('Files collection is not configured.');
@ -335,7 +338,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
}
if ($authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
Console::info(" └── Creating screenshots bucket...");
$authorization->skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('screenshots'),
'$collection' => ID::custom('buckets'),
@ -353,7 +355,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
Console::info(" └── Creating files collection for screenshots bucket...");
$files = $collections['buckets']['files'] ?? [];
if (empty($files)) {
throw new Exception('Files collection is not configured.');
@ -391,6 +392,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$cache = $app->getResource('cache');
foreach ($sharedTablesV2 as $hostname) {
Span::init('database.setup');
Span::add('database.hostname', $hostname);
$adapter = new DatabasePool($pools->get($hostname));
$dbForProject = (new Database($adapter, $cache))
->setDatabase('appwrite')
@ -399,10 +403,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
->setNamespace(System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', ''));
try {
Console::success('[Setup] - Creating project database: ' . $hostname . '...');
$dbForProject->create();
} catch (DuplicateException) {
Console::success('[Setup] - Skip: metadata table already exists');
Span::add('database.exists', true);
}
if ($dbForProject->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
@ -411,6 +414,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$audit->setup();
}
$collectionsCreated = 0;
foreach ($projectCollections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
@ -422,17 +426,21 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']);
$indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']);
Console::success('[Setup] - Creating project collection: ' . $collection['$id'] . '...');
$dbForProject->createCollection($key, $attributes, $indexes);
$collectionsCreated++;
}
}
Console::success('[Setup] - Server database init completed...');
Span::add('database.collections_created', $collectionsCreated);
Span::current()?->finish();
}
});
Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)');
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");
Span::init('http.server.start');
Span::add('server.workers', $totalWorkers);
Span::add('server.payload_size', $payloadSize);
Span::add('server.master_pid', $http->master_pid);
Span::add('server.manager_pid', $http->manager_pid);
Span::current()?->finish();
// Start the task that starts fetching custom domains
$http->task([], 0);
@ -445,12 +453,16 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
});
$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, &$files) {
Span::init('http.request');
Http::setResource('swooleRequest', fn () => $swooleRequest);
Http::setResource('swooleResponse', fn () => $swooleResponse);
$request = new Request($swooleRequest);
$response = new Response($swooleResponse);
Span::add('http.method', $request->getMethod());
if ($files instanceof Files && $files->isFileLoaded($request->getURI())) {
$time = (60 * 60 * 24 * 45); // 45 days cache
@ -479,7 +491,12 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$authorization->addRole(Role::any()->toString());
$app->run($request, $response);
$route = $app->getRoute();
Span::add('http.path', $route?->getPath() ?? 'unknown');
} catch (\Throwable $th) {
Span::error($th);
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$logger = $app->getResource("logger");
@ -542,12 +559,6 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
}
}
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
Console::error('[Error] Line: ' . $th->getLine());
Console::error('[Error] Trace: ' . $th->getTraceAsString());
$swooleResponse->setStatusCode(500);
$output = ((Http::isDevelopment())) ? [
@ -564,6 +575,9 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
];
$swooleResponse->end(\json_encode($output));
} finally {
Span::add('http.response.code', $response->getStatusCode());
Span::current()?->finish();
}
});

8
app/init/span.php Normal file
View file

@ -0,0 +1,8 @@
<?php
use Utopia\Span\Exporter;
use Utopia\Span\Span;
use Utopia\Span\Storage;
Span::setStorage(new Storage\Coroutine());
Span::addExporter(new Exporter\Pretty());

View file

@ -45,17 +45,13 @@ use Utopia\Queue\Message;
use Utopia\Queue\Publisher;
use Utopia\Queue\Server;
use Utopia\Registry\Registry;
use Utopia\Span\Exporter;
use Utopia\Span\Span;
use Utopia\Span\Storage;
use Utopia\Storage\Device\Telemetry as TelemetryDevice;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
Runtime::enableCoroutine();
Span::setStorage(new Storage\Coroutine());
Span::addExporter(new Exporter\Stdout());
require_once __DIR__ . '/init/span.php';
global $register;
Server::setResource('register', fn () => $register);

View file

@ -57,7 +57,7 @@
"utopia-php/detector": "0.2.*",
"utopia-php/domains": "1.*",
"utopia-php/emails": "0.6.*",
"utopia-php/dns": "1.5.*",
"utopia-php/dns": "1.6.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.5.*",
@ -68,6 +68,7 @@
"utopia-php/migration": "1.5.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "1.*",
"utopia-php/span": "1.1.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.15.*",
"utopia-php/registry": "0.5.*",
@ -91,7 +92,7 @@
"appwrite/sdk-generator": "*",
"phpunit/phpunit": "9.*",
"swoole/ide-helper": "6.*",
"phpstan/phpstan": "1.8.*",
"phpstan/phpstan": "1.12.*",
"textalk/websocket": "1.5.*",
"laravel/pint": "1.*",
"phpbench/phpbench": "1.*"

55
composer.lock generated
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": "57189be7990142e6a44a13eefdfe3043",
"content-hash": "9397ae16877660a3ea485cfdcaab906c",
"packages": [
{
"name": "adhocore/jwt",
@ -3948,22 +3948,22 @@
},
{
"name": "utopia-php/dns",
"version": "1.5.4",
"version": "1.6.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/dns.git",
"reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6"
"reference": "98c70520213a41e2fe1867e5b110273c06bf1cab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/ee831a6f2ceb28babb042ea65539c26ea4530bf6",
"reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/98c70520213a41e2fe1867e5b110273c06bf1cab",
"reference": "98c70520213a41e2fe1867e5b110273c06bf1cab",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/domains": "1.0.*",
"utopia-php/span": "1.0.*",
"utopia-php/span": "1.1.*",
"utopia-php/telemetry": "*",
"utopia-php/validators": "0.*"
},
@ -3999,9 +3999,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/dns/issues",
"source": "https://github.com/utopia-php/dns/tree/1.5.4"
"source": "https://github.com/utopia-php/dns/tree/1.6.2"
},
"time": "2026-02-02T10:40:38+00:00"
"time": "2026-02-13T12:29:08+00:00"
},
{
"name": "utopia-php/domains",
@ -4909,25 +4909,26 @@
},
{
"name": "utopia-php/span",
"version": "1.0.0",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/span.git",
"reference": "f2f6c499ded3a776e8019902e83d140ff0f89693"
"reference": "49d04aa588a2cdbbc9381ee7a1c129469e0f905c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/span/zipball/f2f6c499ded3a776e8019902e83d140ff0f89693",
"reference": "f2f6c499ded3a776e8019902e83d140ff0f89693",
"url": "https://api.github.com/repos/utopia-php/span/zipball/49d04aa588a2cdbbc9381ee7a1c129469e0f905c",
"reference": "49d04aa588a2cdbbc9381ee7a1c129469e0f905c",
"shasum": ""
},
"require": {
"php": ">=8.1"
"php": ">=8.2"
},
"require-dev": {
"laravel/pint": "^1.0",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.0",
"rector/rector": "^2.3",
"swoole/ide-helper": "^5.0"
},
"suggest": {
@ -4946,9 +4947,9 @@
"description": "Simple span tracing library for PHP with coroutine support",
"support": {
"issues": "https://github.com/utopia-php/span/issues",
"source": "https://github.com/utopia-php/span/tree/1.0.0"
"source": "https://github.com/utopia-php/span/tree/1.1.4"
},
"time": "2026-01-12T20:05:10+00:00"
"time": "2026-02-13T10:58:12+00:00"
},
{
"name": "utopia-php/storage",
@ -6238,16 +6239,11 @@
},
{
"name": "phpstan/phpstan",
"version": "1.8.11",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "46e223dd68a620da18855c23046ddb00940b4014"
},
"version": "1.12.32",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014",
"reference": "46e223dd68a620da18855c23046ddb00940b4014",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8",
"reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8",
"shasum": ""
},
"require": {
@ -6276,8 +6272,11 @@
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.8.11"
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
@ -6287,13 +6286,9 @@
{
"url": "https://github.com/phpstan",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift"
}
],
"time": "2022-10-24T15:45:13+00:00"
"time": "2025-09-30T10:16:31+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -8919,5 +8914,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.9.0"
}

View file

@ -564,6 +564,7 @@ services:
environment:
# Specific
- _APP_BROWSER_HOST
- _APP_WORKER_SCREENSHOTS_ROUTER
# Basic
- _APP_ENV
- _APP_WORKER_PER_CORE

View file

@ -301,7 +301,7 @@ class Create extends Base
if ($async) {
if (is_null($scheduledAt)) {
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
if ($project->getId() != '6862e6a6000cce69f9da') {
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$queueForFunctions
@ -344,7 +344,7 @@ class Create extends Base
->setAttribute('scheduleInternalId', $schedule->getSequence())
->setAttribute('scheduledAt', $scheduledAt);
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
if ($project->getId() != '6862e6a6000cce69f9da') {
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
}
}
@ -505,7 +505,7 @@ class Create extends Base
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT)))
;
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
if ($project->getId() != '6862e6a6000cce69f9da') {
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
}
}

View file

@ -111,15 +111,16 @@ class Screenshots extends Action
throw new \Exception('Bucket not found');
}
$routerHost = System::getEnv('_APP_WORKER_SCREENSHOTS_ROUTER', 'http://appwrite');
$configs = [
'screenshotLight' => [
'headers' => [ 'x-appwrite-hostname' => $rule->getAttribute('domain') ],
'url' => 'http://appwrite/?appwrite-preview=1&appwrite-theme=light',
'url' => $routerHost . '/?appwrite-preview=1&appwrite-theme=light',
'theme' => 'light'
],
'screenshotDark' => [
'headers' => [ 'x-appwrite-hostname' => $rule->getAttribute('domain') ],
'url' => 'http://appwrite/?appwrite-preview=1&appwrite-theme=dark',
'url' => $routerHost . '/?appwrite-preview=1&appwrite-theme=dark',
'theme' => 'dark'
],
];

View file

@ -7,7 +7,6 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Platform\Action;
use Utopia\Queue\Message;
use Utopia\System\System;
class Executions extends Action
{
@ -45,7 +44,8 @@ class Executions extends Action
throw new Exception('Missing execution');
}
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
$project = new Document($payload['project'] ?? []);
if ($project->getId() != '6862e6a6000cce69f9da') {
$dbForProject->upsertDocument('executions', $execution);
}
}

View file

@ -185,6 +185,14 @@ class Client
$responseHeaders = [];
$cookies = [];
if (isset($params['queries'])) {
foreach ($params['queries'] as $value) {
if (!is_string($value)) {
throw new Exception('Queries must be converted to strings');
}
}
}
$query = match ($headers['content-type']) {
'application/json' => json_encode($params),
'multipart/form-data' => $this->flatten($params),

View file

@ -2430,22 +2430,36 @@ trait DatabasesBase
$this->assertEquals($document['title'], $response['body']['title']);
$this->assertEquals($document['releaseYear'], $response['body']['releaseYear']);
$this->assertArrayNotHasKey('birthDay', $response['body']);
$sequence = $response['body']['$sequence'];
// Query by sequence
// Query by sequence on get single document route
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence])
Query::equal('$sequence', [$sequence])->toString()
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals('Invalid query method: equal', $response['body']['message']);
// Query by sequence
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence.''])->toString()
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($document['title'], $response['body']['title']);
$this->assertEquals($document['releaseYear'], $response['body']['releaseYear']);
$this->assertTrue(array_key_exists('$sequence', $response['body']));
$this->assertEquals($document['title'], $response['body']['documents'][0]['title']);
$this->assertEquals($document['releaseYear'], $response['body']['documents'][0]['releaseYear']);
$this->assertTrue(array_key_exists('$sequence', $response['body']['documents'][0]));
}
/**

View file

@ -2358,6 +2358,36 @@ trait DatabasesBase
$this->assertEquals($row['title'], $response['body']['title']);
$this->assertEquals($row['releaseYear'], $response['body']['releaseYear']);
$this->assertArrayNotHasKey('birthDay', $response['body']);
$sequence = $response['body']['$sequence'];
// Query by sequence on get single row route
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows/' . $row['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence])->toString()
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals('Invalid query method: equal', $response['body']['message']);
// Query by sequence
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence.''])->toString()
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($row['title'], $response['body']['rows'][0]['title']);
$this->assertEquals($row['releaseYear'], $response['body']['rows'][0]['releaseYear']);
$this->assertTrue(array_key_exists('$sequence', $response['body']['rows'][0]));
}
/**

View file

@ -546,7 +546,7 @@ class ProxyCustomServerTest extends Scope
$rules = $this->listRules([
'search' => $rule1Domain,
'queries' => [ Query::orderDesc('$createdAt') ]
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
]);
$this->assertEquals(200, $rules['headers']['status-code']);
@ -555,7 +555,7 @@ class ProxyCustomServerTest extends Scope
$rules = $this->listRules([
'search' => $rule2Domain,
'queries' => [ Query::orderDesc('$createdAt') ]
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
]);
$this->assertEquals(200, $rules['headers']['status-code']);
$ruleIds = \array_column($rules['body']['rules'], '$id');
@ -563,7 +563,7 @@ class ProxyCustomServerTest extends Scope
$rules = $this->listRules([
'search' => $rule1Id,
'queries' => [ Query::orderDesc('$createdAt') ]
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
]);
$this->assertEquals(200, $rules['headers']['status-code']);
$ruleDomains = \array_column($rules['body']['rules'], 'domain');
@ -571,7 +571,7 @@ class ProxyCustomServerTest extends Scope
$rules = $this->listRules([
'search' => $rule2Id,
'queries' => [ Query::orderDesc('$createdAt') ]
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
]);
$this->assertEquals(200, $rules['headers']['status-code']);
$ruleDomains = \array_column($rules['body']['rules'], 'domain');

View file

@ -155,7 +155,7 @@ class SitesCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('deploymentResourceId', [$siteId])
Query::equal('deploymentResourceId', [$siteId])->toString()
]
]);