mirror of
https://github.com/appwrite/appwrite
synced 2026-05-22 00:18:25 +00:00
Fix merge conflicts
This commit is contained in:
commit
ea238bc5ec
40 changed files with 629 additions and 306 deletions
1
.env
1
.env
|
|
@ -22,6 +22,7 @@ _APP_OPTIONS_FORCE_HTTPS=disabled
|
|||
_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled
|
||||
_APP_OPENSSL_KEY_V1=your-secret-key
|
||||
_APP_DOMAIN=traefik
|
||||
_APP_CONSOLE_DOMAIN=localhost
|
||||
_APP_DOMAIN_FUNCTIONS=functions.localhost
|
||||
_APP_DOMAIN_SITES=sites.localhost
|
||||
_APP_DOMAIN_TARGET_CNAME=test.localhost
|
||||
|
|
|
|||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
|
@ -283,8 +283,6 @@ jobs:
|
|||
name: E2E Service Test (Dev Keys)
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
|
|
|||
10
app/cli.php
10
app/cli.php
|
|
@ -188,16 +188,18 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
|||
return $database;
|
||||
};
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
CLI::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
CLI::setResource('publisherRedis', function () {
|
||||
// Stub
|
||||
});
|
||||
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
return new StatsResources($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
CLI::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Network\Platform;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\System\System;
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ $console = [
|
|||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Localhost',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'hostname' => 'localhost',
|
||||
], // Current host is added on app init
|
||||
],
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ return [
|
|||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '8.1.0',
|
||||
'version' => '8.1.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
|
|
@ -250,7 +250,7 @@ return [
|
|||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '17.0.0',
|
||||
'version' => '17.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
|
|
|
|||
|
|
@ -6,13 +6,8 @@ use Utopia\System\System;
|
|||
* List of Appwrite Sites templates
|
||||
*/
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
// TODO: Development override
|
||||
if (System::getEnv('_APP_ENV') === 'development') {
|
||||
$hostname = 'localhost';
|
||||
}
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
$url = $protocol . '://' . $hostname;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use Appwrite\Event\StatsUsage;
|
|||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
|
|
@ -60,7 +61,6 @@ use Utopia\System\System;
|
|||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
|
@ -1182,8 +1182,8 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
|||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.')
|
||||
->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('success', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('failure', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
|
@ -1282,7 +1282,6 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
|||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) {
|
||||
|
||||
$domain = $request->getHostname();
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
|
|
@ -1779,8 +1778,8 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
|||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.')
|
||||
->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('success', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('failure', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
|
@ -1860,7 +1859,7 @@ App::post('/v1/account/tokens/magic-url')
|
|||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
|
@ -3146,7 +3145,7 @@ App::post('/v1/account/recovery')
|
|||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey'])
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
|
@ -3413,7 +3412,7 @@ App::post('/v1/account/verification')
|
|||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{userId}')
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey']) // TODO add built-in confirm page
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey']) // TODO add built-in confirm page
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
|
|
|||
|
|
@ -523,11 +523,16 @@ App::get('/v1/health/queue/databases')
|
|||
->param('name', 'database_db_main', new Text(256), 'Queue name for which to check the queue size', true)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('publisher')
|
||||
->inject('publisherRedis')
|
||||
->inject('response')
|
||||
->action(function (string $name, int|string $threshold, Publisher $publisher, Response $response) {
|
||||
->action(function (string $name, int|string $threshold, Publisher $publisher, ?Publisher $publisherRedis, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$size = $publisher->getQueueSize(new Queue($name));
|
||||
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'databases');
|
||||
|
||||
$size = $isRedisFallback
|
||||
? $publisherRedis->getQueueSize(new Queue($name))
|
||||
: $publisher->getQueueSize(new Queue($name));
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
|
|
@ -655,11 +660,16 @@ App::get('/v1/health/queue/migrations')
|
|||
))
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('publisher')
|
||||
->inject('publisherRedis')
|
||||
->inject('response')
|
||||
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
|
||||
->action(function (int|string $threshold, Publisher $publisher, ?Publisher $publisherRedis, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$size = $publisher->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
|
||||
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'migrations');
|
||||
|
||||
$size = $isRedisFallback
|
||||
? $publisherRedis->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME))
|
||||
: $publisher->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ App::post('/v1/migrations/csv')
|
|||
$hasCompression = $compression !== Compression::NONE;
|
||||
|
||||
$migrationId = ID::unique();
|
||||
$newPath = $deviceForImports->getPath('/' . $migrationId . '_' . $fileId . '.csv');
|
||||
$newPath = $deviceForImports->getPath($migrationId . '_' . $fileId . '.csv');
|
||||
|
||||
if ($hasEncryption || $hasCompression) {
|
||||
$source = $deviceForFiles->read($path);
|
||||
|
|
@ -410,8 +410,8 @@ App::post('/v1/migrations/csv')
|
|||
'resources' => $resources,
|
||||
'resourceId' => $resourceId,
|
||||
'resourceType' => Resource::TYPE_DATABASE,
|
||||
'statusCounters' => [],
|
||||
'resourceData' => [],
|
||||
'statusCounters' => '{}',
|
||||
'resourceData' => '{}',
|
||||
'errors' => [],
|
||||
'options' => [
|
||||
'path' => $newPath,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use Appwrite\Event\Mail;
|
|||
use Appwrite\Event\Validator\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Platform;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
|
|
@ -1790,7 +1790,7 @@ App::post('/v1/projects/:projectId/platforms')
|
|||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY, Origin::CLIENT_TYPE_REACT_NATIVE_IOS, Origin::CLIENT_TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
|
||||
->param('type', null, new WhiteList([Platform::TYPE_WEB, Platform::TYPE_FLUTTER_WEB, Platform::TYPE_FLUTTER_IOS, Platform::TYPE_FLUTTER_ANDROID, Platform::TYPE_FLUTTER_LINUX, Platform::TYPE_FLUTTER_MACOS, Platform::TYPE_FLUTTER_WINDOWS, Platform::TYPE_APPLE_IOS, Platform::TYPE_APPLE_MACOS, Platform::TYPE_APPLE_WATCHOS, Platform::TYPE_APPLE_TVOS, Platform::TYPE_ANDROID, Platform::TYPE_UNITY, Platform::TYPE_REACT_NATIVE_IOS, Platform::TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
|
||||
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
|
||||
->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true)
|
||||
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use Appwrite\Event\Messaging;
|
|||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\Platform\Workers\Deletes;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
|
|
@ -50,7 +51,6 @@ use Utopia\Locale\Locale;
|
|||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
|
@ -466,7 +466,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
}
|
||||
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) // TODO add our own built-in confirm page
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey']) // TODO add our own built-in confirm page
|
||||
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
|
|
|||
|
|
@ -649,6 +649,8 @@ App::get('/v1/users')
|
|||
$total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'users' => $users,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use Appwrite\Auth\OAuth2\Github as OAuth2Github;
|
|||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
|
|
@ -53,7 +54,6 @@ use Utopia\Detector\Detector\Runtime;
|
|||
use Utopia\Detector\Detector\Strategy;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
|
@ -116,8 +116,10 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
|||
}
|
||||
|
||||
$commentStatus = $isAuthorized ? 'waiting' : 'failed';
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
$authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
|
||||
$authorizeUrl = $protocol . '://' . $hostname . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
|
||||
|
||||
$action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl];
|
||||
|
||||
|
|
@ -378,7 +380,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
|||
}
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$providerTargetUrl = $protocol . '://' . $hostname . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name);
|
||||
}
|
||||
|
||||
|
|
@ -424,8 +426,8 @@ App::get('/v1/vcs/github/authorize')
|
|||
type: MethodType::WEBAUTH,
|
||||
hide: true,
|
||||
))
|
||||
->param('success', '', fn ($clients) => new Host($clients), 'URL to redirect back to console after a successful installation attempt.', true, ['clients'])
|
||||
->param('failure', '', fn ($clients) => new Host($clients), 'URL to redirect back to console after a failed installation attempt.', true, ['clients'])
|
||||
->param('success', '', fn ($platforms) => new Redirect($platforms), 'URL to redirect back to console after a successful installation attempt.', true, ['platforms'])
|
||||
->param('failure', '', fn ($platforms) => new Redirect($platforms), 'URL to redirect back to console after a failed installation attempt.', true, ['platforms'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
|
@ -437,6 +439,8 @@ App::get('/v1/vcs/github/authorize')
|
|||
]);
|
||||
|
||||
$appName = System::getEnv('_APP_VCS_GITHUB_APP_NAME');
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
if (empty($appName)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.');
|
||||
|
|
@ -444,7 +448,7 @@ App::get('/v1/vcs/github/authorize')
|
|||
|
||||
$url = "https://github.com/apps/$appName/installations/new?" . \http_build_query([
|
||||
'state' => $state,
|
||||
'redirect_uri' => $request->getProtocol() . '://' . $request->getHostname() . "/v1/vcs/github/callback"
|
||||
'redirect_uri' => $protocol . '://' . $hostname . "/v1/vcs/github/callback"
|
||||
]);
|
||||
|
||||
$response
|
||||
|
|
@ -494,10 +498,12 @@ App::get('/v1/vcs/github/callback')
|
|||
}
|
||||
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
$defaultState = [
|
||||
'success' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'failure' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'success' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'failure' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
];
|
||||
|
||||
$state = \array_merge($defaultState, $state ?? []);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ use Utopia\Logger\Log\User;
|
|||
use Utopia\Logger\Logger;
|
||||
use Utopia\Platform\Service;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
Config::setParam('domainVerification', false);
|
||||
|
|
@ -76,7 +75,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
}
|
||||
|
||||
$errorView = __DIR__ . '/../views/general/error.phtml';
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . '://' . System::getEnv('_APP_DOMAIN', '');
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . '://' . System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
if ($rule->isEmpty()) {
|
||||
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
|
||||
|
|
@ -267,7 +266,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
}
|
||||
|
||||
if (!$authorized) {
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_DOMAIN');
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
|
|
@ -453,7 +452,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
|
||||
}
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
$endpoint = $protocol . '://' . $hostname . "/v1";
|
||||
|
||||
|
|
@ -799,6 +798,7 @@ App::init()
|
|||
->inject('getProjectDB')
|
||||
->inject('locale')
|
||||
->inject('localeCodes')
|
||||
->inject('platforms')
|
||||
->inject('geodb')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForEvents')
|
||||
|
|
@ -811,7 +811,7 @@ App::init()
|
|||
->inject('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) {
|
||||
->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 $platforms, 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
|
||||
*/
|
||||
|
|
@ -1030,11 +1030,12 @@ App::init()
|
|||
* Skip this check for non-web platforms which are not required to send an origin header
|
||||
*/
|
||||
$origin = $request->getOrigin($request->getReferer(''));
|
||||
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
$originValidator = new Origin($platforms);
|
||||
|
||||
if (
|
||||
!$originValidator->isValid($origin)
|
||||
&& $devKey->isEmpty()
|
||||
$devKey->isEmpty()
|
||||
&& !empty($origin)
|
||||
&& !$originValidator->isValid($origin)
|
||||
&& \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', ''))
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use Appwrite\Event\StatsUsage;
|
|||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\GraphQL\Schema;
|
||||
use Appwrite\Network\Platform;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Executor\Executor;
|
||||
|
|
@ -79,9 +80,15 @@ App::setResource('localeCodes', function () {
|
|||
App::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
App::setResource('publisherRedis', function () {
|
||||
// Stub
|
||||
});
|
||||
App::setResource('consumer', function (Group $pools) {
|
||||
return new BrokerPool(consumer: $pools->get('consumer'));
|
||||
}, ['pools']);
|
||||
App::setResource('consumerRedis', function () {
|
||||
// Stub
|
||||
});
|
||||
App::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
return new Messaging($publisher);
|
||||
}, ['publisher']);
|
||||
|
|
@ -121,11 +128,11 @@ App::setResource('queueForCertificates', function (Publisher $publisher) {
|
|||
App::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||
return new Migration($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('clients', function ($request, $console, $project) {
|
||||
App::setResource('platforms', function (Request $request, Document $console, Document $project) {
|
||||
$console->setAttribute('platforms', [ // Always allow current host
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Current Host',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'hostname' => $request->getHostname(),
|
||||
], Document::SET_TYPE_APPEND);
|
||||
|
||||
|
|
@ -138,39 +145,32 @@ App::setResource('clients', function ($request, $console, $project) {
|
|||
}
|
||||
$console->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'name' => $hostname,
|
||||
'hostname' => $hostname,
|
||||
], Document::SET_TYPE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get All verified client URLs for both console and current projects
|
||||
* + Filter for duplicated entries
|
||||
*/
|
||||
$clientsConsole = \array_map(
|
||||
fn ($node) => $node['hostname'],
|
||||
\array_filter(
|
||||
$console->getAttribute('platforms', []),
|
||||
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname']))
|
||||
)
|
||||
);
|
||||
|
||||
$clients = $clientsConsole;
|
||||
$platforms = $project->getAttribute('platforms', []);
|
||||
|
||||
foreach ($platforms as $node) {
|
||||
if (
|
||||
isset($node['type']) &&
|
||||
($node['type'] === Origin::CLIENT_TYPE_WEB ||
|
||||
$node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) &&
|
||||
!empty($node['hostname'])
|
||||
) {
|
||||
$clients[] = $node['hostname'];
|
||||
}
|
||||
// Add `exp` and `appwrite-callback-{projectId}` schemes
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$project->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Platform::TYPE_SCHEME,
|
||||
'name' => 'Expo',
|
||||
'key' => 'exp',
|
||||
], Document::SET_TYPE_APPEND);
|
||||
$project->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Platform::TYPE_SCHEME,
|
||||
'name' => 'Appwrite Callback',
|
||||
'key' => 'appwrite-callback-' . $project->getId(),
|
||||
], Document::SET_TYPE_APPEND);
|
||||
}
|
||||
|
||||
return \array_unique($clients);
|
||||
return [
|
||||
...$console->getAttribute('platforms', []),
|
||||
...$project->getAttribute('platforms', []),
|
||||
];
|
||||
}, ['request', 'console', 'project']);
|
||||
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
||||
|
|
@ -222,7 +222,9 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
|||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
|
||||
if (APP_MODE_ADMIN !== $mode) {
|
||||
if ($mode === APP_MODE_ADMIN) {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
} else {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new Document([]);
|
||||
} else {
|
||||
|
|
@ -232,8 +234,6 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
|||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
@ -264,7 +264,11 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
|||
|
||||
$jwtUserId = $payload['userId'] ?? '';
|
||||
if (!empty($jwtUserId)) {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
if ($mode === APP_MODE_ADMIN) {
|
||||
$user = $dbForPlatform->getDocument('users', $jwtUserId);
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
}
|
||||
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
|
|
@ -950,7 +954,7 @@ App::setResource('httpReferrer', function (Request $request): string {
|
|||
return $referrer;
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $clients, Database $dbForPlatform, Document $project, App $utopia): string {
|
||||
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $platforms, 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);
|
||||
|
|
@ -963,8 +967,8 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef
|
|||
}
|
||||
|
||||
// Safe if added as web platform
|
||||
$validator = new Hostname($clients);
|
||||
if ($validator->isValid($origin)) {
|
||||
$originValidator = new Origin($platforms);
|
||||
if ($originValidator->isValid($request->getOrigin($httpReferrer))) {
|
||||
return $referrer;
|
||||
}
|
||||
|
||||
|
|
@ -992,4 +996,4 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef
|
|||
$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']);
|
||||
}, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']);
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
}
|
||||
|
||||
$timelimit = $app->getResource('timelimit');
|
||||
$console = $app->getResource('console'); /** @var Document $console */
|
||||
$platforms = $app->getResource('platforms');
|
||||
$user = $app->getResource('user'); /** @var Document $user */
|
||||
|
||||
/*
|
||||
|
|
@ -557,9 +557,9 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
* Skip this check for non-web platforms which are not required to send an origin header.
|
||||
*/
|
||||
$origin = $request->getOrigin();
|
||||
$originValidator = new Origin(array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
$originValidator = new Origin($platforms);
|
||||
|
||||
if (!$originValidator->isValid($origin) && $project->getId() !== 'console') {
|
||||
if (!empty($origin) && !$originValidator->isValid($origin) && $project->getId() !== 'console') {
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ $labelClass = '';
|
|||
$buttons = [];
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
// TODO: remove this later
|
||||
if (System::getEnv('_APP_ENV') === 'development') {
|
||||
$hostname = 'localhost';
|
||||
|
|
|
|||
|
|
@ -247,10 +247,18 @@ Server::setResource('publisher', function (Group $pools) {
|
|||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('publisherRedis', function () {
|
||||
// Stub
|
||||
});
|
||||
|
||||
Server::setResource('consumer', function (Group $pools) {
|
||||
return new BrokerPool(consumer: $pools->get('consumer'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('consumerRedis', function () {
|
||||
// Stub
|
||||
});
|
||||
|
||||
Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
|
|
|
|||
57
composer.lock
generated
57
composer.lock
generated
|
|
@ -3493,16 +3493,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.71.8",
|
||||
"version": "0.71.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "7dff6b67a54f1a7f9d3f210db4c6e40d7052b79e"
|
||||
"reference": "eb2f759020bba617e99dd67973a9bd949b47f54e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/7dff6b67a54f1a7f9d3f210db4c6e40d7052b79e",
|
||||
"reference": "7dff6b67a54f1a7f9d3f210db4c6e40d7052b79e",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb2f759020bba617e99dd67973a9bd949b47f54e",
|
||||
"reference": "eb2f759020bba617e99dd67973a9bd949b47f54e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3543,9 +3543,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.71.8"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.71.9"
|
||||
},
|
||||
"time": "2025-06-26T14:48:17+00:00"
|
||||
"time": "2025-07-02T16:37:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
|
|
@ -3993,16 +3993,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.10.1",
|
||||
"version": "0.10.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f"
|
||||
"reference": "0c85917482db172b3ccdc0704e42af3c1cc89361"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ea1c585df7ec5f346f061a11581fc9a91679966f",
|
||||
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/0c85917482db172b3ccdc0704e42af3c1cc89361",
|
||||
"reference": "0c85917482db172b3ccdc0704e42af3c1cc89361",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4043,9 +4043,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.10.1"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.10.4"
|
||||
},
|
||||
"time": "2025-05-26T15:29:19+00:00"
|
||||
"time": "2025-07-02T18:31:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
|
|
@ -4810,16 +4810,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.41.9",
|
||||
"version": "0.41.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "61037c1ed9262308cab49c1d760f3278036ab694"
|
||||
"reference": "60122cb613a5a1c82667ecc2217e351654a8d404"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/61037c1ed9262308cab49c1d760f3278036ab694",
|
||||
"reference": "61037c1ed9262308cab49c1d760f3278036ab694",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/60122cb613a5a1c82667ecc2217e351654a8d404",
|
||||
"reference": "60122cb613a5a1c82667ecc2217e351654a8d404",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4855,9 +4855,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.9"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.11"
|
||||
},
|
||||
"time": "2025-06-27T10:16:17+00:00"
|
||||
"time": "2025-07-04T09:56:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
|
|
@ -5084,16 +5084,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/pint.git",
|
||||
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7"
|
||||
"reference": "9ab851dba4faa51a3c3223dd3d07044129021024"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7",
|
||||
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7",
|
||||
"url": "https://api.github.com/repos/laravel/pint/zipball/9ab851dba4faa51a3c3223dd3d07044129021024",
|
||||
"reference": "9ab851dba4faa51a3c3223dd3d07044129021024",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5104,10 +5104,10 @@
|
|||
"php": "^8.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.75.0",
|
||||
"illuminate/view": "^11.44.7",
|
||||
"larastan/larastan": "^3.4.0",
|
||||
"laravel-zero/framework": "^11.36.1",
|
||||
"friendsofphp/php-cs-fixer": "^3.76.0",
|
||||
"illuminate/view": "^11.45.1",
|
||||
"larastan/larastan": "^3.5.0",
|
||||
"laravel-zero/framework": "^11.45.0",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"nunomaduro/termwind": "^2.3.1",
|
||||
"pestphp/pest": "^2.36.0"
|
||||
|
|
@ -5117,6 +5117,9 @@
|
|||
],
|
||||
"type": "project",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"overrides/Runner/Parallel/ProcessFactory.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Seeders\\": "database/seeders/",
|
||||
|
|
@ -5146,7 +5149,7 @@
|
|||
"issues": "https://github.com/laravel/pint/issues",
|
||||
"source": "https://github.com/laravel/pint"
|
||||
},
|
||||
"time": "2025-05-08T08:38:12+00:00"
|
||||
"time": "2025-07-03T10:37:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "matthiasmullie/minify",
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ services:
|
|||
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DOMAIN
|
||||
- _APP_CONSOLE_DOMAIN
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
|
|
@ -213,7 +214,7 @@ services:
|
|||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:6.0.43
|
||||
image: appwrite/console:6.1.2
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
|
@ -482,6 +483,7 @@ services:
|
|||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
|
||||
- _APP_DOMAIN
|
||||
- _APP_CONSOLE_DOMAIN
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_S3_SECRET
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setSession(''); // The user session to authenticate with
|
||||
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const result = await databases.upsertDocument(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
{}, // data
|
||||
["read("any")"] // permissions (optional)
|
||||
);
|
||||
|
|
@ -10,5 +10,5 @@ const databases = new sdk.Databases(client);
|
|||
const result = await databases.upsertDocuments(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
[] // documents (optional)
|
||||
[] // documents
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
# Change Log
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Fix circular dependency issue due to usage of `success` method in `utils.js` file from `parser.js` file
|
||||
* Type generation fixes:
|
||||
* Add ability to generate types directly to a specific file by passing a file path to `appwrite types output_path`, instead of just a directory
|
||||
* Fix non-required attributes to not be null if default value is provided
|
||||
* Fix `Models` import error
|
||||
* Improve formatting and add auto-generated comments
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Add multi-region support to `init` command
|
||||
|
|
|
|||
|
|
@ -1,13 +1,68 @@
|
|||
# Change Log
|
||||
|
||||
## 17.1.0
|
||||
|
||||
* Add `upsertDocument` method
|
||||
* Add `dart-3.8` and `flutter-3.32` runtimes
|
||||
* Add `gif` image format
|
||||
* Update bulk operation methods to reflect warning message
|
||||
* Fix file parameter handling in chunked upload method
|
||||
|
||||
## 17.0.0
|
||||
|
||||
* Add `<REGION>` to doc examples due to the new multi region endpoints
|
||||
* Add `REGION` to doc examples due to the new multi region endpoints
|
||||
* Add doc examples and methods for bulk api transactions: `createDocuments`, `deleteDocuments` etc.
|
||||
* Add doc examples, class and methods for new `Sites` service
|
||||
* Add doc examples, class and methods for new `Tokens` service
|
||||
* Add enums for `BuildRuntime `, `Adapter`, `Framework`, `DeploymentDownloadType` and `VCSDeploymentType`
|
||||
* Add enums for `BuildRuntime`, `Adapter`, `Framework`, `DeploymentDownloadType` and `VCSDeploymentType`
|
||||
* Updates enum for `runtimes` with Pythonml312, Dart219, Flutter327 and Flutter329
|
||||
* Add `token` param to `getFilePreview` and `getFileView` for File tokens usage
|
||||
* Add `queries` and `search` params to `listMemberships` method
|
||||
* Removes `search` param from `listExecutions` method
|
||||
* Removes `search` param from `listExecutions` method
|
||||
|
||||
## 16.0.0
|
||||
|
||||
* Fix: remove content-type from GET requests
|
||||
* Update (breaking): min and max params are now optional in `updateFloatAttribute` and `updateIntegerAttribute` methods (changes their positioning in method definition)
|
||||
|
||||
## 15.0.1
|
||||
|
||||
* Remove titles from all function descriptions
|
||||
* Fix typing for collection "attribute" key
|
||||
* Remove unnecessary awaits and asyncs
|
||||
* Ensure `AppwriteException` response is always string
|
||||
|
||||
## 15.0.0
|
||||
|
||||
* Fix: pong response & chunked upload
|
||||
|
||||
## 14.2.0
|
||||
|
||||
* Add new push message parameters
|
||||
|
||||
## 14.1.0
|
||||
|
||||
* Support updating attribute name and size
|
||||
|
||||
## 14.0.0
|
||||
|
||||
* Support for Appwrite 1.6
|
||||
* Add `key` attribute to `Runtime` response model.
|
||||
* Add `buildSize` attribute to `Deployments` response model
|
||||
* Add `scheduledAt` attribute to `Executions` response model
|
||||
* Add `scopes` attribute to `Functions` response model
|
||||
* Add `specifications` attribute to `Functions` response model
|
||||
* Add new response model for `Specifications`
|
||||
* Add new response model for `Builds`
|
||||
* Add `createJWT()` : Enables creating a JWT using the `userId`
|
||||
* Add `listSpecifications()`: Enables listing available runtime specifications
|
||||
* Add `deleteExecution()` : Enables deleting executions
|
||||
* Add `updateDeploymentBuild()`: Enables cancelling a deployment
|
||||
* Add `scheduledAt` parameter to `createExecution()`: Enables creating a delayed execution
|
||||
* Breaking changes
|
||||
* Remove `otp` parameter from `deleteMFAAuthenticator`.
|
||||
* Add `scopes` parameter for create/update function.
|
||||
* Rename `templateBranch` to `templateVersion` in `createFunction()`.
|
||||
* Rename `downloadDeployment()` to `getDeploymentDownload()`
|
||||
|
||||
> You can find the new syntax for breaking changes in the [Appwrite API references](https://appwrite.io/docs/references). Select version `1.6.x`.
|
||||
|
|
@ -226,7 +226,7 @@ class Mapper
|
|||
];
|
||||
|
||||
if (!$rule['required']) {
|
||||
$fields[$escapedKey]['defaultValue'] = $rule['default'];
|
||||
$fields[$escapedKey]['defaultValue'] = $rule['default'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
159
src/Appwrite/Network/Platform.php
Normal file
159
src/Appwrite/Network/Platform.php
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Network;
|
||||
|
||||
class Platform
|
||||
{
|
||||
public const TYPE_UNKNOWN = 'unknown';
|
||||
public const TYPE_WEB = 'web';
|
||||
public const TYPE_FLUTTER_IOS = 'flutter-ios';
|
||||
public const TYPE_FLUTTER_ANDROID = 'flutter-android';
|
||||
public const TYPE_FLUTTER_MACOS = 'flutter-macos';
|
||||
public const TYPE_FLUTTER_WINDOWS = 'flutter-windows';
|
||||
public const TYPE_FLUTTER_LINUX = 'flutter-linux';
|
||||
public const TYPE_FLUTTER_WEB = 'flutter-web';
|
||||
public const TYPE_APPLE_IOS = 'apple-ios';
|
||||
public const TYPE_APPLE_MACOS = 'apple-macos';
|
||||
public const TYPE_APPLE_WATCHOS = 'apple-watchos';
|
||||
public const TYPE_APPLE_TVOS = 'apple-tvos';
|
||||
public const TYPE_ANDROID = 'android';
|
||||
public const TYPE_UNITY = 'unity';
|
||||
public const TYPE_REACT_NATIVE_IOS = 'react-native-ios';
|
||||
public const TYPE_REACT_NATIVE_ANDROID = 'react-native-android';
|
||||
public const TYPE_SCHEME = 'scheme';
|
||||
|
||||
public const SCHEME_HTTP = 'http';
|
||||
public const SCHEME_HTTPS = 'https';
|
||||
public const SCHEME_CHROME_EXTENSION = 'chrome-extension';
|
||||
public const SCHEME_FIREFOX_EXTENSION = 'moz-extension';
|
||||
public const SCHEME_SAFARI_EXTENSION = 'safari-web-extension';
|
||||
public const SCHEME_EDGE_EXTENSION = 'ms-browser-extension';
|
||||
public const SCHEME_IOS = 'appwrite-ios';
|
||||
public const SCHEME_MACOS = 'appwrite-macos';
|
||||
public const SCHEME_WATCHOS = 'appwrite-watchos';
|
||||
public const SCHEME_TVOS = 'appwrite-tvos';
|
||||
public const SCHEME_ANDROID = 'appwrite-android';
|
||||
public const SCHEME_WINDOWS = 'appwrite-windows';
|
||||
public const SCHEME_LINUX = 'appwrite-linux';
|
||||
|
||||
/**
|
||||
* @var array<string, string> Map scheme types to user-friendly platform names.
|
||||
*/
|
||||
private static array $names = [
|
||||
self::SCHEME_HTTP => 'Web',
|
||||
self::SCHEME_HTTPS => 'Web',
|
||||
self::SCHEME_IOS => 'iOS',
|
||||
self::SCHEME_MACOS => 'macOS',
|
||||
self::SCHEME_WATCHOS => 'watchOS',
|
||||
self::SCHEME_TVOS => 'tvOS',
|
||||
self::SCHEME_ANDROID => 'Android',
|
||||
self::SCHEME_WINDOWS => 'Windows',
|
||||
self::SCHEME_LINUX => 'Linux',
|
||||
self::SCHEME_CHROME_EXTENSION => 'Web (Chrome Extension)',
|
||||
self::SCHEME_FIREFOX_EXTENSION => 'Web (Firefox Extension)',
|
||||
self::SCHEME_SAFARI_EXTENSION => 'Web (Safari Extension)',
|
||||
self::SCHEME_EDGE_EXTENSION => 'Web (Edge Extension)',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get user-friendly platform name from a scheme.
|
||||
*
|
||||
* @param string|null $scheme
|
||||
* @return string Empty string if scheme is not found.
|
||||
*/
|
||||
public static function getNameByScheme(?string $scheme): string
|
||||
{
|
||||
return self::$names[$scheme] ?? '';
|
||||
}
|
||||
|
||||
public static function getHostnames(array $platforms): array
|
||||
{
|
||||
$hostnames = [];
|
||||
foreach ($platforms as $platform) {
|
||||
$type = strtolower($platform['type'] ?? self::TYPE_UNKNOWN);
|
||||
$hostname = strtolower($platform['hostname'] ?? '');
|
||||
$key = strtolower($platform['key'] ?? '');
|
||||
|
||||
switch ($type) {
|
||||
case self::TYPE_WEB:
|
||||
case self::TYPE_FLUTTER_WEB:
|
||||
if (!empty($hostname)) {
|
||||
$hostnames[] = $hostname;
|
||||
}
|
||||
break;
|
||||
case self::TYPE_FLUTTER_IOS:
|
||||
case self::TYPE_FLUTTER_ANDROID:
|
||||
case self::TYPE_FLUTTER_MACOS:
|
||||
case self::TYPE_FLUTTER_WINDOWS:
|
||||
case self::TYPE_FLUTTER_LINUX:
|
||||
case self::TYPE_ANDROID:
|
||||
case self::TYPE_APPLE_IOS:
|
||||
case self::TYPE_APPLE_MACOS:
|
||||
case self::TYPE_APPLE_WATCHOS:
|
||||
case self::TYPE_APPLE_TVOS:
|
||||
case self::TYPE_REACT_NATIVE_IOS:
|
||||
case self::TYPE_REACT_NATIVE_ANDROID:
|
||||
case self::TYPE_UNITY:
|
||||
if (!empty($key)) {
|
||||
$hostnames[] = $key;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array_unique($hostnames);
|
||||
}
|
||||
|
||||
public static function getSchemes(array $platforms): array
|
||||
{
|
||||
$schemes = [];
|
||||
foreach ($platforms as $platform) {
|
||||
$type = strtolower($platform['type'] ?? self::TYPE_UNKNOWN);
|
||||
$scheme = strtolower($platform['key'] ?? '');
|
||||
|
||||
switch ($type) {
|
||||
case self::TYPE_SCHEME:
|
||||
if (!empty($scheme) && preg_match('/^[a-z][a-z0-9+.-]*$/', $scheme)) {
|
||||
$schemes[] = $scheme;
|
||||
}
|
||||
break;
|
||||
case self::TYPE_WEB:
|
||||
case self::TYPE_FLUTTER_WEB:
|
||||
$schemes[] = self::SCHEME_HTTP;
|
||||
$schemes[] = self::SCHEME_HTTPS;
|
||||
break;
|
||||
case self::TYPE_FLUTTER_IOS:
|
||||
case self::TYPE_APPLE_IOS:
|
||||
case self::TYPE_REACT_NATIVE_IOS:
|
||||
$schemes[] = self::SCHEME_IOS;
|
||||
break;
|
||||
case self::TYPE_FLUTTER_ANDROID:
|
||||
case self::TYPE_ANDROID:
|
||||
case self::TYPE_REACT_NATIVE_ANDROID:
|
||||
$schemes[] = self::SCHEME_ANDROID;
|
||||
break;
|
||||
case self::TYPE_FLUTTER_MACOS:
|
||||
case self::TYPE_APPLE_MACOS:
|
||||
$schemes[] = self::SCHEME_MACOS;
|
||||
break;
|
||||
case self::TYPE_FLUTTER_WINDOWS:
|
||||
case self::TYPE_UNITY:
|
||||
$schemes[] = self::SCHEME_WINDOWS;
|
||||
break;
|
||||
case self::TYPE_FLUTTER_LINUX:
|
||||
$schemes[] = self::SCHEME_LINUX;
|
||||
break;
|
||||
case self::TYPE_APPLE_WATCHOS:
|
||||
$schemes[] = self::SCHEME_WATCHOS;
|
||||
break;
|
||||
case self::TYPE_APPLE_TVOS:
|
||||
$schemes[] = self::SCHEME_TVOS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array_unique($schemes);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,150 +2,85 @@
|
|||
|
||||
namespace Appwrite\Network\Validator;
|
||||
|
||||
use Appwrite\Network\Platform;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Hostname;
|
||||
|
||||
class Origin extends Validator
|
||||
{
|
||||
public const CLIENT_TYPE_UNKNOWN = 'unknown';
|
||||
public const CLIENT_TYPE_WEB = 'web';
|
||||
public const CLIENT_TYPE_FLUTTER_IOS = 'flutter-ios';
|
||||
public const CLIENT_TYPE_FLUTTER_ANDROID = 'flutter-android';
|
||||
public const CLIENT_TYPE_FLUTTER_MACOS = 'flutter-macos';
|
||||
public const CLIENT_TYPE_FLUTTER_WINDOWS = 'flutter-windows';
|
||||
public const CLIENT_TYPE_FLUTTER_LINUX = 'flutter-linux';
|
||||
public const CLIENT_TYPE_FLUTTER_WEB = 'flutter-web';
|
||||
public const CLIENT_TYPE_APPLE_IOS = 'apple-ios';
|
||||
public const CLIENT_TYPE_APPLE_MACOS = 'apple-macos';
|
||||
public const CLIENT_TYPE_APPLE_WATCHOS = 'apple-watchos';
|
||||
public const CLIENT_TYPE_APPLE_TVOS = 'apple-tvos';
|
||||
public const CLIENT_TYPE_ANDROID = 'android';
|
||||
public const CLIENT_TYPE_UNITY = 'unity';
|
||||
public const CLIENT_TYPE_REACT_NATIVE_IOS = 'react-native-ios';
|
||||
public const CLIENT_TYPE_REACT_NATIVE_ANDROID = 'react-native-android';
|
||||
|
||||
|
||||
public const SCHEME_TYPE_HTTP = 'http';
|
||||
public const SCHEME_TYPE_HTTPS = 'https';
|
||||
public const SCHEME_TYPE_IOS = 'appwrite-ios';
|
||||
public const SCHEME_TYPE_MACOS = 'appwrite-macos';
|
||||
public const SCHEME_TYPE_WATCHOS = 'appwrite-watchos';
|
||||
public const SCHEME_TYPE_TVOS = 'appwrite-tvos';
|
||||
public const SCHEME_TYPE_ANDROID = 'appwrite-android';
|
||||
public const SCHEME_TYPE_WINDOWS = 'appwrite-windows';
|
||||
public const SCHEME_TYPE_LINUX = 'appwrite-linux';
|
||||
protected array $hostnames = [];
|
||||
protected array $schemes = [];
|
||||
protected ?string $scheme = null;
|
||||
protected ?string $host = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* Constructor
|
||||
*
|
||||
* @param array<\Utopia\Database\Document> $platforms
|
||||
*/
|
||||
protected $platforms = [
|
||||
self::SCHEME_TYPE_HTTP => 'Web',
|
||||
self::SCHEME_TYPE_HTTPS => 'Web',
|
||||
self::SCHEME_TYPE_IOS => 'iOS',
|
||||
self::SCHEME_TYPE_MACOS => 'macOS',
|
||||
self::SCHEME_TYPE_WATCHOS => 'watchOS',
|
||||
self::SCHEME_TYPE_TVOS => 'tvOS',
|
||||
self::SCHEME_TYPE_ANDROID => 'Android',
|
||||
self::SCHEME_TYPE_WINDOWS => 'Windows',
|
||||
self::SCHEME_TYPE_LINUX => 'Linux',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $clients = [
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $client = self::CLIENT_TYPE_UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $host = '';
|
||||
|
||||
/**
|
||||
* @param string $target
|
||||
*/
|
||||
public function __construct($platforms)
|
||||
public function __construct(array $platforms)
|
||||
{
|
||||
foreach ($platforms as $platform) {
|
||||
$type = (isset($platform['type'])) ? $platform['type'] : '';
|
||||
|
||||
switch ($type) {
|
||||
case self::CLIENT_TYPE_WEB:
|
||||
case self::CLIENT_TYPE_FLUTTER_WEB:
|
||||
$this->clients[] = (isset($platform['hostname'])) ? $platform['hostname'] : '';
|
||||
break;
|
||||
|
||||
case self::CLIENT_TYPE_FLUTTER_IOS:
|
||||
case self::CLIENT_TYPE_FLUTTER_ANDROID:
|
||||
case self::CLIENT_TYPE_FLUTTER_MACOS:
|
||||
case self::CLIENT_TYPE_FLUTTER_WINDOWS:
|
||||
case self::CLIENT_TYPE_FLUTTER_LINUX:
|
||||
case self::CLIENT_TYPE_ANDROID:
|
||||
case self::CLIENT_TYPE_APPLE_IOS:
|
||||
case self::CLIENT_TYPE_APPLE_MACOS:
|
||||
case self::CLIENT_TYPE_APPLE_WATCHOS:
|
||||
case self::CLIENT_TYPE_APPLE_TVOS:
|
||||
case self::CLIENT_TYPE_REACT_NATIVE_IOS:
|
||||
case self::CLIENT_TYPE_REACT_NATIVE_ANDROID:
|
||||
$this->clients[] = (isset($platform['key'])) ? $platform['key'] : '';
|
||||
break;
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->hostnames = Platform::getHostnames($platforms);
|
||||
$this->schemes = Platform::getSchemes($platforms);
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
if (!\array_key_exists($this->client, $this->platforms)) {
|
||||
return 'Unsupported platform';
|
||||
}
|
||||
|
||||
return 'Invalid Origin. Register your new client (' . $this->host . ') as a new '
|
||||
. $this->platforms[$this->client] . ' platform on your project console dashboard';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Origin has been allowed
|
||||
* for access to the API
|
||||
*
|
||||
* @param mixed $origin
|
||||
*
|
||||
* Check if Origin is valid.
|
||||
* @param mixed $origin The Origin URI.
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($origin): bool
|
||||
{
|
||||
if (!is_string($origin)) {
|
||||
$this->scheme = null;
|
||||
$this->host = null;
|
||||
|
||||
if (!is_string($origin) || empty($origin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$scheme = \parse_url($origin, PHP_URL_SCHEME);
|
||||
$host = \parse_url($origin, PHP_URL_HOST);
|
||||
$this->scheme = $this->parseScheme($origin);
|
||||
$this->host = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
|
||||
|
||||
$this->host = $host;
|
||||
$this->client = $scheme;
|
||||
$webPlatforms = [
|
||||
Platform::SCHEME_HTTP,
|
||||
Platform::SCHEME_HTTPS,
|
||||
Platform::SCHEME_CHROME_EXTENSION,
|
||||
Platform::SCHEME_FIREFOX_EXTENSION,
|
||||
Platform::SCHEME_SAFARI_EXTENSION,
|
||||
Platform::SCHEME_EDGE_EXTENSION,
|
||||
];
|
||||
if (in_array($this->scheme, $webPlatforms, true)) {
|
||||
$validator = new Hostname($this->hostnames);
|
||||
return $validator->isValid($this->host);
|
||||
}
|
||||
|
||||
if (empty($host)) {
|
||||
if (!empty($this->scheme) && in_array($this->scheme, $this->schemes, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$validator = new Hostname($this->clients);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $validator->isValid($host);
|
||||
/**
|
||||
* Get Description
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
$platform = $this->scheme ? Platform::getNameByScheme($this->scheme) : null;
|
||||
$host = $this->host ? '(' . $this->host . ')' : '';
|
||||
|
||||
if (empty($this->host) && empty($this->scheme)) {
|
||||
return 'Invalid Origin.';
|
||||
}
|
||||
|
||||
return 'Invalid Origin. Register your new client ' . $host . ' as a new '
|
||||
. $platform . ' platform on your project console dashboard';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
|
|
@ -155,13 +90,35 @@ class Origin extends Validator
|
|||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Returns validator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the scheme from a URI string.
|
||||
*
|
||||
* @param string $uri The URI string to parse.
|
||||
* @return string|null The extracted scheme string (e.g., "http", "exp", "mailto")
|
||||
*/
|
||||
public function parseScheme(string $uri): ?string
|
||||
{
|
||||
$uri = trim($uri);
|
||||
if ($uri === '') {
|
||||
return null; // No scheme in empty string
|
||||
}
|
||||
|
||||
$scheme = parse_url($uri, PHP_URL_SCHEME);
|
||||
if ($scheme === false) {
|
||||
if (preg_match('/^([a-z][a-z0-9+.-]*):/i', $uri, $matches)) {
|
||||
return $matches[1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return $scheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
src/Appwrite/Network/Validator/Redirect.php
Normal file
25
src/Appwrite/Network/Validator/Redirect.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Network\Validator;
|
||||
|
||||
use Appwrite\Network\Platform;
|
||||
|
||||
class Redirect extends Origin
|
||||
{
|
||||
/**
|
||||
* Get Description
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
$platform = Platform::getNameByScheme($this->scheme);
|
||||
$host = $this->host ? '(' . $this->host . ')' : '';
|
||||
|
||||
if (empty($this->host) && empty($this->scheme)) {
|
||||
return 'Invalid URI.';
|
||||
}
|
||||
|
||||
return 'Invalid URI. Register your new client ' . $host . ' as a new '
|
||||
. $platform . ' platform on your project console dashboard';
|
||||
}
|
||||
}
|
||||
|
|
@ -446,7 +446,7 @@ class Builds extends Action
|
|||
Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
|
||||
|
||||
// Commit and push
|
||||
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($resource->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr);
|
||||
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git checkout -b ' . \escapeshellarg($branchName) . ' && git add . && git commit -m "Create ' . \escapeshellarg($resource->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to push code repository: ' . $stderr);
|
||||
|
|
@ -1473,7 +1473,7 @@ class Builds extends Action
|
|||
$name = "{$resourceName} ({$projectName})";
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
$projectId = $project->getId();
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ abstract class ScheduleBase extends Action
|
|||
protected array $schedules = [];
|
||||
|
||||
protected BrokerPool $publisher;
|
||||
protected ?BrokerPool $publisherRedis = null;
|
||||
|
||||
private ?Histogram $collectSchedulesTelemetryDuration = null;
|
||||
private ?Gauge $collectSchedulesTelemetryCount = null;
|
||||
|
|
@ -72,6 +73,13 @@ abstract class ScheduleBase extends Action
|
|||
Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started');
|
||||
|
||||
$this->publisher = new BrokerPool($pools->get('publisher'));
|
||||
|
||||
try {
|
||||
$this->publisherRedis = new BrokerPool($pools->get('publisherRedis'));
|
||||
} catch (\Throwable) {
|
||||
$this->publisherRedis = null;
|
||||
}
|
||||
|
||||
$this->scheduleTelemetryCount = $telemetry->createGauge('task.schedule.count');
|
||||
$this->collectSchedulesTelemetryDuration = $telemetry->createHistogram('task.schedule.collect_schedules.duration', 's');
|
||||
$this->collectSchedulesTelemetryCount = $telemetry->createGauge('task.schedule.collect_schedules.count');
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Appwrite\Event\Func;
|
|||
use Swoole\Coroutine as Co;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\System\System;
|
||||
|
||||
class ScheduleExecutions extends ScheduleBase
|
||||
{
|
||||
|
|
@ -29,9 +30,16 @@ class ScheduleExecutions extends ScheduleBase
|
|||
|
||||
protected function enqueueResources(Group $pools, Database $dbForPlatform, callable $getProjectDB): void
|
||||
{
|
||||
$queueForFunctions = new Func($this->publisher);
|
||||
$intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds');
|
||||
|
||||
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'functions');
|
||||
|
||||
$queueForFunctions = new Func(
|
||||
$isRedisFallback
|
||||
? $this->publisherRedis
|
||||
: $this->publisher
|
||||
);
|
||||
|
||||
foreach ($this->schedules as $schedule) {
|
||||
if (!$schedule['active']) {
|
||||
$dbForPlatform->deleteDocument(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Utopia\CLI\Console;
|
|||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\System\System;
|
||||
|
||||
class ScheduleFunctions extends ScheduleBase
|
||||
{
|
||||
|
|
@ -90,7 +91,13 @@ class ScheduleFunctions extends ScheduleBase
|
|||
|
||||
$this->updateProjectAccess($schedule['project'], $dbForPlatform);
|
||||
|
||||
$queueForFunctions = new Func($this->publisher);
|
||||
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'functions');
|
||||
|
||||
$queueForFunctions = new Func(
|
||||
$isRedisFallback
|
||||
? $this->publisherRedis
|
||||
: $this->publisher
|
||||
);
|
||||
|
||||
$queueForFunctions
|
||||
->setType('schedule')
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@ class Migrations extends Action
|
|||
$migration->getAttribute('resourceType')
|
||||
);
|
||||
}
|
||||
|
||||
$destination->shutDown();
|
||||
$source->shutDown();
|
||||
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ class Comment
|
|||
|
||||
$i = 0;
|
||||
foreach ($projects as $projectId => $project) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN'));
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
$text .= "## {$project['name']}\n\n";
|
||||
$text .= "Project ID: `{$projectId}`\n\n";
|
||||
|
|
@ -200,8 +200,8 @@ class Comment
|
|||
|
||||
public function generatImage(string $pathLight, string $pathDark, string $alt, int $width): string
|
||||
{
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN'));
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
$imageLight = $protocol . '://' . $hostname . $pathLight;
|
||||
$imageDark = $protocol . '://' . $hostname . $pathDark;
|
||||
|
|
|
|||
|
|
@ -1414,9 +1414,20 @@ trait DatabasesBase
|
|||
$this->assertEquals($releaseYearIndex['body']['key'], $movies['body']['indexes'][1]['key']);
|
||||
$this->assertEquals($releaseWithDate1['body']['key'], $movies['body']['indexes'][2]['key']);
|
||||
$this->assertEquals($releaseWithDate2['body']['key'], $movies['body']['indexes'][3]['key']);
|
||||
foreach ($movies['body']['indexes'] as $index) {
|
||||
$this->assertEquals('available', $index['status']);
|
||||
}
|
||||
|
||||
$this->assertEventually(function () use ($databaseId, $data) {
|
||||
$movies = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
foreach ($movies['body']['indexes'] as $index) {
|
||||
$this->assertEquals('available', $index['status']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}, 60000, 500);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1470,9 +1470,24 @@ class DatabasesCustomServerTest extends Scope
|
|||
$this->assertCount(64, $collection['body']['attributes']);
|
||||
$this->assertCount(0, $collection['body']['indexes']);
|
||||
|
||||
foreach ($collection['body']['attributes'] as $attribute) {
|
||||
$this->assertEquals('available', $attribute['status'], 'attribute: ' . $attribute['key']);
|
||||
}
|
||||
$this->assertEventually(function () use ($databaseId, $collectionId) {
|
||||
$collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
foreach ($collection['body']['attributes'] ?? [] as $attribute) {
|
||||
$this->assertEquals(
|
||||
'available',
|
||||
$attribute['status'],
|
||||
'attribute: ' . $attribute['key']
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}, 60000, 500);
|
||||
|
||||
|
||||
// Test indexLimit = 64
|
||||
// MariaDB, MySQL, and MongoDB create 6 indexes per new collection
|
||||
|
|
|
|||
|
|
@ -4637,7 +4637,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'failure' => 'https://example.com'
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString('Invalid `success` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']);
|
||||
|
||||
/** Test oauth2 with devKey and now get oauth2 is disabled */
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [
|
||||
|
|
@ -4660,7 +4659,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'url' => 'https://example.com',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
$this->assertEquals('Invalid `url` param: URL host must be one of: localhost, appwrite.io, *.appwrite.io', $response['body']['message']);
|
||||
|
||||
/** Test hostname in Magic URL with devKey */
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [
|
||||
|
|
|
|||
|
|
@ -16,13 +16,6 @@ trait RealtimeBase
|
|||
$projectId = $this->getProject()['$id'];
|
||||
}
|
||||
|
||||
$headers = array_merge(
|
||||
[
|
||||
"Origin" => "appwrite.test",
|
||||
],
|
||||
$headers
|
||||
);
|
||||
|
||||
$query = [
|
||||
"project" => $projectId,
|
||||
"channels" => $channels,
|
||||
|
|
@ -49,7 +42,7 @@ trait RealtimeBase
|
|||
|
||||
public function testConnectionFailureMissingChannels(): void
|
||||
{
|
||||
$client = $this->getWebsocket();
|
||||
$client = $this->getWebsocket([]);
|
||||
$payload = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey("type", $payload);
|
||||
|
|
@ -64,14 +57,7 @@ trait RealtimeBase
|
|||
|
||||
public function testConnectionFailureUnknownProject(): void
|
||||
{
|
||||
$client = new WebSocketClient(
|
||||
"ws://appwrite-traefik/v1/realtime?project=123",
|
||||
[
|
||||
"headers" => [
|
||||
"Origin" => "appwrite.test",
|
||||
],
|
||||
]
|
||||
);
|
||||
$client = $this->getWebsocket(projectId: '123');
|
||||
$payload = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey("type", $payload);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\System\System;
|
||||
|
||||
class SitesCustomClientTest extends Scope
|
||||
{
|
||||
|
|
@ -121,6 +122,7 @@ class SitesCustomClientTest extends Scope
|
|||
* Test for SUCCESS
|
||||
*/
|
||||
$template = $this->getTemplate('starter-for-react');
|
||||
$hostname = System::getEnv('_APP_DOMAIN') ?: '';
|
||||
$this->assertEquals(200, $template['headers']['status-code']);
|
||||
$this->assertIsArray($template['body']);
|
||||
$this->assertEquals('starter-for-react', $template['body']['key']);
|
||||
|
|
@ -129,8 +131,8 @@ class SitesCustomClientTest extends Scope
|
|||
$this->assertEquals('github', $template['body']['vcsProvider']);
|
||||
$this->assertEquals('Simple React application integrated with Appwrite SDK.', $template['body']['tagline']);
|
||||
$this->assertIsArray($template['body']['frameworks']);
|
||||
$this->assertEquals('http://localhost/images/sites/templates/starter-for-react-dark.png', $template['body']['screenshotDark']);
|
||||
$this->assertEquals('http://localhost/images/sites/templates/starter-for-react-light.png', $template['body']['screenshotLight']);
|
||||
$this->assertEquals('http://'. $hostname . '/images/sites/templates/starter-for-react-dark.png', $template['body']['screenshotDark']);
|
||||
$this->assertEquals('http://' . $hostname . '/images/sites/templates/starter-for-react-light.png', $template['body']['screenshotLight']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\Unit\Network\Validators;
|
||||
|
||||
use Appwrite\Network\Platform;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
|
|
@ -14,62 +15,96 @@ class OriginTest extends TestCase
|
|||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Production',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'hostname' => 'appwrite.io',
|
||||
],
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Development',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'hostname' => 'appwrite.test',
|
||||
],
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Localhost',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'hostname' => 'localhost',
|
||||
],
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Flutter',
|
||||
'type' => Origin::CLIENT_TYPE_FLUTTER_WEB,
|
||||
'type' => Platform::TYPE_FLUTTER_WEB,
|
||||
'hostname' => 'appwrite.flutter',
|
||||
],
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Expo',
|
||||
'type' => Platform::TYPE_SCHEME,
|
||||
'key' => 'exp',
|
||||
],
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Appwrite Callback',
|
||||
'type' => Platform::TYPE_SCHEME,
|
||||
'key' => 'appwrite-callback-123',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($validator->isValid('https://localhost'), true);
|
||||
$this->assertEquals($validator->isValid('http://localhost'), true);
|
||||
$this->assertEquals($validator->isValid('http://localhost:80'), true);
|
||||
$this->assertEquals(false, $validator->isValid(''));
|
||||
$this->assertEquals(false, $validator->isValid('/'));
|
||||
|
||||
$this->assertEquals($validator->isValid('https://appwrite.io'), true);
|
||||
$this->assertEquals($validator->isValid('http://appwrite.io'), true);
|
||||
$this->assertEquals($validator->isValid('http://appwrite.io:80'), true);
|
||||
$this->assertEquals(true, $validator->isValid('https://localhost'));
|
||||
$this->assertEquals(true, $validator->isValid('http://localhost'));
|
||||
$this->assertEquals(true, $validator->isValid('http://localhost:80'));
|
||||
|
||||
$this->assertEquals($validator->isValid('https://appwrite.test'), true);
|
||||
$this->assertEquals($validator->isValid('http://appwrite.test'), true);
|
||||
$this->assertEquals($validator->isValid('http://appwrite.test:80'), true);
|
||||
$this->assertEquals(true, $validator->isValid('https://appwrite.io'));
|
||||
$this->assertEquals(true, $validator->isValid('http://appwrite.io'));
|
||||
$this->assertEquals(true, $validator->isValid('http://appwrite.io:80'));
|
||||
|
||||
$this->assertEquals($validator->isValid('https://appwrite.flutter'), true);
|
||||
$this->assertEquals($validator->isValid('http://appwrite.flutter'), true);
|
||||
$this->assertEquals($validator->isValid('http://appwrite.flutter:80'), true);
|
||||
$this->assertEquals(true, $validator->isValid('https://appwrite.test'));
|
||||
$this->assertEquals(true, $validator->isValid('http://appwrite.test'));
|
||||
$this->assertEquals(true, $validator->isValid('http://appwrite.test:80'));
|
||||
|
||||
$this->assertEquals($validator->isValid('https://example.com'), false);
|
||||
$this->assertEquals($validator->isValid('http://example.com'), false);
|
||||
$this->assertEquals($validator->isValid('http://example.com:80'), false);
|
||||
$this->assertEquals(true, $validator->isValid('https://appwrite.flutter'));
|
||||
$this->assertEquals(true, $validator->isValid('http://appwrite.flutter'));
|
||||
$this->assertEquals(true, $validator->isValid('http://appwrite.flutter:80'));
|
||||
|
||||
$this->assertEquals($validator->isValid('appwrite-ios://com.company.appname'), false);
|
||||
$this->assertEquals($validator->getDescription(), 'Invalid Origin. Register your new client (com.company.appname) as a new iOS platform on your project console dashboard');
|
||||
$this->assertEquals(false, $validator->isValid('https://example.com'));
|
||||
$this->assertEquals(false, $validator->isValid('http://example.com'));
|
||||
$this->assertEquals(false, $validator->isValid('http://example.com:80'));
|
||||
|
||||
$this->assertEquals($validator->isValid('appwrite-android://com.company.appname'), false);
|
||||
$this->assertEquals($validator->getDescription(), 'Invalid Origin. Register your new client (com.company.appname) as a new Android platform on your project console dashboard');
|
||||
$this->assertEquals(true, $validator->isValid('exp://'));
|
||||
$this->assertEquals(true, $validator->isValid('exp:///'));
|
||||
$this->assertEquals(true, $validator->isValid('exp://index'));
|
||||
|
||||
$this->assertEquals($validator->isValid('appwrite-macos://com.company.appname'), false);
|
||||
$this->assertEquals($validator->getDescription(), 'Invalid Origin. Register your new client (com.company.appname) as a new macOS platform on your project console dashboard');
|
||||
$this->assertEquals(true, $validator->isValid('appwrite-callback-123://'));
|
||||
$this->assertEquals(false, $validator->isValid('appwrite-callback-456://'));
|
||||
|
||||
$this->assertEquals($validator->isValid('appwrite-linux://com.company.appname'), false);
|
||||
$this->assertEquals($validator->getDescription(), 'Invalid Origin. Register your new client (com.company.appname) as a new Linux platform on your project console dashboard');
|
||||
$this->assertEquals(false, $validator->isValid('appwrite-ios://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new iOS platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals($validator->isValid('appwrite-windows://com.company.appname'), false);
|
||||
$this->assertEquals($validator->getDescription(), 'Invalid Origin. Register your new client (com.company.appname) as a new Windows platform on your project console dashboard');
|
||||
$this->assertEquals(false, $validator->isValid('appwrite-android://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Android platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals(false, $validator->isValid('appwrite-macos://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new macOS platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals(false, $validator->isValid('appwrite-linux://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Linux platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals(false, $validator->isValid('appwrite-windows://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Windows platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals(false, $validator->isValid('chrome-extension://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Chrome Extension) platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals(false, $validator->isValid('moz-extension://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Firefox Extension) platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals(false, $validator->isValid('safari-web-extension://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Safari Extension) platform on your project console dashboard', $validator->getDescription());
|
||||
|
||||
$this->assertEquals(false, $validator->isValid('ms-browser-extension://com.company.appname'));
|
||||
$this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Edge Extension) platform on your project console dashboard', $validator->getDescription());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue