mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 00:49:02 +00:00
Merge branch '1.8.x' into refactor-specs-task-methods
This commit is contained in:
commit
c744354973
14 changed files with 135 additions and 28 deletions
1
.env
1
.env
|
|
@ -125,3 +125,4 @@ _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10
|
|||
_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
|
||||
|
|
@ -357,6 +357,15 @@ return [
|
|||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_TRUSTED_HEADERS',
|
||||
'description' => 'This option allows you to set the list of trusted headers, the value is a comma‑separated list of HTTP header names, evaluated left-to-right for the first valid IP. Header names are treated case-insensitively.',
|
||||
'introduction' => '1.8.0',
|
||||
'default' => 'x-forwarded-for',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
]
|
||||
],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -628,7 +628,6 @@ App::init()
|
|||
$queueForFunctions->setPlatform($platform);
|
||||
$queueForBuilds->setPlatform($platform);
|
||||
$queueForMails->setPlatform($platform);
|
||||
$queueForMigrations->setPlatform($platform);
|
||||
|
||||
// Clone the queues, to prevent events triggered by the database listener
|
||||
// from overwriting the events that are supposed to be triggered in the shutdown hook.
|
||||
|
|
|
|||
30
composer.lock
generated
30
composer.lock
generated
|
|
@ -4264,16 +4264,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/framework",
|
||||
"version": "0.33.34",
|
||||
"version": "0.33.35",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/http.git",
|
||||
"reference": "76def92594c32504ec80eaacdb60ff8fad73c856"
|
||||
"reference": "82b139fb04f30045db51b0d322224f222da32313"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/76def92594c32504ec80eaacdb60ff8fad73c856",
|
||||
"reference": "76def92594c32504ec80eaacdb60ff8fad73c856",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/82b139fb04f30045db51b0d322224f222da32313",
|
||||
"reference": "82b139fb04f30045db51b0d322224f222da32313",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4306,9 +4306,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/http/issues",
|
||||
"source": "https://github.com/utopia-php/http/tree/0.33.34"
|
||||
"source": "https://github.com/utopia-php/http/tree/0.33.35"
|
||||
},
|
||||
"time": "2025-12-08T07:55:31+00:00"
|
||||
"time": "2025-12-12T08:33:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/image",
|
||||
|
|
@ -5011,22 +5011,22 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/swoole.git",
|
||||
"reference": "150c30700e738c52348cce9ed0e0f0ff96872081"
|
||||
"reference": "e42b6b8e44c457a7b35d8a857d7af1d67d667c58"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/150c30700e738c52348cce9ed0e0f0ff96872081",
|
||||
"reference": "150c30700e738c52348cce9ed0e0f0ff96872081",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/e42b6b8e44c457a7b35d8a857d7af1d67d667c58",
|
||||
"reference": "e42b6b8e44c457a7b35d8a857d7af1d67d667c58",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-swoole": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/framework": "0.33.*"
|
||||
"utopia-php/framework": "0.33.35"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
|
|
@ -5056,9 +5056,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/swoole/issues",
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.8.4"
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.8.5"
|
||||
},
|
||||
"time": "2025-09-07T09:39:46+00:00"
|
||||
"time": "2025-12-15T14:03:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/system",
|
||||
|
|
@ -8943,7 +8943,7 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
|
@ -8967,5 +8967,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.3"
|
||||
},
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ services:
|
|||
- _APP_DATABASE_SHARED_NAMESPACE
|
||||
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
|
||||
- _APP_CUSTOM_DOMAIN_DENY_LIST
|
||||
- _APP_TRUSTED_HEADERS
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
|
|
@ -736,6 +737,7 @@ services:
|
|||
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
|
||||
appwrite-task-maintenance:
|
||||
entrypoint: maintenance
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Queue\Publisher;
|
||||
|
||||
|
|
@ -110,13 +111,18 @@ class Build extends Event
|
|||
*/
|
||||
protected function preparePayload(): array
|
||||
{
|
||||
$platform = $this->platform;
|
||||
if (empty($platform)) {
|
||||
$platform = Config::getParam('platform', []);
|
||||
}
|
||||
|
||||
return [
|
||||
'project' => $this->project,
|
||||
'resource' => $this->resource,
|
||||
'deployment' => $this->deployment,
|
||||
'type' => $this->type,
|
||||
'template' => $this->template,
|
||||
'platform' => $this->platform
|
||||
'platform' => $platform,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Queue\Publisher;
|
||||
|
||||
|
|
@ -202,6 +203,11 @@ class Func extends Event
|
|||
{
|
||||
$events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null;
|
||||
|
||||
$platform = $this->platform;
|
||||
if (empty($platform)) {
|
||||
$platform = Config::getParam('platform', []);
|
||||
}
|
||||
|
||||
return [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
|
|
@ -217,7 +223,7 @@ class Func extends Event
|
|||
'path' => $this->path,
|
||||
'headers' => $this->headers,
|
||||
'method' => $this->method,
|
||||
'platform' => $this->platform
|
||||
'platform' => $platform,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Queue\Publisher;
|
||||
|
||||
class Mail extends Event
|
||||
|
|
@ -516,6 +517,11 @@ class Mail extends Event
|
|||
*/
|
||||
protected function preparePayload(): array
|
||||
{
|
||||
$platform = $this->platform;
|
||||
if (empty($platform)) {
|
||||
$platform = Config::getParam('platform', []);
|
||||
}
|
||||
|
||||
return [
|
||||
'project' => $this->project,
|
||||
'recipient' => $this->recipient,
|
||||
|
|
@ -528,7 +534,8 @@ class Mail extends Event
|
|||
'variables' => $this->variables,
|
||||
'attachment' => $this->attachment,
|
||||
'customMailOptions' => $this->customMailOptions,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams()),
|
||||
'platform' => $platform,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Queue\Publisher;
|
||||
|
||||
|
|
@ -73,11 +74,16 @@ class Migration extends Event
|
|||
*/
|
||||
protected function preparePayload(): array
|
||||
{
|
||||
$platform = $this->platform;
|
||||
if (empty($platform)) {
|
||||
$platform = Config::getParam('platform', []);
|
||||
}
|
||||
|
||||
return [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'migration' => $this->migration,
|
||||
'platform' => $this->platform,
|
||||
'platform' => $platform,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Appwrite\Network;
|
||||
|
||||
use Utopia\Validator\Hostname;
|
||||
|
||||
/**
|
||||
* Generate CORS response headers for an incoming request.
|
||||
*
|
||||
|
|
@ -76,7 +78,8 @@ final class Cors
|
|||
}
|
||||
|
||||
// Match only by host
|
||||
if (!\in_array($host, $this->allowedHosts, true)) {
|
||||
$validator = new Hostname($this->allowedHosts);
|
||||
if (!$validator->isValid($host)) {
|
||||
return $headers;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,6 @@ class Migrations extends Action
|
|||
|
||||
protected array $plan;
|
||||
|
||||
protected array $platform;
|
||||
|
||||
/**
|
||||
* @var array<string, int>
|
||||
*/
|
||||
|
|
@ -108,7 +106,6 @@ class Migrations extends Action
|
|||
$this->deviceForMigrations = $deviceForMigrations;
|
||||
$this->deviceForFiles = $deviceForFiles;
|
||||
$this->plan = $plan;
|
||||
$this->platform = $payload['platform'] ?? [];
|
||||
|
||||
if (empty($payload)) {
|
||||
throw new Exception('Missing payload');
|
||||
|
|
@ -144,10 +141,15 @@ class Migrations extends Action
|
|||
$credentials = $migration->getAttribute('credentials');
|
||||
$migrationOptions = $migration->getAttribute('options');
|
||||
$dataSource = Appwrite::SOURCE_API;
|
||||
$endpoint = $this->platform['endpoint'] ?: ($credentials['endpoint'] ?? 'http://appwrite.test/v1');
|
||||
$database = null;
|
||||
$queries = [];
|
||||
|
||||
if ($credentials['endpoint'] === 'http://localhost/v1') {
|
||||
$platform = Config::getParam('platform', []);
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$credentials['endpoint'] = $protocol . '://' . $platform['apiHostname'] . '/v1';
|
||||
}
|
||||
|
||||
if ($source === Appwrite::getName() && $destination === DestinationCSV::getName()) {
|
||||
$dataSource = Appwrite::SOURCE_DATABASE;
|
||||
$database = $this->dbForProject;
|
||||
|
|
@ -178,7 +180,7 @@ class Migrations extends Action
|
|||
),
|
||||
SourceAppwrite::getName() => new SourceAppwrite(
|
||||
$credentials['projectId'],
|
||||
$endpoint,
|
||||
$credentials['endpoint'],
|
||||
$credentials['apiKey'],
|
||||
$dataSource,
|
||||
$database,
|
||||
|
|
@ -206,10 +208,13 @@ class Migrations extends Action
|
|||
$destination = $migration->getAttribute('destination');
|
||||
$options = $migration->getAttribute('options', []);
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$platform = Config::getParam('platform', []);
|
||||
|
||||
return match ($destination) {
|
||||
DestinationAppwrite::getName() => new DestinationAppwrite(
|
||||
$this->project->getId(),
|
||||
$this->platform['endpoint'],
|
||||
$protocol . '://' . $platform['apiHostname'] . '/v1',
|
||||
$apiKey,
|
||||
$this->dbForProject,
|
||||
Config::getParam('collections', [])['databases']['collections'],
|
||||
|
|
@ -306,6 +311,8 @@ class Migrations extends Action
|
|||
|
||||
$transfer = $source = $destination = null;
|
||||
|
||||
|
||||
|
||||
try {
|
||||
if (
|
||||
$migration->getAttribute('source') === SourceAppwrite::getName() &&
|
||||
|
|
@ -313,8 +320,13 @@ class Migrations extends Action
|
|||
) {
|
||||
$credentials = $migration->getAttribute('credentials', []);
|
||||
$credentials['projectId'] = $credentials['projectId'] ?? $project->getId();
|
||||
$credentials['endpoint'] = $credentials['endpoint'] ?? $this->platform['endpoint'];
|
||||
$credentials['apiKey'] = $credentials['apiKey'] ?? $tempAPIKey;
|
||||
|
||||
if (empty($credentials['endpoint'])) {
|
||||
$platform = Config::getParam('platform', []);
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$credentials['endpoint'] = $protocol . '://' . $platform['apiHostname'] . '/v1';
|
||||
}
|
||||
$migration->setAttribute('credentials', $credentials);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Swoole\Http\Request as SwooleRequest;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Route;
|
||||
use Utopia\Swoole\Request as UtopiaRequest;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Request extends UtopiaRequest
|
||||
{
|
||||
|
|
@ -20,6 +21,9 @@ class Request extends UtopiaRequest
|
|||
|
||||
public function __construct(SwooleRequest $request)
|
||||
{
|
||||
$trustedHeaders = System::getEnv('_APP_TRUSTED_HEADERS', 'x-forwarded-for');
|
||||
$this->setTrustedIpHeaders(explode(',', $trustedHeaders));
|
||||
|
||||
parent::__construct($request);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -326,4 +326,41 @@ trait AccountBase
|
|||
|
||||
$this->assertEquals($response['headers']['status-code'], 204);
|
||||
}
|
||||
|
||||
public function testFallbackForTrustedIp(): void
|
||||
{
|
||||
$email = uniqid() . 'user@localhost.test';
|
||||
$password = 'password';
|
||||
$name = 'User Name';
|
||||
|
||||
// call appwrite directly to avoid proxy stripping the headers
|
||||
$this->client->setEndpoint('http://localhost/v1');
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-forwarded-for' => '191.0.113.195',
|
||||
]), [
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-forwarded-for' => '191.0.113.195',
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
$this->assertEquals('191.0.113.195', $response['body']['clientIp'] ?? $response['body']['ip'] ?? '');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,21 @@ final class CorsTest extends TestCase
|
|||
$this->assertSame('https://foo.com', $result[Cors::HEADER_ALLOW_ORIGIN]);
|
||||
}
|
||||
|
||||
public function testSubdomainWildcardAllowsAnySubdomain(): void
|
||||
{
|
||||
$cors = new Cors(
|
||||
allowedHosts: ['*.example.com'],
|
||||
allowedMethods: ['GET'],
|
||||
allowedHeaders: ['X-Test'],
|
||||
exposedHeaders: [],
|
||||
allowCredentials: false
|
||||
);
|
||||
|
||||
$result = $cors->headers('https://foo.example.com');
|
||||
|
||||
$this->assertSame('https://foo.example.com', $result[Cors::HEADER_ALLOW_ORIGIN]);
|
||||
}
|
||||
|
||||
public function testEmptyOriginReturnsStaticHeadersOnly(): void
|
||||
{
|
||||
$cors = new Cors(
|
||||
|
|
|
|||
Loading…
Reference in a new issue