Compare commits

...
64 Commits
Author SHA1 Message Date
Chirag Aggarwal 60fe3acd45 chore: update cache and db dependencies 2025-05-01 05:26:18 +00:00
Jake BarnbyandGitHub bf9deb09f5 Merge pull request #9703 from appwrite/revert-9659-feat-pool-adapter
Revert "Feat pool adapter"
2025-04-30 12:14:55 +00:00
Jake BarnbyandGitHub c759f9ec11 Revert "Feat pool adapter" 2025-04-30 23:24:54 +12:00
Jake BarnbyandGitHub d312fe22ff Merge pull request #9659 from appwrite/feat-pool-adapter
Feat pool adapter
2025-04-30 10:05:47 +00:00
Jake Barnby 6e59859f5c AI review fixes 2025-04-30 21:18:24 +12:00
Jake Barnby 6400b0dc48 Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter 2025-04-30 13:16:56 +12:00
Jake BarnbyandGitHub 44abf276d7 Merge pull request #9478 from appwrite/chore-merge-namespace
Move specification into SDK namespace
2025-04-30 01:15:13 +00:00
Jake Barnby ed0929416b Format 2025-04-30 13:13:14 +12:00
Jake Barnby 2e2af32be3 Fix missing trigger 2025-04-30 13:06:51 +12:00
Jake Barnby ea38a7f869 Coroutine sleep 2025-04-30 13:05:31 +12:00
Jake Barnby 43fb92ca83 Set publisher on base 2025-04-30 13:04:19 +12:00
Jake Barnby f7296d80b2 Fix function schedule sleep 2025-04-30 12:27:18 +12:00
Jake Barnby 706e5f2453 Merge remote-tracking branch 'origin/1.6.x' into chore-merge-namespace 2025-04-30 10:46:54 +12:00
Jake Barnby 5fc2c1ceb8 Fix registry ref 2025-04-30 10:34:24 +12:00
Jake Barnby e643e7a406 Lint 2025-04-29 21:33:51 +12:00
Jake Barnby 02b1c82226 Remove all remaining pool usages 2025-04-29 21:28:53 +12:00
Jake Barnby 4d92af5ba6 Realtime pool adapters 2025-04-29 21:28:40 +12:00
Jake Barnby acec1ab699 Use pool adapters in health api 2025-04-29 21:28:25 +12:00
Jake Barnby 1d82c67d1f Add pubsub pool adapter 2025-04-29 21:28:13 +12:00
Jake Barnby eabf3993a0 Use pool adapter on project create 2025-04-29 18:13:30 +12:00
Jake Barnby 47c5c55267 Remove redundant reclaims 2025-04-29 14:58:54 +12:00
Jake Barnby 43ae7c4b14 Format 2025-04-29 14:57:18 +12:00
Jake Barnby 38aae9e2c1 Fix sync 2025-04-29 14:07:48 +12:00
Jake BarnbyandGitHub 4d2a08e7fd Merge branch '1.6.x' into feat-pool-adapter 2025-04-28 10:02:55 +00:00
Jake Barnby a9e953d840 Lint 2025-04-28 22:02:31 +12:00
Jake Barnby 6caa347b26 Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter
# Conflicts:
#	composer.lock
#	src/Appwrite/Messaging/Adapter/Realtime.php
2025-04-28 21:56:45 +12:00
Christy JacobandGitHub 5578e199e4 Merge pull request #9689 from appwrite/PLA-2808
feat(users): synchronously delete identities and targets in controller
2025-04-28 12:56:31 +04:00
Jake BarnbyandGitHub 8623767926 Merge pull request #9677 from ArnabChatterjee20k/chore--utopia-migration
Chore: updated utopia migration version
2025-04-28 03:51:57 +00:00
Jake BarnbyandGitHub d39fce55c1 Merge branch '1.6.x' into chore--utopia-migration 2025-04-28 03:38:30 +00:00
Steven NguyenandGitHub 70f31bb48c Merge pull request #9691 from appwrite/update-flutter-sdk
chore: update flutter sdk
2025-04-25 12:48:50 -07:00
Chirag Aggarwal b65c375655 chore: update flutter sdk 2025-04-25 19:24:12 +00:00
Fabian Gruber 270250a563 review feedback 2025-04-25 16:25:26 +02:00
Fabian Gruber 9601860247 feat(users): synchronously delete identities and targets in controller 2025-04-25 15:55:10 +02:00
Christy JacobandGitHub 09ca143231 Merge pull request #9687 from appwrite/PLA-2807
feat(dispatch): add contextual dispatch logic
2025-04-25 17:52:43 +04:00
Christy JacobandGitHub 2afbfea0d0 Merge pull request #9638 from appwrite/fix-zero-specifications
fix(functions): treat 0 as unlimited for CPUs and memory
2025-04-25 14:49:04 +04:00
Steven Nguyen 928f535800 chore(functions): update desc for CPU and mem env vars
Clarify that 0 for _APP_FUNCTIONS_CPUS and _APP_FUNCTIONS_MEMORY means
unlimited.
2025-04-24 17:30:03 -07:00
Fabian Gruber 391e045775 feat(dispatch): add contextual dispatch logic 2025-04-24 23:15:00 +02:00
Christy JacobandGitHub 8b23d4bab1 Merge pull request #9666 from appwrite/fix-headers
fix: remove content-type header from get request specs
2025-04-24 19:14:19 +04:00
ArnabChatterjee20k 26edc75455 updated utopia migration version 2025-04-23 15:09:44 +05:30
Jake Barnby d9d15b0fbe Update queue version 2025-04-18 00:21:53 +12:00
Jake Barnby 59c4536208 Format 2025-04-18 00:10:20 +12:00
Jake Barnby cc20484131 Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter
# Conflicts:
#	composer.lock
2025-04-18 00:00:55 +12:00
Jake Barnby ec65071ae3 Revert "Add publisher consumer pool adapter"
This reverts commit fe26dcb50a.
2025-04-17 23:59:43 +12:00
Jake Barnby fe26dcb50a Add publisher consumer pool adapter 2025-04-17 22:27:20 +12:00
Jake Barnby 25a3266301 Remove dump 2025-04-17 22:25:36 +12:00
Jake Barnby 61cf767232 Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter 2025-04-17 17:09:13 +12:00
Jake Barnby 70364d7a07 Format 2025-04-17 17:09:08 +12:00
Jake Barnby 100870557b Use cache pool adapter 2025-04-17 16:50:13 +12:00
Jake Barnby 03cae4c5ee Pool use project create 2025-04-17 16:49:12 +12:00
Jake Barnby 28addd6702 Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter
# Conflicts:
#	app/controllers/api/health.php
2025-04-17 13:34:30 +12:00
Jake Barnby 4b8c68e5ed Update db 2025-04-17 13:34:08 +12:00
Jake Barnby d10757e69f Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter 2025-04-16 13:55:55 +12:00
Jake Barnby 49ea2f6fd9 Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter
# Conflicts:
#	app/controllers/api/projects.php
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Workers/Deletes.php
2025-04-15 22:21:01 +12:00
Jake Barnby b972e7a1d4 Stash 2025-04-15 22:18:48 +12:00
Steven Nguyen b431eb4a51 fix(functions): treat 0 as unlimited for CPUs and memory
Prior versions of Appwrite treated 0 as unlimited for
_APP_FUNCTIONS_CPUS and _APP_FUNCTIONS_MEMORY. As such, this PR updates
the specification validator and the list specifications endpoint to also
treat the default 0 value as unlimited, allowing any specification.
2025-04-11 12:13:06 -07:00
Jake Barnby 608bda0b48 Update lock 2025-04-02 13:47:12 +13:00
Jake Barnby 91d4b1b99f Merge remote-tracking branch 'origin/1.6.x' into feat-pool-adapter
# Conflicts:
#	composer.lock
2025-04-02 13:40:27 +13:00
Jake Barnby a9152c0c20 Doctor pool use 2025-03-28 16:51:47 +13:00
Jake Barnby 906f774a90 Worker scope pool use 2025-03-28 16:50:34 +13:00
Jake Barnby ec2f3b69cc API scope pool use 2025-03-28 16:50:13 +13:00
Jake Barnby 5b843015b0 Realtime scope pool use 2025-03-28 16:43:17 +13:00
Jake Barnby 3a2ff9790c CLI scope pool use 2025-03-28 16:42:26 +13:00
Jake Barnby 08bc6379bd Remove redundant DSN parse 2025-03-28 14:43:34 +13:00
Jake Barnby 5be62c22e0 Move specification into SDK namespace 2025-02-05 00:08:56 +13:00
17 changed files with 262 additions and 229 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '15.0.1',
'version' => '15.0.2',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
+2 -2
View File
@@ -800,7 +800,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_CPUS',
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty or 0, CPU limit will be disabled.',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
@@ -809,7 +809,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty or 0, memory limit will be disabled.',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
+9 -5
View File
@@ -186,8 +186,8 @@ App::post('/v1/functions')
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
$plan,
Config::getParam('runtime-specifications', []),
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
System::getEnv('_APP_FUNCTIONS_CPUS', 0),
System::getEnv('_APP_FUNCTIONS_MEMORY', 0)
), 'Runtime specification for the function and builds.', true, ['plan'])
->inject('request')
->inject('response')
@@ -569,8 +569,12 @@ App::get('/v1/functions/specifications')
$spec['enabled'] = in_array($spec['slug'], $plan['runtimeSpecifications']);
}
$maxCpus = System::getEnv('_APP_FUNCTIONS_CPUS', 0);
$maxMemory = System::getEnv('_APP_FUNCTIONS_MEMORY', 0);
// Only add specs that are within the limits set by environment variables
if ($spec['cpus'] <= System::getEnv('_APP_FUNCTIONS_CPUS', 1) && $spec['memory'] <= System::getEnv('_APP_FUNCTIONS_MEMORY', 512)) {
// Treat 0 as no limit
if ((empty($maxCpus) || $spec['cpus'] <= $maxCpus) && (empty($maxMemory) || $spec['memory'] <= $maxMemory)) {
$runtimeSpecs[] = $spec;
}
}
@@ -872,8 +876,8 @@ App::put('/v1/functions/:functionId')
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
$plan,
Config::getParam('runtime-specifications', []),
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
System::getEnv('_APP_FUNCTIONS_CPUS', 0),
System::getEnv('_APP_FUNCTIONS_MEMORY', 0)
), 'Runtime specification for the function and builds.', true, ['plan'])
->inject('request')
->inject('response')
+4
View File
@@ -9,6 +9,8 @@ use Appwrite\Auth\Validator\PasswordDictionary;
use Appwrite\Auth\Validator\PasswordHistory;
use Appwrite\Auth\Validator\PersonalData;
use Appwrite\Auth\Validator\Phone;
use Appwrite\Deletes\Identities as DeleteIdentities;
use Appwrite\Deletes\Targets as DeleteTargets;
use Appwrite\Detector\Detector;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
@@ -2275,6 +2277,8 @@ App::delete('/v1/users/:userId')
$clone = clone $user;
$dbForProject->deleteDocument('users', $userId);
DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
$queueForDeletes
->setType(DELETE_TYPE_DOCUMENT)
+89 -83
View File
@@ -43,29 +43,6 @@ $http = new Server(
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
$totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
$http
->set([
'worker_num' => $totalWorkers,
'dispatch_func' => 'dispatch',
'open_http2_protocol' => true,
'http_compression' => false,
'package_max_length' => $payloadSize,
'buffer_output_size' => $payloadSize,
'task_worker_num' => 1, // required for the task to fetch domains background
]);
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
Console::success('Starting reload...');
});
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
Console::success('Reload completed...');
});
/**
* Assigns HTTP requests to worker threads by analyzing its payload/content.
*
@@ -83,76 +60,105 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
*/
function dispatch(Server $server, int $fd, int $type, $data = null): int
{
global $totalWorkers, $domains;
$resolveWorkerId = function (Server $server, $data = null) {
global $totalWorkers, $domains;
// If data is not set we can send request to any worker
// first we try to pick idle worker, if not we randomly pick a worker
if ($data === null) {
// If data is not set we can send request to any worker
// first we try to pick idle worker, if not we randomly pick a worker
if ($data === null) {
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
return rand(0, $totalWorkers - 1);
}
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
// Each worker has numeric ID, starting from 0 and incrementing
// From 0 to riskyWorkers, we consider safe workers
// From riskyWorkers to totalWorkers, we consider risky workers
$riskyWorkers = (int)floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
$domain = '';
// max up to 3 as first line has request details and second line has host
$lines = explode("\n", $data, 3);
$request = $lines[0];
if (count($lines) > 1) {
$domain = trim(explode('Host: ', $lines[1])[1]);
}
// Sync executions are considered risky
$risky = false;
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
$risky = true;
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
$risky = true;
} elseif ($domains->get(md5($domain), 'value') === 1) {
// executions request coming from custom domain
$risky = true;
}
if ($risky) {
// If risky request, only consider risky workers
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
// If idle worker found, give to him
return $j;
}
}
// If no idle workers, give to random risky worker
$worker = rand($riskyWorkers, $totalWorkers - 1);
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
}
// If safe request, give to any idle worker
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
return rand(0, $totalWorkers - 1);
}
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
// Each worker has numeric ID, starting from 0 and incrementing
// From 0 to riskyWorkers, we consider safe workers
// From riskyWorkers to totalWorkers, we consider risky workers
$riskyWorkers = (int) floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
$domain = '';
// max up to 3 as first line has request details and second line has host
$lines = explode("\n", $data, 3);
$request = $lines[0];
if (count($lines) > 1) {
$domain = trim(explode('Host: ', $lines[1])[1]);
}
// Sync executions are considered risky
$risky = false;
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
$risky = true;
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
$risky = true;
} elseif ($domains->get(md5($domain), 'value') === 1) {
// executions request coming from custom domain
$risky = true;
}
if ($risky) {
// If risky request, only consider risky workers
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
// If idle worker found, give to him
return $j;
}
}
// If no idle workers, give to random risky worker
$worker = rand($riskyWorkers, $totalWorkers - 1);
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
// If no idle worker found, give to random safe worker
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
$worker = rand(0, $riskyWorkers - 1);
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
}
// If safe request, give to any idle worker
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
// If no idle worker found, give to random safe worker
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
$worker = rand(0, $riskyWorkers - 1);
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
};
$workerId = $resolveWorkerId($server, $data);
$server->bind($fd, $workerId);
return $workerId;
}
$http
->set([
Constant::OPTION_WORKER_NUM => $totalWorkers,
Constant::OPTION_DISPATCH_FUNC => dispatch(...),
Constant::OPTION_DISPATCH_MODE => SWOOLE_DISPATCH_UIDMOD,
Constant::OPTION_HTTP_COMPRESSION => false,
Constant::OPTION_PACKAGE_MAX_LENGTH => $payloadSize,
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
]);
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
Console::success('Starting reload...');
});
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
Console::success('Reload completed...');
});
include __DIR__ . '/controllers/general.php';
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
+2 -2
View File
@@ -48,7 +48,7 @@
"utopia-php/abuse": "0.52.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.55.*",
"utopia-php/cache": "0.12.*",
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.66.*",
@@ -60,7 +60,7 @@
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.16.*",
"utopia-php/migration": "0.8.*",
"utopia-php/migration": "0.9.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
Generated
+48 -47
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": "35fd85e8d566d20d8177266469c5ebcb",
"content-hash": "bd7be6b780f9963ca1c001655d731e70",
"packages": [
{
"name": "adhocore/jwt",
@@ -3300,16 +3300,16 @@
},
{
"name": "utopia-php/cache",
"version": "0.12.0",
"version": "0.13.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
"reference": "646038f1d470b759c129348be8fc14da3c00bbd9"
"reference": "dee01dec33a211644d60f6cfa56b1b8176d3fae3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/646038f1d470b759c129348be8fc14da3c00bbd9",
"reference": "646038f1d470b759c129348be8fc14da3c00bbd9",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/dee01dec33a211644d60f6cfa56b1b8176d3fae3",
"reference": "dee01dec33a211644d60f6cfa56b1b8176d3fae3",
"shasum": ""
},
"require": {
@@ -3317,6 +3317,7 @@
"ext-memcached": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/pools": "0.8.*",
"utopia-php/telemetry": "0.1.*"
},
"require-dev": {
@@ -3345,9 +3346,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
"source": "https://github.com/utopia-php/cache/tree/0.12.0"
"source": "https://github.com/utopia-php/cache/tree/0.13.0"
},
"time": "2025-02-25T09:09:21+00:00"
"time": "2025-04-17T04:20:26+00:00"
},
{
"name": "utopia-php/cli",
@@ -3497,23 +3498,23 @@
},
{
"name": "utopia-php/database",
"version": "0.66.0",
"version": "0.66.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "67d2ab418efba31dc76b3564cf043e2b3f98d027"
"reference": "0d4dc51c767f2307d8555574712dc6adaf893b9a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/67d2ab418efba31dc76b3564cf043e2b3f98d027",
"reference": "67d2ab418efba31dc76b3564cf043e2b3f98d027",
"url": "https://api.github.com/repos/utopia-php/database/zipball/0d4dc51c767f2307d8555574712dc6adaf893b9a",
"reference": "0d4dc51c767f2307d8555574712dc6adaf893b9a",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-pdo": "*",
"php": ">=8.1",
"utopia-php/cache": "0.12.*",
"utopia-php/cache": "0.13.*",
"utopia-php/framework": "0.33.*",
"utopia-php/pools": "0.8.*"
},
@@ -3547,9 +3548,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.66.0"
"source": "https://github.com/utopia-php/database/tree/0.66.4"
},
"time": "2025-04-16T07:10:27+00:00"
"time": "2025-05-01T04:44:17+00:00"
},
{
"name": "utopia-php/domains",
@@ -3660,16 +3661,16 @@
},
{
"name": "utopia-php/fetch",
"version": "0.4.1",
"version": "0.4.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/fetch.git",
"reference": "65095dac14037db0c822fb5e209e5bd3187a0303"
"reference": "83986d1be75a2fae4e684107fe70dd78a8e19b77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/fetch/zipball/65095dac14037db0c822fb5e209e5bd3187a0303",
"reference": "65095dac14037db0c822fb5e209e5bd3187a0303",
"url": "https://api.github.com/repos/utopia-php/fetch/zipball/83986d1be75a2fae4e684107fe70dd78a8e19b77",
"reference": "83986d1be75a2fae4e684107fe70dd78a8e19b77",
"shasum": ""
},
"require": {
@@ -3693,9 +3694,9 @@
"description": "A simple library that provides an interface for making HTTP Requests.",
"support": {
"issues": "https://github.com/utopia-php/fetch/issues",
"source": "https://github.com/utopia-php/fetch/tree/0.4.1"
"source": "https://github.com/utopia-php/fetch/tree/0.4.2"
},
"time": "2025-04-14T07:34:27+00:00"
"time": "2025-04-25T13:48:02+00:00"
},
{
"name": "utopia-php/framework",
@@ -3951,16 +3952,16 @@
},
{
"name": "utopia-php/migration",
"version": "0.8.6",
"version": "0.9.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "84163e16edc0b2e64c34ad7b7c4cc5f05d762daf"
"reference": "8a56e928d6ccb5958a5b80ac27286d969f11a6b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/84163e16edc0b2e64c34ad7b7c4cc5f05d762daf",
"reference": "84163e16edc0b2e64c34ad7b7c4cc5f05d762daf",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/8a56e928d6ccb5958a5b80ac27286d969f11a6b0",
"reference": "8a56e928d6ccb5958a5b80ac27286d969f11a6b0",
"shasum": ""
},
"require": {
@@ -4001,9 +4002,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.8.6"
"source": "https://github.com/utopia-php/migration/tree/0.9.2"
},
"time": "2025-04-14T08:22:09+00:00"
"time": "2025-04-22T16:09:28+00:00"
},
{
"name": "utopia-php/orchestration",
@@ -4543,28 +4544,28 @@
},
{
"name": "utopia-php/vcs",
"version": "0.9.4",
"version": "0.9.5",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a"
"reference": "055956545ca7ab4e8688df5de1df3e2833859793"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/055956545ca7ab4e8688df5de1df3e2833859793",
"reference": "055956545ca7ab4e8688df5de1df3e2833859793",
"shasum": ""
},
"require": {
"adhocore/jwt": "^1.1",
"php": ">=8.0",
"utopia-php/cache": "0.12.*",
"utopia-php/cache": "0.13.*",
"utopia-php/framework": "0.*.*",
"utopia-php/system": "0.9.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpstan/phpstan": "1.8.*",
"laravel/pint": "1.*.*",
"phpstan/phpstan": "1.*.*",
"phpunit/phpunit": "^9.4"
},
"type": "library",
@@ -4587,9 +4588,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.9.4"
"source": "https://github.com/utopia-php/vcs/tree/0.9.5"
},
"time": "2025-03-13T10:09:45+00:00"
"time": "2025-04-17T04:38:49+00:00"
},
{
"name": "utopia-php/websocket",
@@ -4767,16 +4768,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.40.14",
"version": "0.40.15",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "bf3bf29b24d2dce9f49e6849e3af2a1567592d02"
"reference": "65c708b931b29b3e01c5cc7504a734ce2cc3dc95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/bf3bf29b24d2dce9f49e6849e3af2a1567592d02",
"reference": "bf3bf29b24d2dce9f49e6849e3af2a1567592d02",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/65c708b931b29b3e01c5cc7504a734ce2cc3dc95",
"reference": "65c708b931b29b3e01c5cc7504a734ce2cc3dc95",
"shasum": ""
},
"require": {
@@ -4812,9 +4813,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.14"
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.15"
},
"time": "2025-04-24T14:36:08+00:00"
"time": "2025-04-25T08:50:44+00:00"
},
{
"name": "doctrine/annotations",
@@ -5231,16 +5232,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.13.0",
"version": "1.13.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
"shasum": ""
},
"require": {
@@ -5279,7 +5280,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
},
"funding": [
{
@@ -5287,7 +5288,7 @@
"type": "tidelift"
}
],
"time": "2025-02-12T12:17:51+00:00"
"time": "2025-04-29T12:36:36+00:00"
},
{
"name": "nikic/php-parser",
+12
View File
@@ -1,5 +1,17 @@
## 15.0.2
* Avoid setting empty `User-Agent` header and only encode it when present.
* Update doc examples to use new multi-region endpoint: `https://<REGION>.cloud.appwrite.io/v1`.
## 15.0.1
* Removed `Content-Type` header from GET and HEAD requests.
* Add validation for setting endpoint in `setEndpoint` and `setEndPointRealtime` methods.
* Include Figma in list of available OAuth providers.
## 15.0.0
* Encode `User-Agent` header to fix invalid HTTP header field value error.
* Breaking changes:
* Changed the typing of `AppwriteException`'s response parameter from a `dynamic` object to an optional string (`?String`).
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace Appwrite\Deletes;
use Utopia\Database\Database;
use Utopia\Database\Query;
class Identities
{
public static function delete(Database $database, Query $query): void
{
$database->deleteDocuments(
'identities',
[
$query,
Query::orderAsc()
],
Database::DELETE_BATCH_SIZE
);
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace Appwrite\Deletes;
use Appwrite\Extend\Exception;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
class Targets
{
public static function delete(Database $database, Query $query): void
{
$database->deleteDocuments(
'targets',
[
$query,
Query::orderAsc()
],
Database::DELETE_BATCH_SIZE,
fn (Document $target) => self::deleteSubscribers($database, $target)
);
}
public static function deleteSubscribers(Database $database, Document $target): void
{
$database->deleteDocuments(
'subscribers',
[
Query::equal('targetInternalId', [$target->getInternalId()]),
Query::orderAsc(),
],
Database::DELETE_BATCH_SIZE,
function (Document $subscriber) use ($database, $target) {
$topicId = $subscriber->getAttribute('topicId');
$topicInternalId = $subscriber->getAttribute('topicInternalId');
$topic = $database->getDocument('topics', $topicId);
if (!$topic->isEmpty() && $topic->getInternalId() === $topicInternalId) {
$totalAttribute = match ($target->getAttribute('providerType')) {
MESSAGE_TYPE_EMAIL => 'emailTotal',
MESSAGE_TYPE_SMS => 'smsTotal',
MESSAGE_TYPE_PUSH => 'pushTotal',
default => throw new Exception('Invalid target provider type'),
};
$database->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
min: 0
);
}
}
);
}
}
@@ -34,7 +34,7 @@ class RuntimeSpecification extends Validator
$allowedSpecifications = [];
foreach ($this->specifications as $size => $values) {
if ($values['cpus'] <= $this->maxCpus && $values['memory'] <= $this->maxMemory) {
if ((empty($this->maxCpus) || $values['cpus'] <= $this->maxCpus) && (empty($this->maxMemory) || $values['memory'] <= $this->maxMemory)) {
if (!empty($this->plan) && array_key_exists('runtimeSpecifications', $this->plan)) {
if (!\in_array($size, $this->plan['runtimeSpecifications'])) {
continue;
+3 -3
View File
@@ -3,9 +3,9 @@
namespace Appwrite\Platform\Tasks;
use Appwrite\SDK\AuthType;
use Appwrite\Specification\Format\OpenAPI3;
use Appwrite\Specification\Format\Swagger2;
use Appwrite\Specification\Specification;
use Appwrite\SDK\Specification\Format\OpenAPI3;
use Appwrite\SDK\Specification\Format\Swagger2;
use Appwrite\SDK\Specification\Specification;
use Appwrite\Utopia\Request as AppwriteRequest;
use Appwrite\Utopia\Response as AppwriteResponse;
use Exception;
+7 -79
View File
@@ -4,6 +4,8 @@ namespace Appwrite\Platform\Workers;
use Appwrite\Auth\Auth;
use Appwrite\Certificates\Adapter as CertificatesAdapter;
use Appwrite\Deletes\Identities;
use Appwrite\Deletes\Targets;
use Appwrite\Extend\Exception;
use Executor\Executor;
use Throwable;
@@ -149,7 +151,7 @@ class Deletes extends Action
$this->deleteTopic($project, $getProjectDB, $document);
break;
case DELETE_TYPE_TARGET:
$this->deleteTargetSubscribers($project, $getProjectDB, $document);
Targets::deleteSubscribers($getProjectDB($project), $document);
break;
case DELETE_TYPE_EXPIRED_TARGETS:
$this->deleteExpiredTargets($project, $getProjectDB);
@@ -265,47 +267,6 @@ class Deletes extends Action
);
}
/**
* @param Document $project
* @param callable $getProjectDB
* @param Document $target
* @throws Exception
*/
private function deleteTargetSubscribers(Document $project, callable $getProjectDB, Document $target): void
{
/** @var Database */
$dbForProject = $getProjectDB($project);
// Delete subscribers and decrement topic counts
$this->deleteByGroup(
'subscribers',
[
Query::equal('targetInternalId', [$target->getInternalId()]),
Query::orderAsc(),
],
$dbForProject,
function (Document $subscriber) use ($dbForProject, $target) {
$topicId = $subscriber->getAttribute('topicId');
$topicInternalId = $subscriber->getAttribute('topicInternalId');
$topic = $dbForProject->getDocument('topics', $topicId);
if (!$topic->isEmpty() && $topic->getInternalId() === $topicInternalId) {
$totalAttribute = match ($target->getAttribute('providerType')) {
MESSAGE_TYPE_EMAIL => 'emailTotal',
MESSAGE_TYPE_SMS => 'smsTotal',
MESSAGE_TYPE_PUSH => 'pushTotal',
default => throw new Exception('Invalid target CertificatesAdapter type'),
};
$dbForProject->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
min: 0
);
}
}
);
}
/**
* @param Document $project
* @param callable $getProjectDB
@@ -315,32 +276,12 @@ class Deletes extends Action
*/
private function deleteExpiredTargets(Document $project, callable $getProjectDB): void
{
$this->deleteByGroup(
'targets',
[
Query::equal('expired', [true]),
Query::orderAsc(),
],
$getProjectDB($project),
function (Document $target) use ($getProjectDB, $project) {
$this->deleteTargetSubscribers($project, $getProjectDB, $target);
}
);
Targets::delete($getProjectDB($project), Query::equal('expired', [true]));
}
private function deleteSessionTargets(Document $project, callable $getProjectDB, Document $session): void
{
$this->deleteByGroup(
'targets',
[
Query::equal('sessionInternalId', [$session->getInternalId()]),
Query::orderAsc(),
],
$getProjectDB($project),
function (Document $target) use ($getProjectDB, $project) {
$this->deleteTargetSubscribers($project, $getProjectDB, $target);
}
);
Targets::delete($getProjectDB($project), Query::equal('sessionInternalId', [$session->getInternalId()]));
}
/**
@@ -723,23 +664,10 @@ class Deletes extends Action
], $dbForProject);
// Delete identities
$this->deleteByGroup('identities', [
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
], $dbForProject);
Identities::delete($dbForProject, Query::equal('userInternalId', [$userInternalId]));
// Delete targets
$this->deleteByGroup(
'targets',
[
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
],
$dbForProject,
function (Document $target) use ($getProjectDB, $project) {
$this->deleteTargetSubscribers($project, $getProjectDB, $target);
}
);
Targets::delete($dbForProject, Query::equal('userInternalId', [$userInternalId]));
}
/**
@@ -1,6 +1,6 @@
<?php
namespace Appwrite\Specification;
namespace Appwrite\SDK\Specification;
use Appwrite\Utopia\Response\Model;
use Utopia\App;
@@ -1,10 +1,10 @@
<?php
namespace Appwrite\Specification\Format;
namespace Appwrite\SDK\Specification\Format;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\MethodType;
use Appwrite\Specification\Format;
use Appwrite\SDK\Specification\Format;
use Appwrite\Template\Template;
use Appwrite\Utopia\Response\Model;
use Utopia\Database\Helpers\Permission;
@@ -1,10 +1,10 @@
<?php
namespace Appwrite\Specification\Format;
namespace Appwrite\SDK\Specification\Format;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\MethodType;
use Appwrite\Specification\Format;
use Appwrite\SDK\Specification\Format;
use Appwrite\Template\Template;
use Appwrite\Utopia\Response\Model;
use Utopia\Database\Helpers\Permission;
@@ -1,6 +1,6 @@
<?php
namespace Appwrite\Specification;
namespace Appwrite\SDK\Specification;
class Specification
{