Merge remote-tracking branch 'origin/1.7.x' into chore-upgrade-utopia-queue

This commit is contained in:
Jake Barnby 2025-06-30 21:01:47 -04:00
commit 72cd501f21
No known key found for this signature in database
GPG key ID: C437A8CC85B96E9C
7 changed files with 135 additions and 53 deletions

View file

@ -64,8 +64,9 @@ jobs:
sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg
sudo apt update
sudo apt install oha
oha --version
- name: Benchmark PR
run: 'oha -z 180s http://localhost/v1/health/version --output-format json > benchmark.json'
run: 'oha -z 180s http://localhost/v1/health/version --output-format json > benchmark.json'
- name: Cleaning
run: docker compose down -v
- name: Installing latest version
@ -78,7 +79,7 @@ jobs:
docker compose up -d
sleep 10
- name: Benchmark Latest
run: oha -z 180s http://localhost/v1/health/version --output-format json > benchmark-latest.json
run: oha -z 180s http://localhost/v1/health/version --output-format json > benchmark-latest.json
- name: Prepare comment
run: |
echo '## :sparkles: Benchmark results' > benchmark.txt

View file

@ -217,7 +217,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '8.0.2',
'version' => '8.1.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,

View file

@ -799,7 +799,6 @@ App::init()
->inject('getProjectDB')
->inject('locale')
->inject('localeCodes')
->inject('clients')
->inject('geodb')
->inject('queueForStatsUsage')
->inject('queueForEvents')
@ -810,7 +809,9 @@ App::init()
->inject('previewHostname')
->inject('devKey')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey) {
->inject('httpReferrer')
->inject('httpReferrerSafe')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, string $httpReferrer, string $httpReferrerSafe) {
/*
* Appwrite Router
*/
@ -942,42 +943,9 @@ App::init()
$locale->setDefault($localeParam);
}
$referrer = $request->getReferer();
$origin = \parse_url($request->getOrigin($referrer), PHP_URL_HOST);
$protocol = \parse_url($request->getOrigin($referrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($referrer), PHP_URL_PORT);
$refDomainOrigin = 'localhost';
$validator = new Hostname($clients);
if ($validator->isValid($origin)) {
$refDomainOrigin = $origin;
} elseif (!empty($origin)) {
// Auto-allow domains with linked rule
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
} else {
$rule = Authorization::skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$origin]),
Query::limit(1)
])
)[0] ?? new Document();
}
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
$refDomainOrigin = $origin;
}
}
$refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : '');
$refDomain = (!$route->getLabel('origin', false)) // This route is publicly accessible
? $refDomain
: (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
$selfDomain = new Domain($request->getHostname());
$endDomain = new Domain((string)$origin);
Config::setParam(
'domainVerification',
($selfDomain->getRegisterable() === $endDomain->getRegisterable()) &&
@ -1049,7 +1017,7 @@ App::init()
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Dev-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Origin', $httpReferrerSafe)
->addHeader('Access-Control-Allow-Credentials', 'true');
if (!$devKey->isEmpty()) {
@ -1070,6 +1038,7 @@ App::init()
&& \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
&& $route->getLabel('origin', false) !== '*'
&& empty($request->getHeader('x-appwrite-key', ''))
&& \parse_url($httpReferrerSafe, PHP_URL_HOST) === 'localhost'
) {
throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription());
}

View file

@ -564,7 +564,7 @@ App::init()
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$timestamp = 60 * 60 * 24 * 30;
$timestamp = 60 * 60 * 24 * 180; // Temporarily increase the TTL to 180 day to ensure files in the cache are still fetched.
$data = $cache->load($key, $timestamp);
if (!empty($data) && !$cacheLog->isEmpty()) {
@ -831,6 +831,10 @@ App::shutdown()
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
}
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$key = $request->cacheIdentifier();
$signature = md5($data['payload']);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
@ -848,12 +852,11 @@ App::shutdown()
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
// Overwrite the file every APP_CACHE_UPDATE seconds to update the file modified time that is used in the TTL checks in cache->load()
$cache->save($key, $data['payload']);
}
if ($signature !== $cacheLog->getAttribute('signature')) {
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$cache->save($key, $data['payload']);
}
}

View file

@ -944,3 +944,52 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
}
return new Document([]);
}, ['project', 'dbForProject', 'request']);
App::setResource('httpReferrer', function (Request $request): string {
$referrer = $request->getReferer();
return $referrer;
}, ['request']);
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $clients, Database $dbForPlatform, Document $project, App $utopia): string {
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
// Safe if route is publicly accessible
$route = $utopia->getRoute();
if ($route->getLabel('origin', false)) {
return $referrer;
}
// Safe if added as web platform
$validator = new Hostname($clients);
if ($validator->isValid($origin)) {
return $referrer;
}
// Safe if rule with same project ID exists
if (!empty($origin)) {
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
} else {
$rule = Authorization::skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$origin]),
Query::limit(1)
])
)[0] ?? new Document();
}
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
return $referrer;
}
}
// Unsafe; Localhost is always safe for ease of local development
$origin = 'localhost';
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
return $referrer;
}, ['request', 'httpReferrer', 'clients', 'dbForPlatform', 'project', 'utopia']);

16
composer.lock generated
View file

@ -4822,16 +4822,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.41.8",
"version": "0.41.9",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2"
"reference": "61037c1ed9262308cab49c1d760f3278036ab694"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/93ffb24b25b376ca4423e3a5caf6f916673af4b2",
"reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/61037c1ed9262308cab49c1d760f3278036ab694",
"reference": "61037c1ed9262308cab49c1d760f3278036ab694",
"shasum": ""
},
"require": {
@ -4867,9 +4867,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.41.8"
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.9"
},
"time": "2025-06-18T13:20:45+00:00"
"time": "2025-06-27T10:16:17+00:00"
},
{
"name": "doctrine/annotations",
@ -8248,7 +8248,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@ -8272,5 +8272,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View file

@ -1 +1,61 @@
# Change Log
# Change Log
## 8.1.0
* Add multi-region support to `init` command
* Update `init` command to clear previous configuration in `appwrite.json`
* Update localConfig to store multi-region endpoint
* Fix throw error when creating unknown attribute instead of timing out
* Fix equal comparison of large numbers and BigNumber instances using proper equality checks
* Fix duplication of reasons when comparing localConfig with remoteConfig
* Fix `firstOrNull()` to `firstOrNull` in types generation for dart
* Refactor to use `isCloud()` method consistently
## 8.0.2
* Add Type generation fixes:
* Properly handle enum attributes in dart, java and kotlin
* Fix initialisation of null attributes in dart's fromMap method
* Fix relationships and enums in swift
## 8.0.1
* Add `resourceId` and `resourceType` attributes to `createRedirectRule`
* Add `providerReference` to vcs command for getting repository contents
* Add warning comment to `bulk updateDocuments` method
* Fix type generation for enums in Typescript and PHP language
## 8.0.0
* Add `types` command to generate language specific typings for collections. Currently supports - `php`, `swift`, `dart`, `js`, `ts`, `kotlin` and `java`
* Update bulk operation docs to include experiment feature warnings
* Remove assistant service and commands
## 7.0.0
* Add `sites` command
* Add `tokens` command
* Add `devKeys` support to `projects` command
* Add `init site`, `pull site` and `push site` commands
* Add bulk operation methods like `createDocuments`, `deleteDocuments` etc.
* Add new upsert methods: `upsertDocument` and `upsertDocuments`
* Update GET requests to not include content-type header
## 6.2.3
* Fix hot swapping error in `python-ml` function
## 6.2.2
* Fix GitHub builds by adding `qemu-system` package
* Fix attribute creation timed out
## 6.2.1
* Add `listOrganizations` method to `organizations` service and fix init project command
## 6.2.0
* Add specifications support to CLI
* Update package version
* Fix: Missed specifications param when updating a function