mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.8.x' into fix-smtp-auth-check
This commit is contained in:
@@ -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
|
||||
@@ -42,6 +42,7 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
target: production
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.version }}
|
||||
VITE_APPWRITE_GROWTH_ENDPOINT=https://growth.appwrite.io/v1
|
||||
|
||||
@@ -20,10 +20,10 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
@@ -46,6 +46,7 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
target: production
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.version }}
|
||||
push: true
|
||||
|
||||
+23
-10
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
|
||||
--no-plugins --no-scripts --prefer-dist \
|
||||
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
|
||||
|
||||
FROM appwrite/base:0.10.6 AS final
|
||||
FROM appwrite/base:0.10.6 AS base
|
||||
|
||||
LABEL maintainer="team@appwrite.io"
|
||||
|
||||
@@ -36,9 +36,7 @@ COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor
|
||||
COPY ./app /usr/src/code/app
|
||||
COPY ./public /usr/src/code/public
|
||||
COPY ./bin /usr/local/bin
|
||||
COPY ./docs /usr/src/code/docs
|
||||
COPY ./src /usr/src/code/src
|
||||
COPY ./dev /usr/src/code/dev
|
||||
|
||||
# Set Volumes
|
||||
RUN mkdir -p /storage/uploads && \
|
||||
@@ -90,15 +88,30 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/stats-resources && \
|
||||
chmod +x /usr/local/bin/worker-stats-resources
|
||||
|
||||
# Letsencrypt Permissions
|
||||
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
|
||||
|
||||
# Enable Extensions
|
||||
RUN if [ "$DEBUG" = "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
|
||||
RUN if [ "$DEBUG" = "true" ]; then mkdir -p /tmp/xdebug; fi
|
||||
RUN if [ "$DEBUG" = "true" ]; then apk add --update --no-cache openssh-client github-cli; fi
|
||||
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
|
||||
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so; fi
|
||||
FROM base AS production
|
||||
|
||||
RUN rm -rf /usr/src/code/app/config/specs && \
|
||||
rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20240924/xdebug.so && \
|
||||
find /usr -name '*.a' -delete 2>/dev/null || true && \
|
||||
find /usr -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true && \
|
||||
find /usr -name '*.pyc' -delete 2>/dev/null || true
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD [ "php", "app/http.php" ]
|
||||
|
||||
FROM base AS development
|
||||
|
||||
COPY ./docs /usr/src/code/docs
|
||||
COPY ./dev /usr/src/code/dev
|
||||
|
||||
RUN if [ "$DEBUG" = "true" ]; then \
|
||||
cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini && \
|
||||
mkdir -p /tmp/xdebug && \
|
||||
apk add --update --no-cache openssh-client github-cli; \
|
||||
fi
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -198,8 +198,14 @@ App::setResource('allowedHostnames', function (array $platform, Document $projec
|
||||
$allowed[] = $request->getHostname();
|
||||
}
|
||||
|
||||
/* Allow the request origin if a dev key or rule is found */
|
||||
$originHostname = parse_url($request->getOrigin(), PHP_URL_HOST);
|
||||
|
||||
/* Add request hostname for preflight requests */
|
||||
if ($request->getMethod() === 'OPTIONS') {
|
||||
$allowed[] = $originHostname;
|
||||
}
|
||||
|
||||
/* Allow the request origin if a dev key or rule is found */
|
||||
if ((!$rule->isEmpty() || !$devKey->isEmpty()) && !empty($originHostname)) {
|
||||
$allowed[] = $originHostname;
|
||||
}
|
||||
|
||||
Generated
+15
-15
@@ -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"
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ services:
|
||||
image: appwrite-dev
|
||||
build:
|
||||
context: .
|
||||
target: development
|
||||
args:
|
||||
DEBUG: false
|
||||
TESTING: true
|
||||
@@ -219,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"
|
||||
|
||||
@@ -735,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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+100
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Queue\Publisher;
|
||||
|
||||
class Mail extends Event
|
||||
@@ -16,6 +17,8 @@ class Mail extends Event
|
||||
protected string $bodyTemplate = '';
|
||||
protected array $attachment = [];
|
||||
|
||||
protected array $customMailOptions = [];
|
||||
|
||||
public function __construct(protected Publisher $publisher)
|
||||
{
|
||||
parent::__construct($publisher);
|
||||
@@ -400,6 +403,94 @@ class Mail extends Event
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sender email
|
||||
*
|
||||
* @param string $email
|
||||
* @return self
|
||||
*/
|
||||
public function setSenderEmail(string $email): self
|
||||
{
|
||||
$this->customMailOptions['senderEmail'] = $email;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sender email
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSenderEmail(): string
|
||||
{
|
||||
return $this->customMailOptions['senderEmail'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sender name
|
||||
*
|
||||
* @param string $name
|
||||
* @return self
|
||||
*/
|
||||
public function setSenderName(string $name): self
|
||||
{
|
||||
$this->customMailOptions['senderName'] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sender name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSenderName(): string
|
||||
{
|
||||
return $this->customMailOptions['senderName'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set reply-to email
|
||||
*
|
||||
* @param string $email
|
||||
* @return self
|
||||
*/
|
||||
public function setReplyToEmail(string $email): self
|
||||
{
|
||||
$this->customMailOptions['replyToEmail'] = $email;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reply-to email
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReplyToEmail(): string
|
||||
{
|
||||
return $this->customMailOptions['replyToEmail'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set reply-to name
|
||||
*
|
||||
* @param string $name
|
||||
* @return self
|
||||
*/
|
||||
public function setReplyToName(string $name): self
|
||||
{
|
||||
$this->customMailOptions['replyToName'] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reply-to name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReplyToName(): string
|
||||
{
|
||||
return $this->customMailOptions['replyToName'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset
|
||||
*
|
||||
@@ -415,6 +506,7 @@ class Mail extends Event
|
||||
$this->variables = [];
|
||||
$this->bodyTemplate = '';
|
||||
$this->attachment = [];
|
||||
$this->customMailOptions = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -425,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,
|
||||
@@ -436,7 +533,9 @@ class Mail extends Event
|
||||
'smtp' => $this->smtp,
|
||||
'variables' => $this->variables,
|
||||
'attachment' => $this->attachment,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
'customMailOptions' => $this->customMailOptions,
|
||||
'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;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,9 +152,21 @@ class Mails extends Action
|
||||
$replyTo = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
$replyToName = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
|
||||
|
||||
if (!empty($smtp)) {
|
||||
$replyTo = !empty($smtp['replyTo']) ? $smtp['replyTo'] : $smtp['senderEmail'];
|
||||
$replyToName = $smtp['senderName'];
|
||||
$customMailOptions = $payload['customMailOptions'] ?? [];
|
||||
|
||||
// fallback hierarchy: Custom options > SMTP config > Defaults.
|
||||
if (!empty($customMailOptions['senderEmail']) || !empty($customMailOptions['senderName'])) {
|
||||
$fromEmail = $customMailOptions['senderEmail'] ?? $mail->From;
|
||||
$fromName = $customMailOptions['senderName'] ?? $mail->FromName;
|
||||
$mail->setFrom($fromEmail, $fromName);
|
||||
}
|
||||
|
||||
if (!empty($customMailOptions['replyToEmail']) || !empty($customMailOptions['replyToName'])) {
|
||||
$replyTo = $customMailOptions['replyToEmail'] ?? $replyTo;
|
||||
$replyToName = $customMailOptions['replyToName'] ?? $replyToName;
|
||||
} elseif (!empty($smtp)) {
|
||||
$replyTo = !empty($smtp['replyTo']) ? $smtp['replyTo'] : ($smtp['senderEmail'] ?? $replyTo);
|
||||
$replyToName = $smtp['senderName'] ?? $replyToName;
|
||||
}
|
||||
|
||||
$mail->addReplyTo($replyTo, $replyToName);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,22 @@ class HTTPTest extends Scope
|
||||
'origin' => 'http://google.com',
|
||||
]);
|
||||
$this->assertNull($response['headers']['access-control-allow-origin'] ?? null);
|
||||
}
|
||||
|
||||
public function testPreflight()
|
||||
{
|
||||
|
||||
$endpoint = '/v1/projects'; // Can be any non-404 route
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_OPTIONS, $endpoint, [
|
||||
'origin' => 'http://random.com',
|
||||
'access-control-request-headers' => 'X-Appwrite-Project',
|
||||
'access-control-request-method' => 'GET'
|
||||
]);
|
||||
$this->assertEquals('http://random.com', $response['headers']['access-control-allow-origin']);
|
||||
}
|
||||
|
||||
public function testConsoleRedirect()
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user