mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81a8b661ad |
@@ -22,7 +22,6 @@ _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
|
||||
@@ -85,9 +84,8 @@ _APP_COMPUTE_MAINTENANCE_INTERVAL=600
|
||||
_APP_COMPUTE_RUNTIMES_NETWORK=runtimes
|
||||
_APP_EXECUTOR_SECRET=your-secret-key
|
||||
_APP_EXECUTOR_HOST=http://exc1/v1
|
||||
_APP_BROWSER_HOST=http://appwrite-browser:3000/v1
|
||||
_APP_FUNCTIONS_RUNTIMES=node-22
|
||||
_APP_SITES_RUNTIMES=static-1,node-22
|
||||
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
|
||||
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.32
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
_APP_MAINTENANCE_START_TIME=12:00
|
||||
_APP_MAINTENANCE_RETENTION_CACHE=2592000
|
||||
|
||||
@@ -178,7 +178,6 @@ jobs:
|
||||
- name: Load and Start Appwrite
|
||||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
sed -i 's|^_APP_BROWSER_HOST=.*|_APP_BROWSER_HOST=http://invalid-browser/v1|' .env
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
@@ -199,7 +198,7 @@ jobs:
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
|
||||
|
||||
e2e_shared_mode_test:
|
||||
name: E2E Shared Mode Service Test
|
||||
@@ -278,12 +277,14 @@ jobs:
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
|
||||
|
||||
e2e_dev_keys:
|
||||
name: E2E Service Test (Dev Keys)
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -359,85 +360,3 @@ jobs:
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
|
||||
|
||||
e2e_screenshots_keys:
|
||||
name: E2E Service Test (Site Screenshots)
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Load and Start Appwrite
|
||||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Run Site tests with browser connected in dedicated table mode
|
||||
run: |
|
||||
echo "Keeping original value of _APP_BROWSER_HOST"
|
||||
echo "Using project tables"
|
||||
export _APP_DATABASE_SHARED_TABLES=
|
||||
export _APP_DATABASE_SHARED_TABLES_V1=
|
||||
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
|
||||
|
||||
e2e_screenshots_shared_mode:
|
||||
name: E2E Shared Mode Service Test (Site Screenshots)
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ setup, check_database_changes ]
|
||||
if: needs.check_database_changes.outputs.database_changed == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tables-mode: [
|
||||
'Shared V1',
|
||||
'Shared V2',
|
||||
]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Load and Start Appwrite
|
||||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Run Site tests with browser connected in ${{ matrix.tables-mode }} table mode
|
||||
run: |
|
||||
echo "Keeping original value of _APP_BROWSER_HOST"
|
||||
if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then
|
||||
echo "Using shared tables V1"
|
||||
export _APP_DATABASE_SHARED_TABLES=database_db_main
|
||||
export _APP_DATABASE_SHARED_TABLES_V1=database_db_main
|
||||
elif [ "${{ matrix.tables-mode }}" == "Shared V2" ]; then
|
||||
echo "Using shared tables V2"
|
||||
export _APP_DATABASE_SHARED_TABLES=database_db_main
|
||||
export _APP_DATABASE_SHARED_TABLES_V1=
|
||||
fi
|
||||
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
|
||||
|
||||
+4
-6
@@ -188,18 +188,16 @@ 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\Platform;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\System\System;
|
||||
|
||||
@@ -23,7 +23,7 @@ $console = [
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Localhost',
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'hostname' => 'localhost',
|
||||
], // Current host is added on app init
|
||||
],
|
||||
|
||||
@@ -59,7 +59,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '17.0.2',
|
||||
'version' => '17.0.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -134,7 +134,7 @@ return [
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.10.1',
|
||||
'version' => '0.10.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -217,7 +217,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '8.2.2',
|
||||
'version' => '8.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -231,7 +231,6 @@ return [
|
||||
'gitRepoName' => 'sdk-for-cli',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'repoBranch' => 'master',
|
||||
'exclude' => [
|
||||
'services' => [
|
||||
['name' => 'assistant'],
|
||||
@@ -251,7 +250,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '17.1.0',
|
||||
'version' => '17.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -359,7 +358,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.14.0',
|
||||
'version' => '0.13.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -377,7 +376,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '16.1.0',
|
||||
'version' => '16.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -6,8 +6,13 @@ use Utopia\System\System;
|
||||
* List of Appwrite Sites templates
|
||||
*/
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN', '');
|
||||
$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';
|
||||
}
|
||||
|
||||
$url = $protocol . '://' . $hostname;
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ 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;
|
||||
@@ -61,6 +60,7 @@ 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,23 +1182,16 @@ 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 ($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('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('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')
|
||||
->inject('project')
|
||||
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||
|
||||
if (!$providerEnabled) {
|
||||
@@ -1223,20 +1216,12 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$redirectBase = $protocol . '://' . $host;
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$redirectBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$redirectBase .= ':' . $port;
|
||||
}
|
||||
|
||||
if (empty($success)) {
|
||||
$success = $redirectBase . $oauthDefaultSuccess;
|
||||
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
|
||||
}
|
||||
|
||||
if (empty($failure)) {
|
||||
$failure = $redirectBase . $oauthDefaultFailure;
|
||||
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
|
||||
}
|
||||
|
||||
$oauth2 = new $className($appId, $appSecret, $callback, [
|
||||
@@ -1266,14 +1251,9 @@ App::get('/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) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
|
||||
$domain = $request->getHostname();
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$params = $request->getParams();
|
||||
$params['project'] = $projectId;
|
||||
@@ -1282,7 +1262,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
. \http_build_query($params));
|
||||
});
|
||||
|
||||
@@ -1302,14 +1282,9 @@ 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) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
|
||||
$domain = $request->getHostname();
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$params = $request->getParams();
|
||||
$params['project'] = $projectId;
|
||||
@@ -1318,7 +1293,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
. \http_build_query($params));
|
||||
});
|
||||
|
||||
@@ -1342,24 +1317,15 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('platforms')
|
||||
->inject('devKey')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
|
||||
$protocol = $request->getProtocol();
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
||||
$redirect = new Redirect($platforms);
|
||||
$validateURL = new URL();
|
||||
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||
@@ -1386,11 +1352,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$state = $defaultState;
|
||||
}
|
||||
|
||||
if ($devKey->isEmpty() && !$redirect->isValid($state['success'])) {
|
||||
if (!$validateURL->isValid($state['success'])) {
|
||||
throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL);
|
||||
}
|
||||
|
||||
if ($devKey->isEmpty() && !empty($state['failure']) && !$redirect->isValid($state['failure'])) {
|
||||
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
|
||||
throw new Exception(Exception::PROJECT_INVALID_FAILURE_URL);
|
||||
}
|
||||
$failure = [];
|
||||
@@ -1457,13 +1423,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
|
||||
$name = '';
|
||||
$nameOAuth = $oauth2->getUserName($accessToken);
|
||||
$userParam = $request->getParam('user');
|
||||
$userParam = \json_decode($request->getParam('user'), true);
|
||||
if (!empty($nameOAuth)) {
|
||||
$name = $nameOAuth;
|
||||
} elseif ($userParam !== null) {
|
||||
$userDecoded = \json_decode($userParam, true);
|
||||
if (isset($userDecoded['name']['firstName']) && isset($userDecoded['name']['lastName'])) {
|
||||
$name = $userDecoded['name']['firstName'] . ' ' . $userDecoded['name']['lastName'];
|
||||
} elseif (is_array($userParam)) {
|
||||
$nameParam = $userParam['name'];
|
||||
if (is_array($nameParam) && isset($nameParam['firstName']) && isset($nameParam['lastName'])) {
|
||||
$name = $nameParam['firstName'] . ' ' . $nameParam['lastName'];
|
||||
}
|
||||
}
|
||||
$email = $oauth2->getUserEmail($accessToken);
|
||||
@@ -1781,6 +1747,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$state['success']['query'] = URLParser::unparseQuery($query);
|
||||
$state['success'] = URLParser::unparse($state['success']);
|
||||
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
@@ -1811,23 +1779,16 @@ 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 ($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('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('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')
|
||||
->inject('project')
|
||||
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||
|
||||
if (!$providerEnabled) {
|
||||
@@ -1852,20 +1813,12 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$redirectBase = $protocol . '://' . $host;
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$redirectBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$redirectBase .= ':' . $port;
|
||||
}
|
||||
|
||||
if (empty($success)) {
|
||||
$success = $redirectBase . $oauthDefaultSuccess;
|
||||
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
|
||||
}
|
||||
|
||||
if (empty($failure)) {
|
||||
$failure = $redirectBase . $oauthDefaultFailure;
|
||||
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
|
||||
}
|
||||
|
||||
$oauth2 = new $className($appId, $appSecret, $callback, [
|
||||
@@ -1907,7 +1860,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 ($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('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('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')
|
||||
@@ -2008,16 +1961,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
if (empty($url)) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $host;
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
$url = $callbackBase . '/console/auth/magic-url';
|
||||
$url = $request->getProtocol() . '://' . $request->getHostname() . '/console/auth/magic-url';
|
||||
}
|
||||
|
||||
$url = Template::parseURL($url);
|
||||
@@ -3202,7 +3146,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 ($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'])
|
||||
->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'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
@@ -3469,7 +3413,7 @@ App::post('/v1/account/verification')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{userId}')
|
||||
->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
|
||||
->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
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
||||
@@ -2958,7 +2958,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||
$collection->getAttribute('attributes'),
|
||||
$dbForProject->getAdapter()->getMaxIndexLength(),
|
||||
$dbForProject->getAdapter()->getInternalIndexesKeys(),
|
||||
$dbForProject->getAdapter()->getSupportForIndexArray(),
|
||||
);
|
||||
|
||||
if (!$validator->isValid($index)) {
|
||||
@@ -4310,7 +4309,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
}
|
||||
|
||||
$data['$id'] = $documentId;
|
||||
$data['$permissions'] = $permissions ?? [];
|
||||
$data['$permissions'] = $permissions;
|
||||
$newDocument = new Document($data);
|
||||
|
||||
$operations = 0;
|
||||
|
||||
@@ -523,16 +523,11 @@ 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, ?Publisher $publisherRedis, Response $response) {
|
||||
->action(function (string $name, int|string $threshold, Publisher $publisher, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'databases');
|
||||
|
||||
$size = $isRedisFallback
|
||||
? $publisherRedis->getQueueSize(new Queue($name))
|
||||
: $publisher->getQueueSize(new Queue($name));
|
||||
$size = $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}.");
|
||||
@@ -660,16 +655,11 @@ 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, ?Publisher $publisherRedis, Response $response) {
|
||||
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$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));
|
||||
$size = $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([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('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('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)
|
||||
|
||||
@@ -967,7 +967,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
|
||||
}
|
||||
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
@@ -988,7 +987,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
/* @type Document $file */
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
@@ -1159,7 +1157,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForFiles')
|
||||
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
|
||||
/* @type Document $bucket */
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
@@ -1177,10 +1175,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
/* @type Document $file */
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
@@ -1320,7 +1317,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForFiles')
|
||||
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
@@ -1338,10 +1334,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
/* @type Document $file */
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ 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;
|
||||
@@ -51,6 +50,7 @@ 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 ($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('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('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
@@ -54,6 +53,7 @@ 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,10 +116,8 @@ $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 = $protocol . '://' . $hostname . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
|
||||
$authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
|
||||
|
||||
$action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl];
|
||||
|
||||
@@ -380,7 +378,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
}
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
$providerTargetUrl = $protocol . '://' . $hostname . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name);
|
||||
}
|
||||
|
||||
@@ -426,8 +424,8 @@ App::get('/v1/vcs/github/authorize')
|
||||
type: MethodType::WEBAUTH,
|
||||
hide: true,
|
||||
))
|
||||
->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'])
|
||||
->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'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
@@ -439,8 +437,6 @@ 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.');
|
||||
@@ -448,7 +444,7 @@ App::get('/v1/vcs/github/authorize')
|
||||
|
||||
$url = "https://github.com/apps/$appName/installations/new?" . \http_build_query([
|
||||
'state' => $state,
|
||||
'redirect_uri' => $protocol . '://' . $hostname . "/v1/vcs/github/callback"
|
||||
'redirect_uri' => $request->getProtocol() . '://' . $request->getHostname() . "/v1/vcs/github/callback"
|
||||
]);
|
||||
|
||||
$response
|
||||
@@ -498,12 +494,10 @@ 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' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'failure' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'success' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'failure' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
|
||||
];
|
||||
|
||||
$state = \array_merge($defaultState, $state ?? []);
|
||||
|
||||
@@ -49,6 +49,7 @@ 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);
|
||||
@@ -75,7 +76,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_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . '://' . System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
if ($rule->isEmpty()) {
|
||||
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
|
||||
@@ -266,7 +267,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_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_DOMAIN');
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
@@ -452,7 +453,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";
|
||||
|
||||
@@ -798,7 +799,6 @@ 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, 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) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, string $httpReferrer, string $httpReferrerSafe) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -1030,12 +1030,11 @@ 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($platforms);
|
||||
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
|
||||
if (
|
||||
$devKey->isEmpty()
|
||||
&& !empty($origin)
|
||||
&& !$originValidator->isValid($origin)
|
||||
!$originValidator->isValid($origin)
|
||||
&& $devKey->isEmpty()
|
||||
&& \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', ''))
|
||||
|
||||
@@ -129,6 +129,32 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
||||
]);
|
||||
});
|
||||
|
||||
App::patch('/v1/mock/functions-v2')
|
||||
->desc('Update Function Version to V2 (outdated code syntax)')
|
||||
->groups(['mock', 'api', 'functions'])
|
||||
->label('scope', 'functions.write')
|
||||
->label('docs', false)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, Response $response, Database $dbForProject) {
|
||||
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
|
||||
|
||||
if (!$isDevelopment) {
|
||||
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('version', 'v2'));
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/mock/api-key-unprefixed')
|
||||
->desc('Create API Key (without standard prefix)')
|
||||
->groups(['mock', 'api', 'projects'])
|
||||
|
||||
@@ -576,6 +576,7 @@ App::init()
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
|
||||
|
||||
+36
-40
@@ -19,7 +19,6 @@ 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;
|
||||
@@ -80,15 +79,9 @@ 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']);
|
||||
@@ -128,11 +121,11 @@ App::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
App::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||
return new Migration($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('platforms', function (Request $request, Document $console, Document $project) {
|
||||
App::setResource('clients', function ($request, $console, $project) {
|
||||
$console->setAttribute('platforms', [ // Always allow current host
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Current Host',
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'hostname' => $request->getHostname(),
|
||||
], Document::SET_TYPE_APPEND);
|
||||
|
||||
@@ -145,32 +138,39 @@ App::setResource('platforms', function (Request $request, Document $console, Doc
|
||||
}
|
||||
$console->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'name' => $hostname,
|
||||
'hostname' => $hostname,
|
||||
], Document::SET_TYPE_APPEND);
|
||||
}
|
||||
|
||||
// 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);
|
||||
/**
|
||||
* 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'];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
...$console->getAttribute('platforms', []),
|
||||
...$project->getAttribute('platforms', []),
|
||||
];
|
||||
return \array_unique($clients);
|
||||
}, ['request', 'console', 'project']);
|
||||
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
||||
@@ -222,9 +222,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
|
||||
if ($mode === APP_MODE_ADMIN) {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
} else {
|
||||
if (APP_MODE_ADMIN !== $mode) {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new Document([]);
|
||||
} else {
|
||||
@@ -234,6 +232,8 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -264,11 +264,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
|
||||
$jwtUserId = $payload['userId'] ?? '';
|
||||
if (!empty($jwtUserId)) {
|
||||
if ($mode === APP_MODE_ADMIN) {
|
||||
$user = $dbForPlatform->getDocument('users', $jwtUserId);
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
@@ -954,7 +950,7 @@ App::setResource('httpReferrer', function (Request $request): string {
|
||||
return $referrer;
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $platforms, Database $dbForPlatform, Document $project, App $utopia): string {
|
||||
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $clients, Database $dbForPlatform, Document $project, App $utopia): string {
|
||||
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
|
||||
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
|
||||
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
|
||||
@@ -967,8 +963,8 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef
|
||||
}
|
||||
|
||||
// Safe if added as web platform
|
||||
$originValidator = new Origin($platforms);
|
||||
if ($originValidator->isValid($request->getOrigin($httpReferrer))) {
|
||||
$validator = new Hostname($clients);
|
||||
if ($validator->isValid($origin)) {
|
||||
return $referrer;
|
||||
}
|
||||
|
||||
@@ -996,4 +992,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', 'platforms', 'dbForPlatform', 'project', 'utopia']);
|
||||
}, ['request', 'httpReferrer', 'clients', 'dbForPlatform', 'project', 'utopia']);
|
||||
|
||||
+3
-3
@@ -532,7 +532,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
}
|
||||
|
||||
$timelimit = $app->getResource('timelimit');
|
||||
$platforms = $app->getResource('platforms');
|
||||
$console = $app->getResource('console'); /** @var Document $console */
|
||||
$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($platforms);
|
||||
$originValidator = new Origin(array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
|
||||
if (!empty($origin) && !$originValidator->isValid($origin) && $project->getId() !== 'console') {
|
||||
if (!$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_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
// TODO: remove this later
|
||||
if (System::getEnv('_APP_ENV') === 'development') {
|
||||
$hostname = 'localhost';
|
||||
|
||||
@@ -247,18 +247,10 @@ 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']);
|
||||
|
||||
+2
-2
@@ -52,7 +52,7 @@
|
||||
"utopia-php/cache": "0.13.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "dev-validate-create-index-array as 0.71.10",
|
||||
"utopia-php/database": "dev-fix-cursor as 0.71.8",
|
||||
"utopia-php/detector": "0.1.*",
|
||||
"utopia-php/domains": "0.8.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
@@ -62,7 +62,7 @@
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.18.*",
|
||||
"utopia-php/migration": "0.11.*",
|
||||
"utopia-php/migration": "0.10.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.8.*",
|
||||
|
||||
Generated
+44
-47
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b7f645a2dfd3313b7202189c133ea954",
|
||||
"content-hash": "077d03afbf1b8b84cc437fc29fe6bf16",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -69,16 +69,16 @@
|
||||
},
|
||||
{
|
||||
"name": "appwrite/appwrite",
|
||||
"version": "15.0.0",
|
||||
"version": "11.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-for-php.git",
|
||||
"reference": "deb97b62e0abed8a4fd5c5d48e77365cf89867cf"
|
||||
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/deb97b62e0abed8a4fd5c5d48e77365cf89867cf",
|
||||
"reference": "deb97b62e0abed8a4fd5c5d48e77365cf89867cf",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/1d043f543acdb17b9fdb440b1b2dd208e400bad3",
|
||||
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -104,10 +104,10 @@
|
||||
"support": {
|
||||
"email": "team@appwrite.io",
|
||||
"issues": "https://github.com/appwrite/sdk-for-php/issues",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/15.0.0",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/11.1.0",
|
||||
"url": "https://appwrite.io/support"
|
||||
},
|
||||
"time": "2025-05-18T09:47:10+00:00"
|
||||
"time": "2024-06-26T07:03:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "appwrite/php-clamav",
|
||||
@@ -3493,16 +3493,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "dev-validate-create-index-array",
|
||||
"version": "dev-fix-cursor",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "9e2895b92cb094436973d5f8f30d75014bf03ae6"
|
||||
"reference": "e0e71cd343a1b9066aa006cb7bbffb79aa21fd1a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/9e2895b92cb094436973d5f8f30d75014bf03ae6",
|
||||
"reference": "9e2895b92cb094436973d5f8f30d75014bf03ae6",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/e0e71cd343a1b9066aa006cb7bbffb79aa21fd1a",
|
||||
"reference": "e0e71cd343a1b9066aa006cb7bbffb79aa21fd1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3543,9 +3543,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/validate-create-index-array"
|
||||
"source": "https://github.com/utopia-php/database/tree/fix-cursor"
|
||||
},
|
||||
"time": "2025-07-20T08:28:54+00:00"
|
||||
"time": "2025-07-02T07:50:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
@@ -3993,20 +3993,20 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.11.1",
|
||||
"version": "0.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "d528a454d5c1ed6564b2843a39ff13297bcdb1af"
|
||||
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/d528a454d5c1ed6564b2843a39ff13297bcdb1af",
|
||||
"reference": "d528a454d5c1ed6564b2843a39ff13297bcdb1af",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ea1c585df7ec5f346f061a11581fc9a91679966f",
|
||||
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"appwrite/appwrite": "15.*",
|
||||
"appwrite/appwrite": "11.*",
|
||||
"ext-curl": "*",
|
||||
"ext-openssl": "*",
|
||||
"php": ">=8.1",
|
||||
@@ -4043,9 +4043,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.11.1"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.10.1"
|
||||
},
|
||||
"time": "2025-07-11T13:46:37+00:00"
|
||||
"time": "2025-05-26T15:29:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
@@ -4810,16 +4810,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.41.17",
|
||||
"version": "0.41.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "ef5e9d952032deecffee7f825a941c40ed4c84b8"
|
||||
"reference": "61037c1ed9262308cab49c1d760f3278036ab694"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/ef5e9d952032deecffee7f825a941c40ed4c84b8",
|
||||
"reference": "ef5e9d952032deecffee7f825a941c40ed4c84b8",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/61037c1ed9262308cab49c1d760f3278036ab694",
|
||||
"reference": "61037c1ed9262308cab49c1d760f3278036ab694",
|
||||
"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.17"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.9"
|
||||
},
|
||||
"time": "2025-07-18T08:18:48+00:00"
|
||||
"time": "2025-06-27T10:16:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
@@ -5084,16 +5084,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.24.0",
|
||||
"version": "v1.22.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/pint.git",
|
||||
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a"
|
||||
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a",
|
||||
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a",
|
||||
"url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7",
|
||||
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5104,10 +5104,10 @@
|
||||
"php": "^8.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.82.2",
|
||||
"illuminate/view": "^11.45.1",
|
||||
"larastan/larastan": "^3.5.0",
|
||||
"laravel-zero/framework": "^11.45.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.75.0",
|
||||
"illuminate/view": "^11.44.7",
|
||||
"larastan/larastan": "^3.4.0",
|
||||
"laravel-zero/framework": "^11.36.1",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"nunomaduro/termwind": "^2.3.1",
|
||||
"pestphp/pest": "^2.36.0"
|
||||
@@ -5117,9 +5117,6 @@
|
||||
],
|
||||
"type": "project",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"overrides/Runner/Parallel/ProcessFactory.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Seeders\\": "database/seeders/",
|
||||
@@ -5149,7 +5146,7 @@
|
||||
"issues": "https://github.com/laravel/pint/issues",
|
||||
"source": "https://github.com/laravel/pint"
|
||||
},
|
||||
"time": "2025-07-10T18:09:32+00:00"
|
||||
"time": "2025-05-08T08:38:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "matthiasmullie/minify",
|
||||
@@ -5276,16 +5273,16 @@
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.13.3",
|
||||
"version": "1.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "faed855a7b5f4d4637717c2b3863e277116beb36"
|
||||
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36",
|
||||
"reference": "faed855a7b5f4d4637717c2b3863e277116beb36",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
|
||||
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5324,7 +5321,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.3"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5332,7 +5329,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-05T12:25:42+00:00"
|
||||
"time": "2025-04-29T12:36:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
@@ -8240,9 +8237,9 @@
|
||||
"aliases": [
|
||||
{
|
||||
"package": "utopia-php/database",
|
||||
"version": "dev-validate-create-index-array",
|
||||
"alias": "0.71.10",
|
||||
"alias_normalized": "0.71.10.0"
|
||||
"version": "dev-fix-cursor",
|
||||
"alias": "0.71.8",
|
||||
"alias_normalized": "0.71.8.0"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
|
||||
+2
-5
@@ -116,7 +116,6 @@ 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
|
||||
@@ -214,7 +213,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:6.1.12
|
||||
image: appwrite/console:6.1.2
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -456,7 +455,6 @@ services:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_BROWSER_HOST
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
@@ -484,7 +482,6 @@ 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
|
||||
@@ -978,7 +975,7 @@ services:
|
||||
- OPR_EXECUTOR_ENV=$_APP_ENV
|
||||
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES
|
||||
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
|
||||
- OPR_EXECUTOR_RUNTIME_VERSIONS=v5
|
||||
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v5
|
||||
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
|
||||
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
|
||||
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = 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
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
Document result = await databases.upsertDocument(
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
data: {},
|
||||
permissions: ["read("any")"], // (optional)
|
||||
);
|
||||
@@ -10,5 +10,5 @@ Databases databases = Databases(client);
|
||||
DocumentList result = await databases.upsertDocuments(
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documents: [],
|
||||
documents: [], // (optional)
|
||||
);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using Appwrite;
|
||||
using Appwrite.Models;
|
||||
using Appwrite.Services;
|
||||
|
||||
Client client = new 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
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
Document result = await databases.UpsertDocument(
|
||||
databaseId: "<DATABASE_ID>",
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
documentId: "<DOCUMENT_ID>",
|
||||
data: [object],
|
||||
permissions: ["read("any")"] // optional
|
||||
);
|
||||
@@ -12,5 +12,5 @@ Databases databases = new Databases(client);
|
||||
DocumentList result = await databases.UpsertDocuments(
|
||||
databaseId: "<DATABASE_ID>",
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
documents: new List<object>()
|
||||
documents: new List<object>() // optional
|
||||
);
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
[] // documents (optional)
|
||||
);
|
||||
|
||||
@@ -1,48 +1,5 @@
|
||||
# Change Log
|
||||
|
||||
## 8.2.2
|
||||
|
||||
* Fix object comparison logic when pushing settings
|
||||
* Type generation fixes:
|
||||
* Dart: Fixed import casing to snake_case, removed `extends Document` and hardcoded attributes, removed unnecessary imports
|
||||
* Java: Fixed indentation to 4 spaces, updated imports to `java.util.Objects`, fixed enum casing in strict mode as per [Oracle official docs](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html)
|
||||
* Javascript: Updated optional values formatting from `|null` to `| null`
|
||||
* Kotlin: Fixed indentation to 4 spaces per [Kotlinlang official docs](https://kotlinlang.org/docs/coding-conventions.html#indentation)
|
||||
* PHP: Fixed indentation to 4 spaces per [PHP Fig official docs](https://www.php-fig.org/psr/psr-2/)
|
||||
* Swift: Fixed indentation to 4 spaces, improved `decodeIfPresent` usage for optionals, added missing `public` to `init` method
|
||||
* Typescript: Fixed indentation to 4 spaces per [Typescript coding guidelines](https://github.com/microsoft/TypeScript/wiki/Coding-guidelines)
|
||||
|
||||
## 8.2.1
|
||||
|
||||
* Added `--with-variables` option to the Sites command for adding/updating environment variables
|
||||
* Fixed Functions environment variables not being pushed with `--with-variables`
|
||||
* Removed `awaitPools` when wiping old variables
|
||||
|
||||
> **Note:** Storing environment variables in the `vars` attribute of `appwrite.json` is now deprecated due to security risks. Variables are now synced directly from the `.env` file in the root directory of the function’s or site’s folder.
|
||||
|
||||
## 8.2.0
|
||||
|
||||
* Add `encrypt` attribute support
|
||||
* Add improved warnings on attribute recreation and deletion
|
||||
* Fix `null` parsing error when using create attribute command
|
||||
* Type generation fixes and improvements:
|
||||
* Add `--strict` / `-s` flag to `appwrite types` command to generate types in strict mode. This automatically converts the casing of attributes to match the language's naming conventions
|
||||
* Add automatic package import to `dart` language which uses package detection to import the correct package
|
||||
* Add `Document` class extension to generated types in `dart` and `js` language to support internal attributes like `$id` and `$collectionId` etc.
|
||||
* Add proper enum support to `js` language
|
||||
* Fix indentation in `java`, `kotlin` and `swift` to use 2 spaces instead of 4 for consistency across all languages
|
||||
* Fix doc comments to use correct syntax in various languages (for eg. `///` instead of `/*`)
|
||||
* Update enums in `dart` to use lowerCamelCase in `strict` mode as per [constant_identifier_names](https://dart.dev/tools/diagnostics/constant_identifier_names?utm_source=dartdev&utm_medium=redir&utm_id=diagcode&utm_content=constant_identifier_names)
|
||||
|
||||
## 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,5 @@
|
||||
# Change Log
|
||||
|
||||
## 16.1.0
|
||||
|
||||
* Add `gif` support to `ImageFormat` enum
|
||||
* Add support for `dart38` and `flutter332` runtimes
|
||||
* Add `upsertDocument()` method to Databases service
|
||||
* Fix `convertTo()` method in `Document` and `Preferences` models to correctly accept `Map<String, dynamic>`
|
||||
* Fix `upsertDocuments()` method to have `documents` attribute as required parameter
|
||||
|
||||
## 16.0.0
|
||||
|
||||
* Add `<REGION>` to doc examples due to the new multi region endpoints
|
||||
|
||||
@@ -1,63 +1 @@
|
||||
# Change Log
|
||||
|
||||
## 0.14.0
|
||||
|
||||
* Refactor from Newtonsoft.Json to System.Text.Json for serialization/deserialization
|
||||
* Update package dependencies in `Package.csproj.twig`
|
||||
* Migrate all serialization/deserialization logic in `Client.cs.twig`, `Query.cs.twig`, and `Extensions.cs.twig`
|
||||
* Update model attributes from `[JsonProperty]` to `[JsonPropertyName]` in `Model.cs.twig`
|
||||
* Create new `ObjectToInferredTypesConverter.cs.twig` for proper object type handling
|
||||
* Replace `JsonConverter` with `JsonConverter<object>` in `ValueClassConverter.cs.twig`
|
||||
* Update error handling to use `JsonDocument` instead of `JObject`
|
||||
|
||||
## 0.13.0
|
||||
|
||||
* 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`
|
||||
* Update 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
|
||||
* Remove `search` param from `listExecutions` method
|
||||
|
||||
## 0.12.0
|
||||
|
||||
* fix: remove content-type from GET requests by @loks0n in https://github.com/appwrite/sdk-for-dotnet/pull/59
|
||||
* update: min and max are not optional in methods like `UpdateIntegerAttribute` etc.
|
||||
* chore: regenerate sdk by @ChiragAgg5k in https://github.com/appwrite/sdk-for-dotnet/pull/60
|
||||
* chore: fix build error by @ChiragAgg5k in https://github.com/appwrite/sdk-for-dotnet/pull/61
|
||||
|
||||
## 0.11.0
|
||||
|
||||
* Add new push message parameters by @abnegate in https://github.com/appwrite/sdk-for-dotnet/pull/56
|
||||
|
||||
## 0.10.0
|
||||
|
||||
* fix: chunk upload by @byawitz in https://github.com/appwrite/sdk-for-dotnet/pull/52
|
||||
|
||||
## 0.9.0
|
||||
|
||||
* Support for Appwrite 1.6
|
||||
* Added `key` attribute to `Runtime` response model.
|
||||
* Added `buildSize` attribute to `Deployments` response model.
|
||||
* Added `scheduledAt` attribute to `Executions` response model.
|
||||
* Added `scopes` attribute to `Functions` response model.
|
||||
* Added `specifications` attribute to `Functions` response model.
|
||||
* Added new response model for `Specifications`.
|
||||
* Added new response model for `Builds`.
|
||||
* Added `createJWT()` : Enables creating a JWT using the `userId`.
|
||||
* Added `listSpecifications()`: Enables listing available runtime specifications.
|
||||
* Added `deleteExecution()` : Enables deleting executions.
|
||||
* Added `updateDeploymentBuild()`: Enables cancelling a deployment.
|
||||
* Added `scheduledAt` parameter to `createExecution()`: Enables creating a delayed execution
|
||||
|
||||
#### Breaking changes
|
||||
You can find the new syntax for breaking changes in the [Appwrite API references](https://appwrite.io/docs/references). Select version `1.6.x`.
|
||||
* Removed `otp` parameter from `deleteMFAAuthenticator`.
|
||||
* Added `scopes` parameter for create/update function.
|
||||
* Renamed `templateBranch` to `templateVersion` in `createFunction()`.
|
||||
* Renamed `downloadDeployment()` to `getDeploymentDownload()`
|
||||
|
||||
> **Please note: This version is compatible with Appwrite 1.6 and later only. If you do not update your Appwrite SDK, old SDKs will not break your app. Appwrite APIs are backwards compatible.**
|
||||
# Change Log
|
||||
@@ -1,20 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
## 17.0.2
|
||||
|
||||
* Add `gif` support to `ImageFormat` enum
|
||||
* Fix `convertTo()` method in `Document` and `Preferences` models to correctly accept `Map<String, dynamic>`
|
||||
|
||||
## 17.0.1
|
||||
|
||||
* Fix `devKeys` support by conditionally including credentials during requests
|
||||
|
||||
## 17.0.0
|
||||
## 16.1.1
|
||||
|
||||
* Update `flutter_web_auth_2` dependency to version 4.1.0
|
||||
* Update `auth.html` example in README.md to align with `flutter_web_auth_2` documentation
|
||||
* Breaking changes:
|
||||
* Minimum iOS version supported is now 17.4 due to the updated requirements of `flutter_web_auth_2` version 4.1.0
|
||||
|
||||
## 16.1.0
|
||||
|
||||
|
||||
@@ -1,68 +1,13 @@
|
||||
# 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
|
||||
|
||||
## 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`.
|
||||
* Removes `search` param from `listExecutions` method
|
||||
@@ -1,10 +1,5 @@
|
||||
# Change log
|
||||
|
||||
## 0.10.1
|
||||
|
||||
* Fix URL based methods like `getFileViewURL`, `getFilePreviewURL` etc. by adding the missing `projectId` to searchParams
|
||||
* Add `gif` to ImageFormat enum
|
||||
|
||||
## 0.10.0
|
||||
|
||||
* Add generate file URL methods like`getFilePreviewURL`, `getFileViewURL` etc.
|
||||
|
||||
@@ -267,51 +267,48 @@ class Mapper
|
||||
}
|
||||
|
||||
switch ((!empty($validator)) ? $validator::class : '') {
|
||||
case 'Appwrite\Auth\Validator\Password':
|
||||
case 'Appwrite\Event\Validator\Event':
|
||||
case 'Appwrite\Event\Validator\FunctionEvent':
|
||||
case 'Appwrite\Network\Validator\CNAME':
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
case 'Appwrite\Network\Validator\Redirect':
|
||||
case 'Appwrite\Network\Validator\DNS':
|
||||
case 'Appwrite\Network\Validator\Origin':
|
||||
case 'Appwrite\Task\Validator\Cron':
|
||||
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
||||
case 'Utopia\Database\Validator\Key':
|
||||
case 'Utopia\Database\Validator\UID':
|
||||
case 'Utopia\Validator\Domain':
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
case 'Appwrite\Event\Validator\Event':
|
||||
case 'Appwrite\Event\Validator\FunctionEvent':
|
||||
case 'Utopia\Validator\HexColor':
|
||||
case 'Utopia\Validator\Host':
|
||||
case 'Utopia\Validator\IP':
|
||||
case 'Utopia\Database\Validator\Key':
|
||||
case 'Utopia\Validator\Origin':
|
||||
case 'Appwrite\Auth\Validator\Password':
|
||||
case 'Utopia\Validator\Text':
|
||||
case 'Utopia\Database\Validator\UID':
|
||||
case 'Utopia\Validator\URL':
|
||||
case 'Utopia\Validator\WhiteList':
|
||||
default:
|
||||
$type = Type::string();
|
||||
break;
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
|
||||
case 'Utopia\Database\Validator\Authorization':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Base':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
|
||||
case 'Utopia\Database\Validator\Queries\Documents':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Executions':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Files':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Functions':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Memberships':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Projects':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Rules':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Memberships':
|
||||
case 'Utopia\Database\Validator\Permissions':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Projects':
|
||||
case 'Utopia\Database\Validator\Queries':
|
||||
case 'Utopia\Database\Validator\Roles':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Teams':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Users':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Variables':
|
||||
case 'Utopia\Database\Validator\Authorization':
|
||||
case 'Utopia\Database\Validator\Permissions':
|
||||
case 'Utopia\Database\Validator\Queries':
|
||||
case 'Utopia\Database\Validator\Queries\Documents':
|
||||
case 'Utopia\Database\Validator\Roles':
|
||||
$type = Type::listOf(Type::string());
|
||||
break;
|
||||
case 'Utopia\Validator\Boolean':
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
<?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,91 +2,150 @@
|
||||
|
||||
namespace Appwrite\Network\Validator;
|
||||
|
||||
use Appwrite\Network\Platform;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Hostname;
|
||||
|
||||
class Origin extends Validator
|
||||
{
|
||||
protected array $hostnames = [];
|
||||
protected array $schemes = [];
|
||||
protected ?string $scheme = null;
|
||||
protected ?string $host = null;
|
||||
protected string $origin = '';
|
||||
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';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<\Utopia\Database\Document> $platforms
|
||||
* @var array
|
||||
*/
|
||||
public function __construct(array $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)
|
||||
{
|
||||
$this->hostnames = Platform::getHostnames($platforms);
|
||||
$this->schemes = Platform::getSchemes($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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 is valid.
|
||||
* @param mixed $origin The Origin URI.
|
||||
* Check if Origin has been allowed
|
||||
* for access to the API
|
||||
*
|
||||
* @param mixed $origin
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($origin): bool
|
||||
{
|
||||
$this->origin = $origin;
|
||||
$this->scheme = null;
|
||||
$this->host = null;
|
||||
|
||||
if (!is_string($origin) || empty($origin)) {
|
||||
if (!is_string($origin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->scheme = $this->parseScheme($origin);
|
||||
$this->host = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
|
||||
$scheme = \parse_url($origin, PHP_URL_SCHEME);
|
||||
$host = \parse_url($origin, PHP_URL_HOST);
|
||||
|
||||
$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);
|
||||
}
|
||||
$this->host = $host;
|
||||
$this->client = $scheme;
|
||||
|
||||
if (!empty($this->scheme) && in_array($this->scheme, $this->schemes, true)) {
|
||||
if (empty($host)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
$validator = new Hostname($this->clients);
|
||||
|
||||
/**
|
||||
* Get Description
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
$platform = $this->scheme ? Platform::getNameByScheme($this->scheme) : '';
|
||||
$host = $this->host ? '(' . $this->host . ')' : '';
|
||||
|
||||
if (empty($this->host) && empty($this->scheme)) {
|
||||
return 'Invalid Origin.';
|
||||
}
|
||||
|
||||
if (empty($platform)) {
|
||||
return 'Invalid Scheme. The scheme used (' . $this->scheme . ') in the Origin (' . $this->origin . ') is not supported. If you are using a custom scheme, please change it to `appwrite-callback-<PROJECT_ID>`';
|
||||
}
|
||||
|
||||
return 'Invalid Origin. Register your new client ' . $host . ' as a new '
|
||||
. $platform . ' platform on your project console dashboard';
|
||||
return $validator->isValid($host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
@@ -96,35 +155,13 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
@@ -63,8 +63,8 @@ class XList extends Base
|
||||
$spec['enabled'] = in_array($spec['slug'], $plan['runtimeSpecifications']);
|
||||
}
|
||||
|
||||
$maxCpus = System::getEnv('_APP_COMPUTE_CPUS', 0);
|
||||
$maxMemory = System::getEnv('_APP_COMPUTE_MEMORY', 0);
|
||||
$maxCpus = System::getEnv('_APP_FUNCTIONS_CPUS', 0);
|
||||
$maxMemory = System::getEnv('_APP_FUNCTIONS_MEMORY', 0);
|
||||
|
||||
// Only add specs that are within the limits set by environment variables
|
||||
// Treat 0 as no limit
|
||||
|
||||
@@ -264,7 +264,7 @@ class Builds extends Action
|
||||
->setParam('deploymentId', $deployment->getId());
|
||||
|
||||
if ($deployment->getAttribute('status') === 'canceled') {
|
||||
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,8 @@ class Builds extends Action
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
|
||||
$queueForRealtime
|
||||
@@ -391,7 +392,7 @@ class Builds extends Action
|
||||
Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $deploymentId), '', $stdout, $stderr);
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -524,7 +525,8 @@ class Builds extends Action
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
|
||||
$queueForRealtime
|
||||
@@ -656,7 +658,7 @@ class Builds extends Action
|
||||
$err = null;
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -807,7 +809,7 @@ class Builds extends Action
|
||||
]);
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -856,7 +858,9 @@ class Builds extends Action
|
||||
|
||||
$adapter = $resource->getAttribute('adapter', '');
|
||||
if (empty($adapter)) {
|
||||
$resource = $dbForProject->updateDocument('sites', $resource->getId(), new Document(['adapter' => $detection->getName(), 'fallbackFile' => $detection->getFallbackFile() ?? '']));
|
||||
$resource->setAttribute('adapter', $detection->getName());
|
||||
$resource->setAttribute('fallbackFile', $detection->getFallbackFile() ?? '');
|
||||
$resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource);
|
||||
|
||||
$deployment->setAttribute('adapter', $detection->getName());
|
||||
$deployment->setAttribute('fallbackFile', $detection->getFallbackFile() ?? '');
|
||||
@@ -955,9 +959,8 @@ class Builds extends Action
|
||||
$config['sleep'] = $framework['screenshotSleep'];
|
||||
}
|
||||
|
||||
$browserEndpoint = Config::getParam('_APP_BROWSER_HOST', 'http://appwrite-browser:3000/v1');
|
||||
$fetchResponse = $client->fetch(
|
||||
url: $browserEndpoint . '/screenshots',
|
||||
url: 'http://appwrite-browser:3000/v1/screenshots',
|
||||
method: 'POST',
|
||||
body: $config
|
||||
);
|
||||
@@ -1059,7 +1062,8 @@ class Builds extends Action
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
|
||||
$queueForRealtime
|
||||
@@ -1073,38 +1077,14 @@ class Builds extends Action
|
||||
Console::success("Build id: $deploymentId created");
|
||||
|
||||
/** Set auto deploy */
|
||||
$activateBuild = false;
|
||||
if ($deployment->getAttribute('activate') === true) {
|
||||
// Check if current active deployment started later than this deployment
|
||||
$resource = $dbForProject->getDocument($resource->getCollection(), $resource->getId());
|
||||
$currentActiveDeploymentId = $resource->getAttribute('deploymentId', '');
|
||||
if (!empty($currentActiveDeploymentId)) {
|
||||
$currentActiveDeployment = $dbForProject->getDocument('deployments', $currentActiveDeploymentId);
|
||||
if (!$currentActiveDeployment->isEmpty()) {
|
||||
$currentActiveStartTime = $currentActiveDeployment->getCreatedAt();
|
||||
$deploymentStartTime = $deployment->getCreatedAt();
|
||||
|
||||
// Skip auto-activation if current active deployment started later than deployment that is being activated
|
||||
if ($currentActiveStartTime < $deploymentStartTime) {
|
||||
$activateBuild = true;
|
||||
} else {
|
||||
Console::info('Skipping auto-activation as current deployment is more recent');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$activateBuild = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($activateBuild) {
|
||||
$resource->setAttribute('live', true);
|
||||
switch ($resource->getCollection()) {
|
||||
case 'functions':
|
||||
$resource = $dbForProject->updateDocument('functions', $resource->getId(), new Document([
|
||||
'live' => true,
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
'deploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
]));
|
||||
$resource->setAttribute('deploymentId', $deployment->getId());
|
||||
$resource->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
$resource->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt());
|
||||
$resource = $dbForProject->updateDocument('functions', $resource->getId(), $resource);
|
||||
|
||||
$queries = [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
@@ -1118,21 +1098,19 @@ class Builds extends Action
|
||||
$rulesUpdated = false;
|
||||
$dbForPlatform->forEach('rules', function (Document $rule) use ($dbForPlatform, $deployment, &$rulesUpdated) {
|
||||
$rulesUpdated = true;
|
||||
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
]));
|
||||
$rule = $rule
|
||||
->setAttribute('deploymentId', $deployment->getId())
|
||||
->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
|
||||
}, $queries);
|
||||
break;
|
||||
case 'sites':
|
||||
$resource = $dbForProject->updateDocument('sites', $resource->getId(), new Document([
|
||||
'live' => true,
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
'deploymentScreenshotDark' => $deployment->getAttribute('screenshotDark', ''),
|
||||
'deploymentScreenshotLight' => $deployment->getAttribute('screenshotLight', ''),
|
||||
'deploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
]));
|
||||
$resource->setAttribute('deploymentId', $deployment->getId());
|
||||
$resource->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
$resource->setAttribute('deploymentScreenshotDark', $deployment->getAttribute('screenshotDark', ''));
|
||||
$resource->setAttribute('deploymentScreenshotLight', $deployment->getAttribute('screenshotLight', ''));
|
||||
$resource->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt());
|
||||
$resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource);
|
||||
$queries = [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::equal('type', ['deployment']),
|
||||
@@ -1143,10 +1121,10 @@ class Builds extends Action
|
||||
];
|
||||
|
||||
$dbForPlatform->forEach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
|
||||
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
]));
|
||||
$rule = $rule
|
||||
->setAttribute('deploymentId', $deployment->getId())
|
||||
->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
|
||||
}, $queries);
|
||||
|
||||
break;
|
||||
@@ -1188,10 +1166,11 @@ class Builds extends Action
|
||||
'region' => $project->getAttribute('region')
|
||||
]));
|
||||
} catch (Duplicate $err) {
|
||||
$rule = $dbForPlatform->updateDocument('rules', $ruleId, new Document([
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
]));
|
||||
$rule = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
$rule = $rule
|
||||
->setAttribute('deploymentId', $deployment->getId())
|
||||
->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
|
||||
}
|
||||
|
||||
$queries = [
|
||||
@@ -1204,10 +1183,10 @@ class Builds extends Action
|
||||
];
|
||||
|
||||
$dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
|
||||
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
]));
|
||||
$rule = $rule
|
||||
->setAttribute('deploymentId', $deployment->getId())
|
||||
->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
|
||||
}, $queries);
|
||||
}
|
||||
}
|
||||
@@ -1222,7 +1201,7 @@ class Builds extends Action
|
||||
->trigger();
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1245,7 +1224,7 @@ class Builds extends Action
|
||||
Console::error($th->getTraceAsString());
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1277,7 +1256,8 @@ class Builds extends Action
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
|
||||
$queueForRealtime
|
||||
@@ -1303,8 +1283,6 @@ class Builds extends Action
|
||||
|
||||
protected function sendUsage(Document $resource, Document $deployment, Document $project, StatsUsage $queue): void
|
||||
{
|
||||
$spec = Config::getParam('specifications')[$resource->getAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
|
||||
|
||||
switch ($deployment->getAttribute('status')) {
|
||||
case 'ready':
|
||||
$queue
|
||||
@@ -1469,7 +1447,7 @@ class Builds extends Action
|
||||
$name = "{$resourceName} ({$projectName})";
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
$projectId = $project->getId();
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
@@ -1533,22 +1511,4 @@ class Builds extends Action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function cancelDeployment(string $deploymentId, Database $dbForProject, Realtime $queueForRealtime)
|
||||
{
|
||||
Console::info('Build has been canceled');
|
||||
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
$logs = $deployment->getAttribute('buildLogs', '');
|
||||
$date = \date('H:i:s');
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][33m Build has been canceled. [0m\n";
|
||||
|
||||
$deployment->setAttribute('buildLogs', $logs);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ class XList extends Base
|
||||
$spec['enabled'] = in_array($spec['slug'], $plan['runtimeSpecifications']);
|
||||
}
|
||||
|
||||
$maxCpus = System::getEnv('_APP_COMPUTE_CPUS', 0);
|
||||
$maxMemory = System::getEnv('_APP_COMPUTE_MEMORY', 0);
|
||||
$maxCpus = System::getEnv('_APP_FUNCTIONS_CPUS', 0);
|
||||
$maxMemory = System::getEnv('_APP_FUNCTIONS_MEMORY', 0);
|
||||
|
||||
// Only add specs that are within the limits set by environment variables
|
||||
// Treat 0 as no limit
|
||||
|
||||
@@ -37,7 +37,6 @@ class Action extends UtopiaAction
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
return [
|
||||
'bucket' => $bucket,
|
||||
'file' => $file,
|
||||
|
||||
@@ -272,7 +272,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
$gitUrl = 'git@github.com:aw-tests/' . $language['gitRepoName'] . '.git';
|
||||
}
|
||||
|
||||
$repoBranch = $language['repoBranch'] ?? 'main';
|
||||
if ($git && !empty($gitUrl)) {
|
||||
\exec('rm -rf ' . $target . ' && \
|
||||
mkdir -p ' . $target . ' && \
|
||||
@@ -280,8 +279,8 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
git init && \
|
||||
git remote add origin ' . $gitUrl . ' && \
|
||||
git fetch origin && \
|
||||
git checkout ' . $repoBranch . ' || git checkout -b ' . $repoBranch . ' && \
|
||||
git pull origin ' . $repoBranch . ' && \
|
||||
git checkout main || git checkout -b main && \
|
||||
git pull origin main && \
|
||||
git checkout ' . $gitBranch . ' || git checkout -b ' . $gitBranch . ' && \
|
||||
git fetch origin ' . $gitBranch . ' || git push -u origin ' . $gitBranch . ' && \
|
||||
git pull origin ' . $gitBranch . ' && \
|
||||
|
||||
@@ -26,7 +26,6 @@ abstract class ScheduleBase extends Action
|
||||
protected array $schedules = [];
|
||||
|
||||
protected BrokerPool $publisher;
|
||||
protected ?BrokerPool $publisherRedis = null;
|
||||
|
||||
private ?Histogram $collectSchedulesTelemetryDuration = null;
|
||||
private ?Gauge $collectSchedulesTelemetryCount = null;
|
||||
@@ -73,13 +72,6 @@ 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,7 +6,6 @@ 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
|
||||
{
|
||||
@@ -30,16 +29,9 @@ 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,7 +8,6 @@ use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\System\System;
|
||||
|
||||
class ScheduleFunctions extends ScheduleBase
|
||||
{
|
||||
@@ -91,13 +90,7 @@ class ScheduleFunctions extends ScheduleBase
|
||||
|
||||
$this->updateProjectAccess($schedule['project'], $dbForPlatform);
|
||||
|
||||
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'functions');
|
||||
|
||||
$queueForFunctions = new Func(
|
||||
$isRedisFallback
|
||||
? $this->publisherRedis
|
||||
: $this->publisher
|
||||
);
|
||||
$queueForFunctions = new Func($this->publisher);
|
||||
|
||||
$queueForFunctions
|
||||
->setType('schedule')
|
||||
|
||||
@@ -1117,9 +1117,6 @@ class Deletes extends Action
|
||||
): void {
|
||||
$start = \microtime(true);
|
||||
|
||||
$deleteBatchSize = Database::DELETE_BATCH_SIZE;
|
||||
$deleteBatchSize = 500; // TODO: Set right value in DB library after investigation
|
||||
|
||||
/**
|
||||
* deleteDocuments uses a cursor, we need to add a unique order by field or use default
|
||||
*/
|
||||
@@ -1127,7 +1124,7 @@ class Deletes extends Action
|
||||
$count = $database->deleteDocuments(
|
||||
$collection,
|
||||
$queries,
|
||||
$deleteBatchSize,
|
||||
Database::DELETE_BATCH_SIZE,
|
||||
$callback
|
||||
);
|
||||
} catch (Throwable $th) {
|
||||
|
||||
@@ -314,7 +314,6 @@ class Migrations extends Action
|
||||
$migration->getAttribute('resourceType')
|
||||
);
|
||||
}
|
||||
|
||||
$destination->shutDown();
|
||||
$source->shutDown();
|
||||
|
||||
|
||||
@@ -360,7 +360,6 @@ class OpenAPI3 extends Format
|
||||
break;
|
||||
case 'Utopia\Validator\Host':
|
||||
case 'Utopia\Validator\URL':
|
||||
case 'Appwrite\Network\Validator\Redirect':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['format'] = 'url';
|
||||
$node['schema']['x-example'] = 'https://example.com';
|
||||
|
||||
@@ -385,7 +385,6 @@ class Swagger2 extends Format
|
||||
break;
|
||||
case 'Utopia\Validator\Host':
|
||||
case 'Utopia\Validator\URL':
|
||||
case 'Appwrite\Network\Validator\Redirect':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['format'] = 'url';
|
||||
$node['x-example'] = 'https://example.com';
|
||||
|
||||
@@ -26,20 +26,7 @@ class URL
|
||||
'fragment' => '',
|
||||
];
|
||||
|
||||
$parsed = \parse_url($url);
|
||||
if (is_array($parsed)) {
|
||||
return \array_merge($default, $parsed);
|
||||
}
|
||||
|
||||
// see if $url is just a scheme
|
||||
if (preg_match('/^([a-z][a-z0-9+.-]*):/i', $url, $matches)) {
|
||||
$scheme = $matches[1];
|
||||
return \array_merge($default, [
|
||||
'scheme' => $scheme
|
||||
]);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid URL: ' . $url);
|
||||
return \array_merge($default, \parse_url($url));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,9 +55,9 @@ class URL
|
||||
|
||||
$parts['user'] = isset($url['user']) ? $url['user'] : '';
|
||||
|
||||
$parts['pass'] = !empty($url['pass']) ? ':' . $url['pass'] : '';
|
||||
$parts['pass'] = isset($url['pass']) ? ':' . $url['pass'] : '';
|
||||
|
||||
$parts['pass'] = ($parts['user'] || !empty($parts['pass'])) ? $parts['pass'] . '@' : '';
|
||||
$parts['pass'] = ($parts['user'] || $parts['pass']) ? $parts['pass'] . '@' : '';
|
||||
|
||||
$parts['path'] = isset($url['path']) ? $url['path'] : '';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -675,8 +675,7 @@ class UsageTest extends Scope
|
||||
[
|
||||
'functionId' => 'unique()',
|
||||
'name' => 'Test',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
'funcKey2' => 'funcValue2',
|
||||
@@ -698,7 +697,8 @@ class UsageTest extends Scope
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true,
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
@@ -1343,12 +1343,8 @@ trait DatabasesBase
|
||||
'type' => 'key',
|
||||
'attributes' => ['actors'],
|
||||
]);
|
||||
/**
|
||||
* Disabling array index due to mysql bug
|
||||
* @link https://github.com/appwrite/appwrite/pull/10169
|
||||
*/
|
||||
$this->assertEquals(400, $actorsArray['headers']['status-code']);
|
||||
$this->assertEquals('Indexing an array attribute is temporarily disabled', $actorsArray['body']['message']);
|
||||
|
||||
$this->assertEquals(202, $actorsArray['headers']['status-code']);
|
||||
|
||||
$twoLevelsArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
@@ -1361,10 +1357,9 @@ trait DatabasesBase
|
||||
'orders' => ['DESC', 'DESC'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $twoLevelsArray['headers']['status-code']);
|
||||
$this->assertEquals('Indexing an array attribute is temporarily disabled', $twoLevelsArray['body']['message']);
|
||||
//$this->assertEquals('DESC', $twoLevelsArray['body']['orders'][0]);
|
||||
//$this->assertEquals(null, $twoLevelsArray['body']['orders'][1]); // Overwrite by API (array)
|
||||
$this->assertEquals(202, $twoLevelsArray['headers']['status-code']);
|
||||
$this->assertEquals('DESC', $twoLevelsArray['body']['orders'][0]);
|
||||
$this->assertEquals(null, $twoLevelsArray['body']['orders'][1]); // Overwrite by API (array)
|
||||
|
||||
$unknown = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
@@ -1389,8 +1384,7 @@ trait DatabasesBase
|
||||
'attributes' => ['integers'], // array attribute
|
||||
'orders' => ['DESC'], // Check order is removed in API
|
||||
]);
|
||||
$this->assertEquals(400, $index1['headers']['status-code']);
|
||||
$this->assertEquals('Indexing an array attribute is temporarily disabled', $index1['body']['message']);
|
||||
$this->assertEquals(202, $index1['headers']['status-code']);
|
||||
|
||||
$index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
@@ -1401,8 +1395,7 @@ trait DatabasesBase
|
||||
'type' => 'key',
|
||||
'attributes' => ['integers'], // array attribute
|
||||
]);
|
||||
$this->assertEquals(400, $index2['headers']['status-code']);
|
||||
$this->assertEquals('Indexing an array attribute is temporarily disabled', $index2['body']['message']);
|
||||
$this->assertEquals(202, $index2['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Create Indexes by worker
|
||||
@@ -1416,7 +1409,7 @@ trait DatabasesBase
|
||||
]), []);
|
||||
|
||||
$this->assertIsArray($movies['body']['indexes']);
|
||||
$this->assertCount(4, $movies['body']['indexes']);
|
||||
$this->assertCount(8, $movies['body']['indexes']);
|
||||
$this->assertEquals($titleIndex['body']['key'], $movies['body']['indexes'][0]['key']);
|
||||
$this->assertEquals($releaseYearIndex['body']['key'], $movies['body']['indexes'][1]['key']);
|
||||
$this->assertEquals($releaseWithDate1['body']['key'], $movies['body']['indexes'][2]['key']);
|
||||
@@ -1483,15 +1476,14 @@ trait DatabasesBase
|
||||
'attributes' => ['actors'],
|
||||
'lengths' => [120]
|
||||
]);
|
||||
$this->assertEquals(400, $create['headers']['status-code']);
|
||||
$this->assertEquals('Indexing an array attribute is temporarily disabled', $create['body']['message']);
|
||||
$this->assertEquals(202, $create['headers']['status-code']);
|
||||
|
||||
// $index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/indexes/lengthOverrideTestIndex", [
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// 'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
// ]);
|
||||
// $this->assertEquals([Database::ARRAY_INDEX_LENGTH], $index['body']['lengths']);
|
||||
$index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/indexes/lengthOverrideTestIndex", [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]);
|
||||
$this->assertEquals([Database::ARRAY_INDEX_LENGTH], $index['body']['lengths']);
|
||||
|
||||
// Test case for count of lengths greater than attributes (should throw 400)
|
||||
$create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [
|
||||
@@ -2004,36 +1996,6 @@ trait DatabasesBase
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(2, $documents['body']['total']);
|
||||
|
||||
// test without passing permissions
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2000
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
|
||||
$document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
|
||||
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(204, $deleteResponse['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,8 +21,8 @@ class FunctionsConsoleClientTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::user($this->getUser()['$id'])->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'users.*.create',
|
||||
'users.*.delete',
|
||||
@@ -39,8 +39,8 @@ class FunctionsConsoleClientTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Failure',
|
||||
'execute' => ['some-random-string'],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $function2['headers']['status-code']);
|
||||
@@ -453,7 +453,7 @@ class FunctionsConsoleClientTest extends Scope
|
||||
{
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Variable E2E Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'logging' => false,
|
||||
@@ -481,7 +481,7 @@ class FunctionsConsoleClientTest extends Scope
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'entrypoint' => 'index.js',
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -502,7 +502,7 @@ class FunctionsConsoleClientTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Download Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'logging' => false,
|
||||
@@ -511,7 +511,7 @@ class FunctionsConsoleClientTest extends Scope
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'entrypoint' => 'index.js',
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ class FunctionsCustomClientTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::user($this->getUser()['$id'])->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'users.*.create',
|
||||
'users.*.delete',
|
||||
@@ -52,7 +52,8 @@ class FunctionsCustomClientTest extends Scope
|
||||
'timeout' => 10,
|
||||
]);
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -83,12 +84,13 @@ class FunctionsCustomClientTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-fn'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -105,8 +107,8 @@ class FunctionsCustomClientTest extends Scope
|
||||
$this->assertEquals('Test', $output['APPWRITE_FUNCTION_NAME']);
|
||||
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
|
||||
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
|
||||
$this->assertEquals('Node.js', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
|
||||
$this->assertEquals('22', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']);
|
||||
$this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
|
||||
$this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']);
|
||||
$this->assertEquals(APP_VERSION_STABLE, $output['APPWRITE_VERSION']);
|
||||
$this->assertEquals(System::getEnv('_APP_REGION', 'default'), $output['APPWRITE_REGION']);
|
||||
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
|
||||
@@ -140,10 +142,10 @@ class FunctionsCustomClientTest extends Scope
|
||||
*/
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test guest execution',
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
'funcKey2' => 'funcValue2',
|
||||
@@ -152,7 +154,8 @@ class FunctionsCustomClientTest extends Scope
|
||||
'timeout' => 10,
|
||||
]);
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-fn'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -172,8 +175,8 @@ class FunctionsCustomClientTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
@@ -190,14 +193,15 @@ class FunctionsCustomClientTest extends Scope
|
||||
*/
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test synchronous execution',
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-fn'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -210,11 +214,11 @@ class FunctionsCustomClientTest extends Scope
|
||||
$this->assertEquals('completed', $execution['body']['status']);
|
||||
$this->assertEquals(200, $execution['body']['responseStatusCode']);
|
||||
$this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']);
|
||||
$this->assertEquals('Test synchronous execution', $output['APPWRITE_FUNCTION_NAME']);
|
||||
$this->assertEquals('Test', $output['APPWRITE_FUNCTION_NAME']);
|
||||
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
|
||||
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
|
||||
$this->assertEquals('Node.js', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
|
||||
$this->assertEquals('22', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']);
|
||||
$this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
|
||||
$this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']);
|
||||
$this->assertEquals(APP_VERSION_STABLE, $output['APPWRITE_VERSION']);
|
||||
$this->assertEquals(System::getEnv('_APP_REGION', 'default'), $output['APPWRITE_REGION']);
|
||||
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
|
||||
@@ -235,12 +239,12 @@ class FunctionsCustomClientTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'entrypoint' => 'index.js'
|
||||
]);
|
||||
$this->setupDeployment($functionId, [
|
||||
'entrypoint' => 'index.js',
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -336,7 +340,7 @@ class FunctionsCustomClientTest extends Scope
|
||||
'limit' => 5,
|
||||
'offset' => 2,
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => ['node-22']
|
||||
'runtimes' => ['node-16.0']
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $templates['headers']['status-code']);
|
||||
@@ -348,7 +352,7 @@ class FunctionsCustomClientTest extends Scope
|
||||
$this->assertContains($template['useCases'][0], ['databases']);
|
||||
}
|
||||
|
||||
$this->assertContains('node-22', array_column($templates['body']['templates'][0]['runtimes'], 'name'));
|
||||
$this->assertContains('node-16.0', array_column($templates['body']['templates'][0]['runtimes'], 'name'));
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
||||
@@ -35,7 +35,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Specs function',
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'php-8.0',
|
||||
'specification' => $specifications['body']['specifications'][0]['slug']
|
||||
]);
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
@@ -50,7 +50,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Specs function',
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'php-8.0',
|
||||
'specification' => 'cheap-please'
|
||||
]);
|
||||
$this->assertEquals(400, $function['headers']['status-code']);
|
||||
@@ -64,8 +64,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'buckets.*.create',
|
||||
'buckets.*.delete',
|
||||
@@ -79,7 +79,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
$this->assertEquals('Test', $function['body']['name']);
|
||||
$this->assertEquals('node-22', $function['body']['runtime']);
|
||||
$this->assertEquals('php-8.0', $function['body']['runtime']);
|
||||
$this->assertEquals(true, $dateValidator->isValid($function['body']['$createdAt']));
|
||||
$this->assertEquals(true, $dateValidator->isValid($function['body']['$updatedAt']));
|
||||
$this->assertEquals('', $function['body']['deploymentId']);
|
||||
@@ -180,7 +180,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
|
||||
// Test search runtime
|
||||
$functions = $this->listFunctions([
|
||||
'search' => 'node-22'
|
||||
'search' => 'php-8.0'
|
||||
]);
|
||||
|
||||
$this->assertEquals($functions['headers']['status-code'], 200);
|
||||
@@ -193,8 +193,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test 2',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'buckets.*.create',
|
||||
'buckets.*.delete',
|
||||
@@ -286,8 +286,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
],
|
||||
'schedule' => '0 0 1 1 *',
|
||||
'timeout' => 15,
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
]);
|
||||
|
||||
$dateValidator = new DatetimeValidator();
|
||||
@@ -322,8 +322,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::user($this->getUser()['$id'])->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'users.*.create',
|
||||
'users.*.delete',
|
||||
@@ -338,7 +338,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
'x-sdk-language' => 'cli',
|
||||
], [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -362,7 +363,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
|
||||
|
||||
$phpRuntime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
return $runtime['name'] === 'node-22';
|
||||
return $runtime['name'] === 'php-8.0';
|
||||
}))[0];
|
||||
|
||||
// If this fails, the template has variables, and this test needs to be updated
|
||||
@@ -372,7 +373,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
[
|
||||
'functionId' => ID::unique(),
|
||||
'name' => $starterTemplate['body']['name'],
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'php-8.0',
|
||||
'execute' => $starterTemplate['body']['permissions'],
|
||||
'entrypoint' => $phpRuntime['entrypoint'],
|
||||
'events' => $starterTemplate['body']['events'],
|
||||
@@ -513,7 +514,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$functionId = $data['functionId'];
|
||||
|
||||
$deployment = $this->createDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -521,7 +522,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
$this->assertEquals('waiting', $deployment['body']['status']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt']));
|
||||
$this->assertEquals('index.js', $deployment['body']['entrypoint']);
|
||||
$this->assertEquals('index.php', $deployment['body']['entrypoint']);
|
||||
|
||||
$deploymentIdActive = $deployment['body']['$id'] ?? '';
|
||||
|
||||
@@ -532,7 +533,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
}, 50000, 500);
|
||||
|
||||
$deployment = $this->createDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => 'false'
|
||||
]);
|
||||
|
||||
@@ -572,7 +573,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$functionId = $data['functionId'];
|
||||
|
||||
$deployment = $this->createDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => 'false'
|
||||
]);
|
||||
|
||||
@@ -581,7 +582,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt']));
|
||||
$this->assertEquals('index.js', $deployment['body']['entrypoint']);
|
||||
$this->assertEquals('index.php', $deployment['body']['entrypoint']);
|
||||
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
@@ -594,12 +595,12 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals('canceled', $deployment['body']['status']);
|
||||
|
||||
// Ensures worker got eventually aware of cancellation and reacted properly
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertStringContainsString('Build has been canceled.', $deployment['body']['buildLogs']);
|
||||
});
|
||||
/**
|
||||
* Build worker still runs the build.
|
||||
* 30s sleep gives worker enough time to finish build.
|
||||
* After build finished, it should still be canceled, not ready.
|
||||
*/
|
||||
\sleep(30);
|
||||
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
|
||||
@@ -617,7 +618,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
*/
|
||||
$functionId = $data['functionId'];
|
||||
|
||||
$folder = 'large';
|
||||
$folder = 'php-large';
|
||||
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
|
||||
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
|
||||
|
||||
@@ -632,13 +633,13 @@ class FunctionsCustomServerTest extends Scope
|
||||
];
|
||||
$id = '';
|
||||
while (!feof($handle)) {
|
||||
$curlFile = new \CURLFile('data://' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'large-fx.tar.gz');
|
||||
$curlFile = new \CURLFile('data://' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'php-large-fx.tar.gz');
|
||||
$headers['content-range'] = 'bytes ' . ($counter * $chunkSize) . '-' . min(((($counter * $chunkSize) + $chunkSize) - 1), $size - 1) . '/' . $size;
|
||||
if (!empty($id)) {
|
||||
$headers['x-appwrite-id'] = $id;
|
||||
}
|
||||
$largeTag = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge($headers, $this->getHeaders()), [
|
||||
'entrypoint' => 'index.js',
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $curlFile,
|
||||
'activate' => true,
|
||||
'commands' => 'cp blue.mp4 copy.mp4 && ls -al' // +7MB buildSize
|
||||
@@ -651,7 +652,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(202, $largeTag['headers']['status-code']);
|
||||
$this->assertNotEmpty($largeTag['body']['$id']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($largeTag['body']['$createdAt']));
|
||||
$this->assertEquals('index.js', $largeTag['body']['entrypoint']);
|
||||
$this->assertEquals('index.php', $largeTag['body']['entrypoint']);
|
||||
$this->assertGreaterThan(1024 * 1024 * 5, $largeTag['body']['sourceSize']); // ~7MB video file
|
||||
$this->assertLessThan(1024 * 1024 * 10, $largeTag['body']['sourceSize']); // ~7MB video file
|
||||
|
||||
@@ -902,8 +903,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertStringContainsString($data['deploymentId'], $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('Test1', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('http', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('Node.js', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('22', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('PHP', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('8.0', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('Global Variable Value', $execution['body']['responseBody']);
|
||||
// $this->assertStringContainsString('êä', $execution['body']['responseBody']); // tests unknown utf-8 chars
|
||||
$this->assertNotEmpty($execution['body']['errors']);
|
||||
@@ -1016,8 +1017,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(200, $execution['body']['responseStatusCode']);
|
||||
$this->assertStringContainsString('Test1', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('http', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('Node.js', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('22', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('PHP', $execution['body']['responseBody']);
|
||||
$this->assertStringContainsString('8.0', $execution['body']['responseBody']);
|
||||
// $this->assertStringContainsString('êä', $execution['body']['response']); // tests unknown utf-8 chars
|
||||
$this->assertLessThan(1.500, $execution['body']['duration']);
|
||||
|
||||
@@ -1119,8 +1120,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
'users.*.update.email',
|
||||
],
|
||||
'timeout' => 15,
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'specification' => Specification::S_1VCPU_1GB,
|
||||
]);
|
||||
|
||||
@@ -1147,8 +1148,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
'users.*.update.email',
|
||||
],
|
||||
'timeout' => 15,
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'specification' => Specification::S_1VCPU_512MB,
|
||||
]);
|
||||
|
||||
@@ -1177,8 +1178,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
'users.*.update.email',
|
||||
],
|
||||
'timeout' => 15,
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'specification' => 's-2vcpu-512mb', // Invalid specification
|
||||
]);
|
||||
|
||||
@@ -1235,9 +1236,9 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test timeout execution',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test php-8.0',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [],
|
||||
'schedule' => '',
|
||||
'timeout' => 5, // Should timeout after 5 seconds
|
||||
@@ -1279,12 +1280,12 @@ class FunctionsCustomServerTest extends Scope
|
||||
*/
|
||||
public function provideCustomExecutions(): array
|
||||
{
|
||||
// Most disabled to keep tests fast
|
||||
return [
|
||||
// ['folder' => 'php-fn', 'name' => 'php-8.0', 'entrypoint' => 'index.php', 'runtimeName' => 'PHP', 'runtimeVersion' => '8.0'],
|
||||
['folder' => 'node', 'name' => 'node-22', 'entrypoint' => 'index.js', 'runtimeName' => 'Node.js', 'runtimeVersion' => '22'],
|
||||
// ['folder' => 'python', 'name' => 'python-3.9', 'entrypoint' => 'main.py', 'runtimeName' => 'Python', 'runtimeVersion' => '3.9'],
|
||||
// ['folder' => 'ruby', 'name' => 'ruby-3.1', 'entrypoint' => 'main.rb', 'runtimeName' => 'Ruby', 'runtimeVersion' => '3.1'],
|
||||
['folder' => 'php-fn', 'name' => 'php-8.0', 'entrypoint' => 'index.php', 'runtimeName' => 'PHP', 'runtimeVersion' => '8.0'],
|
||||
['folder' => 'node', 'name' => 'node-18.0', 'entrypoint' => 'index.js', 'runtimeName' => 'Node.js', 'runtimeVersion' => '18.0'],
|
||||
['folder' => 'python', 'name' => 'python-3.9', 'entrypoint' => 'main.py', 'runtimeName' => 'Python', 'runtimeVersion' => '3.9'],
|
||||
['folder' => 'ruby', 'name' => 'ruby-3.1', 'entrypoint' => 'main.rb', 'runtimeName' => 'Ruby', 'runtimeVersion' => '3.1'],
|
||||
// Swift and Dart disabled on purpose, as it's very slow.
|
||||
// [ 'folder' => 'dart', 'name' => 'dart-2.15', 'entrypoint' => 'main.dart', 'runtimeName' => 'Dart', 'runtimeVersion' => '2.15' ],
|
||||
// [ 'folder' => 'swift', 'name' => 'swift-5.5', 'entrypoint' => 'index.swift', 'runtimeName' => 'Swift', 'runtimeVersion' => '5.5' ],
|
||||
];
|
||||
@@ -1323,14 +1324,27 @@ class FunctionsCustomServerTest extends Scope
|
||||
]);
|
||||
|
||||
$execution = $this->createExecution($functionId, [
|
||||
'body' => 'foobar',
|
||||
'async' => 'false'
|
||||
]);
|
||||
|
||||
$output = json_decode($execution['body']['responseBody'], true);
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
$this->assertEquals('completed', $execution['body']['status']);
|
||||
$this->assertEquals(200, $execution['body']['responseStatusCode']);
|
||||
$this->assertEquals('OK', $execution['body']['responseBody']);
|
||||
$this->assertEmpty($execution['body']['logs']);
|
||||
$this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']);
|
||||
$this->assertEquals('Test ' . $name, $output['APPWRITE_FUNCTION_NAME']);
|
||||
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
|
||||
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
|
||||
$this->assertEquals($runtimeName, $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
|
||||
$this->assertEquals($runtimeVersion, $output['APPWRITE_FUNCTION_RUNTIME_VERSION']);
|
||||
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
|
||||
$this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']);
|
||||
$this->assertEquals('variable', $output['CUSTOM_VARIABLE']);
|
||||
$this->assertEmpty($output['APPWRITE_FUNCTION_USER_ID']);
|
||||
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
|
||||
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
|
||||
$this->assertStringContainsString('Amazing Function Log', $execution['body']['logs']);
|
||||
$this->assertEmpty($execution['body']['errors']);
|
||||
|
||||
$executionId = $execution['body']['$id'] ?? '';
|
||||
@@ -1343,8 +1357,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertCount(1, $executions['body']['executions']);
|
||||
$this->assertEquals($executions['body']['executions'][0]['$id'], $executionId);
|
||||
$this->assertEquals($executions['body']['executions'][0]['trigger'], 'http');
|
||||
$this->assertEquals(200, $executions['body']['executions'][0]['responseStatusCode']);
|
||||
$this->assertEmpty($executions['body']['executions'][0]['responseBody']);
|
||||
$this->assertStringContainsString('Amazing Function Log', $executions['body']['executions'][0]['logs']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
@@ -1353,14 +1366,15 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Binary executions',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Binary executions',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('binary-response'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-binary-response'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1401,14 +1415,15 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Binary executions',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Binary executions',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('binary-request'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-binary-request'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1446,6 +1461,49 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testv2Function()
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test PHP V2',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [],
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
$variable = $this->client->call(Client::METHOD_PATCH, '/mock/functions-v2', [
|
||||
'content-type' => 'application/json',
|
||||
'origin' => 'http://localhost',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], [
|
||||
'functionId' => $functionId
|
||||
]);
|
||||
$this->assertEquals(204, $variable['headers']['status-code']);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-v2'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$execution = $this->createExecution($functionId, [
|
||||
'body' => 'foobar',
|
||||
'async' => 'false'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
$this->assertEquals('completed', $execution['body']['status']);
|
||||
$this->assertEquals(200, $execution['body']['responseStatusCode']);
|
||||
|
||||
$output = json_decode($execution['body']['responseBody'], true);
|
||||
$this->assertEquals(true, $output['v2Woks']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testGetRuntimes()
|
||||
{
|
||||
$runtimes = $this->client->call(Client::METHOD_GET, '/functions/runtimes', array_merge([
|
||||
@@ -1473,16 +1531,17 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Event executions',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Event executions',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'users.*.create',
|
||||
],
|
||||
'timeout' => 15,
|
||||
]);
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('event-handler'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-event'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1525,16 +1584,17 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Scopes executions',
|
||||
'commands' => 'bash setup.sh && npm install',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Scopes executions',
|
||||
'commands' => 'bash setup.sh && composer install',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'scopes' => ['users.read'],
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('dynamic-api-key'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-scopes'),
|
||||
'activate' => true,
|
||||
]);
|
||||
|
||||
@@ -1583,15 +1643,16 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Cookie executions',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Cookie executions',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
]);
|
||||
$this->assertNotEmpty($functionId);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('cookies'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-cookie'),
|
||||
'activate' => true
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
@@ -1624,9 +1685,9 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Cookie executions',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Cookie executions',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
@@ -1634,7 +1695,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$domain = $this->setupFunctionDomain($functionId);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('cookies'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-cookie'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1681,9 +1743,9 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Binary executions',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Binary executions',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
@@ -1691,7 +1753,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$domain = $this->setupFunctionDomain($functionId);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('binary-response'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-binary-response'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1715,9 +1778,9 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Binary executions',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'name' => 'Test PHP Binary executions',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
@@ -1725,7 +1788,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$domain = $this->setupFunctionDomain($functionId);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('binary-request'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php-binary-request'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1752,8 +1816,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
@@ -1787,8 +1851,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$function1Id = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
@@ -1796,8 +1860,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$function2Id = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test2',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
@@ -1828,7 +1892,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Logging Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'logging' => false,
|
||||
@@ -1844,7 +1908,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$domain = $this->setupFunctionDomain($functionId);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1921,7 +1985,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
// Check if the function specifications are correctly set in builds
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Specification Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'logging' => false,
|
||||
@@ -1937,7 +2001,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$functionId = $functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1963,7 +2027,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Duplicate Deployment Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'commands' => ''
|
||||
@@ -1971,7 +2035,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertNotEmpty($functionId);
|
||||
|
||||
$deploymentId1 = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId1);
|
||||
@@ -1981,7 +2045,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertStringContainsString('APPWRITE_FUNCTION_ID', $execution['body']['responseBody']);
|
||||
|
||||
$function = $this->updateFunction($functionId, [
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Duplicate Deployment Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'commands' => 'rm index.js && mv maintenance.js index.js'
|
||||
@@ -2025,12 +2089,13 @@ class FunctionsCustomServerTest extends Scope
|
||||
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'php-8.0',
|
||||
'name' => 'Re-activate Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'entrypoint' => 'index.php',
|
||||
]);
|
||||
$this->assertNotEmpty($functionId);
|
||||
|
||||
|
||||
$function = $this->getFunction($functionId);
|
||||
$this->assertEquals(200, $function['headers']['status-code']);
|
||||
$this->assertArrayHasKey('latestDeploymentId', $function['body']);
|
||||
@@ -2041,7 +2106,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEmpty($function['body']['latestDeploymentStatus']);
|
||||
|
||||
$deploymentId1 = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('cookies'),
|
||||
'code' => $this->packageFunction('php-cookie'),
|
||||
'activate' => true
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId1);
|
||||
@@ -2059,7 +2124,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertStringContainsString('cookieValue', $execution['body']['responseBody']);
|
||||
|
||||
$deploymentId2 = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId2);
|
||||
@@ -2127,8 +2192,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Error Pages',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'commands' => 'cd non-existing-directory',
|
||||
'execute' => ['any']
|
||||
@@ -2138,7 +2203,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$deployment = $this->createDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -2155,7 +2221,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
|
||||
// canceled deployment
|
||||
$deployment = $this->createDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -2183,8 +2250,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Error Pages',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'commands' => '',
|
||||
'execute' => ['users']
|
||||
@@ -2195,7 +2262,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
@@ -2217,8 +2284,8 @@ class FunctionsCustomServerTest extends Scope
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Error Pages',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 15,
|
||||
'commands' => '',
|
||||
'execute' => ['any']
|
||||
@@ -2229,7 +2296,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
@@ -25,8 +25,8 @@ class FunctionsScheduleTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::user($this->getUser()['$id'])->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'users.*.create',
|
||||
'users.*.delete',
|
||||
@@ -36,7 +36,8 @@ class FunctionsScheduleTest extends Scope
|
||||
]);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -76,13 +77,14 @@ class FunctionsScheduleTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::user($this->getUser()['$id'])->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 10,
|
||||
'logging' => true,
|
||||
]);
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -177,14 +179,15 @@ class FunctionsScheduleTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::user($this->getUser()['$id'])->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 10,
|
||||
'logging' => true,
|
||||
]);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ class FunctionsClientTest extends Scope
|
||||
'variables' => [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Function',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'execute' => [Role::any()->toString()],
|
||||
]
|
||||
];
|
||||
@@ -96,7 +96,7 @@ class FunctionsClientTest extends Scope
|
||||
'map' => \json_encode([
|
||||
'code' => ["variables.code"]
|
||||
]),
|
||||
'code' => $this->packageFunction('basic')
|
||||
'code' => $this->packageFunction('php')
|
||||
];
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/graphql', [
|
||||
|
||||
@@ -26,8 +26,8 @@ class FunctionsServerTest extends Scope
|
||||
'variables' => [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Function',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.php',
|
||||
'runtime' => 'php-8.0',
|
||||
'execute' => [Role::any()->toString()],
|
||||
]
|
||||
];
|
||||
@@ -95,7 +95,7 @@ class FunctionsServerTest extends Scope
|
||||
'map' => \json_encode([
|
||||
'code' => ["variables.code"]
|
||||
]),
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('php'),
|
||||
];
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
|
||||
@@ -805,12 +805,13 @@ trait MigrationsBase
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js'
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php'
|
||||
]);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -854,8 +855,8 @@ trait MigrationsBase
|
||||
|
||||
$this->assertEquals($functionId, $response['body']['$id']);
|
||||
$this->assertEquals('Test', $response['body']['name']);
|
||||
$this->assertEquals('node-22', $response['body']['runtime']);
|
||||
$this->assertEquals('index.js', $response['body']['entrypoint']);
|
||||
$this->assertEquals('php-8.0', $response['body']['runtime']);
|
||||
$this->assertEquals('index.php', $response['body']['entrypoint']);
|
||||
|
||||
|
||||
$this->assertEventually(function () use ($functionId) {
|
||||
|
||||
@@ -4637,6 +4637,7 @@ 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, [
|
||||
@@ -4659,6 +4660,7 @@ 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', [
|
||||
|
||||
@@ -30,6 +30,8 @@ class ProjectsCustomServerTest extends Scope
|
||||
'domain' => $testId . '-api.appwrite.test',
|
||||
]);
|
||||
|
||||
\var_dump($response);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', $headers, [
|
||||
|
||||
@@ -227,7 +227,7 @@ trait ProxyBase
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Proxy Function',
|
||||
'entrypoint' => 'index.js',
|
||||
'commands' => '',
|
||||
@@ -244,7 +244,7 @@ trait ProxyBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
|
||||
@@ -16,6 +16,13 @@ trait RealtimeBase
|
||||
$projectId = $this->getProject()['$id'];
|
||||
}
|
||||
|
||||
$headers = array_merge(
|
||||
[
|
||||
"Origin" => "appwrite.test",
|
||||
],
|
||||
$headers
|
||||
);
|
||||
|
||||
$query = [
|
||||
"project" => $projectId,
|
||||
"channels" => $channels,
|
||||
@@ -42,7 +49,7 @@ trait RealtimeBase
|
||||
|
||||
public function testConnectionFailureMissingChannels(): void
|
||||
{
|
||||
$client = $this->getWebsocket([]);
|
||||
$client = $this->getWebsocket();
|
||||
$payload = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey("type", $payload);
|
||||
@@ -57,7 +64,14 @@ trait RealtimeBase
|
||||
|
||||
public function testConnectionFailureUnknownProject(): void
|
||||
{
|
||||
$client = $this->getWebsocket(projectId: '123');
|
||||
$client = new WebSocketClient(
|
||||
"ws://appwrite-traefik/v1/realtime?project=123",
|
||||
[
|
||||
"headers" => [
|
||||
"Origin" => "appwrite.test",
|
||||
],
|
||||
]
|
||||
);
|
||||
$payload = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey("type", $payload);
|
||||
|
||||
@@ -524,8 +524,8 @@ class RealtimeConsoleClientTest extends Scope
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [
|
||||
'users.*.create',
|
||||
'users.*.delete',
|
||||
@@ -564,7 +564,8 @@ class RealtimeConsoleClientTest extends Scope
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('php'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
|
||||
@@ -1285,10 +1285,10 @@ class RealtimeCustomClientTest extends Scope
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test timeout execution',
|
||||
'name' => 'Test',
|
||||
'execute' => ['users'],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
@@ -1302,6 +1302,7 @@ class RealtimeCustomClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $this->packageFunction('timeout'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -14,9 +14,6 @@ class SitesConsoleClientTest extends Scope
|
||||
use SideConsole;
|
||||
use SitesBase;
|
||||
|
||||
/**
|
||||
* @group screenshots
|
||||
*/
|
||||
public function testSiteScreenshot(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
|
||||
@@ -6,7 +6,6 @@ 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
|
||||
{
|
||||
@@ -122,7 +121,6 @@ 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']);
|
||||
@@ -131,8 +129,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://'. $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']);
|
||||
$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']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
||||
@@ -937,12 +937,12 @@ class SitesCustomServerTest extends Scope
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals('canceled', $deployment['body']['status']);
|
||||
|
||||
// Ensures worker got eventually aware of cancellation and reacted properly
|
||||
$this->assertEventually(function () use ($siteId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($siteId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertStringContainsString('Build has been canceled.', $deployment['body']['buildLogs']);
|
||||
});
|
||||
/**
|
||||
* Build worker still runs the build.
|
||||
* 30s sleep gives worker enough time to finish build.
|
||||
* After build finished, it should still be canceled, not ready.
|
||||
*/
|
||||
\sleep(30);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deploymentId);
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ namespace Tests\E2E\Services\Tokens;
|
||||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
||||
trait TokensBase
|
||||
{
|
||||
@@ -277,88 +275,4 @@ trait TokensBase
|
||||
$this->assertEquals($image->getImageHeight(), $original->getImageHeight());
|
||||
$this->assertEquals('PNG', $image->getImageFormat());
|
||||
}
|
||||
|
||||
public function testFileAccessWithFileSecurity(): void
|
||||
{
|
||||
$bucket = $this->client->call(
|
||||
Client::METHOD_POST,
|
||||
'/storage/buckets',
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
],
|
||||
[
|
||||
'name' => 'Test Bucket',
|
||||
'bucketId' => ID::unique(),
|
||||
'fileSecurity' => true,
|
||||
'allowedFileExtensions' => ['jpg', 'png', 'jfif'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
$this->assertNotEmpty($bucket['body']['$id']);
|
||||
|
||||
$bucketId = $bucket['body']['$id'];
|
||||
|
||||
$file = $this->client->call(
|
||||
Client::METHOD_POST,
|
||||
'/storage/buckets/' . $bucketId . '/files',
|
||||
[
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
],
|
||||
[
|
||||
'fileId' => ID::unique(),
|
||||
'permissions' => [ Permission::read(Role::label('devrel')) ],
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
]
|
||||
);
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
$token = $this->client->call(
|
||||
Client::METHOD_POST,
|
||||
'/tokens/buckets/' . $bucketId . '/files/' . $fileId,
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]
|
||||
);
|
||||
|
||||
$jwtToken = $token['body']['secret'];
|
||||
|
||||
$endpoints = ['preview', 'view', 'download'];
|
||||
|
||||
foreach ($endpoints as $endpoint) {
|
||||
$response = $this->client->call(
|
||||
Client::METHOD_GET,
|
||||
"/storage/buckets/$bucketId/files/$fileId/$endpoint",
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
],
|
||||
[
|
||||
'token' => $jwtToken
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
|
||||
if ($endpoint === 'download') {
|
||||
$image = new \Imagick();
|
||||
$image->readImageBlob($response['body']);
|
||||
$original = new \Imagick(__DIR__ . '/../../../resources/logo.png');
|
||||
|
||||
$this->assertEquals($original->getImageWidth(), $image->getImageWidth());
|
||||
$this->assertEquals($original->getImageHeight(), $image->getImageHeight());
|
||||
$this->assertEquals('PNG', $image->getImageFormat());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,8 +399,8 @@ class WebhooksCustomServerTest extends Scope
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
@@ -439,8 +439,8 @@ class WebhooksCustomServerTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Test',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'execute' => [Role::any()->toString()],
|
||||
'vars' => [
|
||||
'key1' => 'value1',
|
||||
@@ -496,7 +496,7 @@ class WebhooksCustomServerTest extends Scope
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'entrypoint' => 'index.js',
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
module.exports = async(context) => {
|
||||
context.log('log-works');
|
||||
context.error('error-log-works');
|
||||
|
||||
if(context.req.headers['x-appwrite-user-jwt']) {
|
||||
context.log('jwt-is-valid');
|
||||
} else {
|
||||
context.log('jwt-is-invalid');
|
||||
}
|
||||
|
||||
if(context.req.path === '/custom-response') {
|
||||
const code = +(context.req.query['code'] || '200');
|
||||
const body = context.req.query['body'] || '';
|
||||
return context.res.send(body, code);
|
||||
}
|
||||
|
||||
context.log('body-is-' + (context.req.body ?? ''));
|
||||
context.log('custom-header-is-' + (context.req.headers['x-custom-header'] ?? ''));
|
||||
context.log('method-is-' + (context.req.method ?? '').toLowerCase());
|
||||
context.log('path-is-' + (context.req.path ?? ''));
|
||||
context.log('user-is-' + (context.req.headers['x-appwrite-user-id'] ?? ''));
|
||||
|
||||
const statusCode = context.req.query['code'] || '200';
|
||||
|
||||
return context.res.json({
|
||||
'APPWRITE_FUNCTION_ID' : process.env.APPWRITE_FUNCTION_ID ?? '',
|
||||
'APPWRITE_FUNCTION_NAME' : process.env.APPWRITE_FUNCTION_NAME ?? '',
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' : process.env.APPWRITE_FUNCTION_DEPLOYMENT ?? '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' : context.req.headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' : process.env.APPWRITE_FUNCTION_RUNTIME_NAME,
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' : process.env.APPWRITE_FUNCTION_RUNTIME_VERSION,
|
||||
'APPWRITE_VERSION' : process.env.APPWRITE_VERSION ?? '',
|
||||
'APPWRITE_REGION' : process.env.APPWRITE_REGION ?? '',
|
||||
'UNICODE_TEST' : "êä",
|
||||
'GLOBAL_VARIABLE' : process.env.GLOBAL_VARIABLE ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT' : context.req.headers['x-appwrite-event'] ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT_DATA' : context.req.bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_DATA' : context.req.bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_USER_ID' : context.req.headers['x-appwrite-user-id'] ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' : context.req.headers['x-appwrite-user-jwt'] ?? '',
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' : process.env.APPWRITE_FUNCTION_PROJECT_ID,
|
||||
'APPWRITE_FUNCTION_MEMORY' : process.env.APPWRITE_FUNCTION_MEMORY,
|
||||
'APPWRITE_FUNCTION_CPUS' : process.env.APPWRITE_FUNCTION_CPUS,
|
||||
'CUSTOM_VARIABLE' : process.env.CUSTOM_VARIABLE
|
||||
}, +statusCode);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
const crypto = require('crypto')
|
||||
|
||||
module.exports = async(context) => {
|
||||
const hash = crypto.createHash('md5').update(context.req.bodyBinary).digest("hex")
|
||||
return context.res.send(hash);
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = async(context) => {
|
||||
const bytes = Buffer.from(Uint8Array.from([0, 10, 255]));
|
||||
return context.res.binary(bytes);
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = async(context) => {
|
||||
return context.res.send(context.req.headers['cookie'] ?? '');
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
Future<dynamic> main(final context) async {
|
||||
context.log('Amazing Function Log');
|
||||
|
||||
response.json({
|
||||
'APPWRITE_FUNCTION_ID' : Platform.environment['APPWRITE_FUNCTION_ID'] ?? '',
|
||||
'APPWRITE_FUNCTION_NAME' : Platform.environment['APPWRITE_FUNCTION_NAME'] ?? '',
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' : Platform.environment['APPWRITE_FUNCTION_DEPLOYMENT'] ?? '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' : context.req.headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' : Platform.environment['APPWRITE_FUNCTION_RUNTIME_NAME'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' : Platform.environment['APPWRITE_FUNCTION_RUNTIME_VERSION'] ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT' : context.req.headers['x-appwrite-event'] ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT_DATA' : context.req.bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_DATA' : context.req.bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_USER_ID' : context.req.headers['x-appwrite-user-id'] ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' : context.req.headers['x-appwrite-user-jwt'] ?? '',
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' : Platform.environment['APPWRITE_FUNCTION_PROJECT_ID'] ?? '',
|
||||
'CUSTOM_VARIABLE' : request.variables['CUSTOM_VARIABLE']
|
||||
});
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
const sdk = require('node-appwrite');
|
||||
|
||||
module.exports = async(context) => {
|
||||
const client = new sdk.Client();
|
||||
client.setEndpoint(process.env.APPWRITE_FUNCTION_API_ENDPOINT);
|
||||
client.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID);
|
||||
client.setKey(context.req.headers['x-appwrite-key']);
|
||||
|
||||
const users = new sdk.Users(client);
|
||||
|
||||
const response = await users.list();
|
||||
context.log(JSON.stringify(response));
|
||||
|
||||
return context.res.json(response);
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "dynamic-api-key",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dynamic-api-key",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"node-appwrite": "^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-appwrite": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-17.0.0.tgz",
|
||||
"integrity": "sha512-5Moi5ENPnoAfU1/6CZP9K2NTuB6Nm3dSyhokno+24RDuP7czjXCdwzfeyjmyHieggbrLkN89AYSOv9W1XkCL9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"node-fetch-native-with-agent": "1.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch-native-with-agent": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch-native-with-agent/-/node-fetch-native-with-agent-1.7.2.tgz",
|
||||
"integrity": "sha512-5MaOOCuJEvcckoz7/tjdx1M6OusOY6Xc5f459IaruGStWnKzlI1qpNgaAwmn4LmFYcsSlj+jBMk84wmmRxfk5g==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "dynamic-api-key",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"node-appwrite": "^17.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = async(context) => {
|
||||
context.log(context.req.body.$id);
|
||||
context.log(context.req.body.name);
|
||||
return context.res.empty();
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = async(context) => {
|
||||
return context.res.empty();
|
||||
};
|
||||
@@ -1,3 +1,21 @@
|
||||
module.exports = async(context) => {
|
||||
return context.res.send('OK');
|
||||
context.log('Amazing Function Log');
|
||||
|
||||
return context.res.json({
|
||||
'APPWRITE_FUNCTION_ID' : process.env.APPWRITE_FUNCTION_ID ?? '',
|
||||
'APPWRITE_FUNCTION_NAME' : process.env.APPWRITE_FUNCTION_NAME ?? '',
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' : process.env.APPWRITE_FUNCTION_DEPLOYMENT ?? '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' : context.req.headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' : process.env.APPWRITE_FUNCTION_RUNTIME_NAME,
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' : process.env.APPWRITE_FUNCTION_RUNTIME_VERSION,
|
||||
'APPWRITE_FUNCTION_EVENT' : context.req.headers['x-appwrite-event'] ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT_DATA' : context.req.bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_DATA' : context.req.bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_USER_ID' : context.req.headers['x-appwrite-user-id'] ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' : context.req.headers['x-appwrite-user-jwt'] ?? '',
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' : process.env.APPWRITE_FUNCTION_PROJECT_ID,
|
||||
'APPWRITE_FUNCTION_MEMORY' : process.env.APPWRITE_FUNCTION_MEMORY,
|
||||
'APPWRITE_FUNCTION_CPUS' : process.env.APPWRITE_FUNCTION_CPUS,
|
||||
'CUSTOM_VARIABLE' : process.env.CUSTOM_VARIABLE
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return function ($context) {
|
||||
$hash = md5($context->req->bodyBinary);
|
||||
return $context->res->send($hash);
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return function ($context) {
|
||||
$bytes = pack('C*', ...[0, 10, 255]);
|
||||
return $context->res->binary($bytes);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return function ($context) {
|
||||
return $context->res->send($context->req->headers['cookie'] ?? '');
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return function ($context) {
|
||||
$context->log($context->req->body['$id']);
|
||||
$context->log($context->req->body['name']);
|
||||
|
||||
return $context->res->empty();
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
return function ($context) {
|
||||
$context->log('Amazing Function Log');
|
||||
|
||||
return $context->res->json([
|
||||
'APPWRITE_FUNCTION_ID' => \getenv('APPWRITE_FUNCTION_ID') ?: '',
|
||||
'APPWRITE_FUNCTION_NAME' => \getenv('APPWRITE_FUNCTION_NAME') ?: '',
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => \getenv('APPWRITE_FUNCTION_DEPLOYMENT') ?: '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $context->req->headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
|
||||
'APPWRITE_VERSION' => \getenv('APPWRITE_VERSION') ?: '',
|
||||
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
|
||||
'APPWRITE_FUNCTION_EVENT' => $context->req->headers['x-appwrite-event'] ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT_DATA' => $context->req->bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_DATA' => $context->req->bodyRaw ?? '',
|
||||
'APPWRITE_FUNCTION_USER_ID' => $context->req->headers['x-appwrite-user-id'] ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' => $context->req->headers['x-appwrite-user-jwt'] ?? '',
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => \getenv('APPWRITE_FUNCTION_PROJECT_ID') ?: '',
|
||||
'CUSTOM_VARIABLE' => \getenv('CUSTOM_VARIABLE') ?: '',
|
||||
]);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "appwrite/cloud-function-demo",
|
||||
"description": "Demo cloud function script",
|
||||
"type": "library",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Team Appwrite",
|
||||
"email": "team@appwrite.io"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"appwrite/appwrite": "1.1.*"
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "afdff6a172e6c44aee11f1562175f81a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "appwrite/appwrite",
|
||||
"version": "1.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-for-php.git",
|
||||
"reference": "98b327d3fd18a72f4582019916afd735a0e9e0e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/98b327d3fd18a72f4582019916afd735a0e9e0e7",
|
||||
"reference": "98b327d3fd18a72f4582019916afd735a0e9e0e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"php": ">=7.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "3.7.35"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Appwrite\\": "src/Appwrite"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks",
|
||||
"support": {
|
||||
"email": "team@localhost.test",
|
||||
"issues": "https://github.com/appwrite/sdk-for-php/issues",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/1.1.2",
|
||||
"url": "https://appwrite.io/support"
|
||||
},
|
||||
"time": "2020-08-15T18:24:32+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.4.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
return function ($context) {
|
||||
return $context->res->json([
|
||||
'APPWRITE_FUNCTION_ID' => \getenv('APPWRITE_FUNCTION_ID') ?: '',
|
||||
'APPWRITE_FUNCTION_NAME' => \getenv('APPWRITE_FUNCTION_NAME') ?: '',
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => \getenv('APPWRITE_FUNCTION_DEPLOYMENT') ?: '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $context->req->headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
|
||||
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
|
||||
'APPWRITE_FUNCTION_CPUS' => \getenv('APPWRITE_FUNCTION_CPUS') ?: '',
|
||||
'APPWRITE_FUNCTION_MEMORY' => \getenv('APPWRITE_FUNCTION_MEMORY') ?: '',
|
||||
'UNICODE_TEST' => "êä"
|
||||
]);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "appwrite/php-scopes",
|
||||
"description": "PHP scopes test script",
|
||||
"type": "library",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Team Appwrite",
|
||||
"email": "team@appwrite.io"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"appwrite/appwrite": "11.0.*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Appwrite\Client;
|
||||
use Appwrite\Services\Users;
|
||||
|
||||
return function ($context) {
|
||||
$client = new Client();
|
||||
$client
|
||||
->setEndpoint(getenv('APPWRITE_FUNCTION_API_ENDPOINT'))
|
||||
->setProject(getenv('APPWRITE_FUNCTION_PROJECT_ID'))
|
||||
->setKey($context->req->headers['x-appwrite-key']);
|
||||
$users = new Users($client);
|
||||
$response = $users->list();
|
||||
$context->log($response);
|
||||
return $context->res->json($response);
|
||||
};
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
|
||||
ENDPOINT="$APPWRITE_FUNCTION_API_ENDPOINT/users"
|
||||
PROJECT_ID="$APPWRITE_FUNCTION_PROJECT_ID"
|
||||
API_KEY="$APPWRITE_FUNCTION_API_KEY"
|
||||
|
||||
apk add curl
|
||||
|
||||
curl -v -X GET $ENDPOINT -H "x-appwrite-project: $PROJECT_ID" -H "x-appwrite-key: $API_KEY"
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return function ($req, $res) {
|
||||
$res->json([
|
||||
'v2Woks' => true
|
||||
]);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
return function ($context) {
|
||||
if ($context->req->path === '/custom-response') {
|
||||
$code = (int) ($context->req->query['code'] ?? '200');
|
||||
$body = $context->req->query['body'] ?? '';
|
||||
return $context->res->send($body, $code);
|
||||
}
|
||||
|
||||
$context->log('body-is-' . ($context->req->body ?? ''));
|
||||
$context->log('custom-header-is-' . ($context->req->headers['x-custom-header'] ?? ''));
|
||||
$context->log('method-is-' . \strtolower($context->req->method ?? ''));
|
||||
$context->log('path-is-' . ($context->req->path ?? ''));
|
||||
$context->log('user-is-' . $context->req->headers['x-appwrite-user-id'] ?? '');
|
||||
|
||||
if (empty($context->req->headers['x-appwrite-user-jwt'] ?? '')) {
|
||||
$context->log('jwt-is-invalid');
|
||||
} else {
|
||||
$context->log('jwt-is-valid');
|
||||
}
|
||||
|
||||
$context->error('error-log-works');
|
||||
|
||||
$statusCode = $context->req->query['code'] ?? '200';
|
||||
|
||||
return $context->res->json([
|
||||
'APPWRITE_FUNCTION_ID' => \getenv('APPWRITE_FUNCTION_ID') ?: '',
|
||||
'APPWRITE_FUNCTION_NAME' => \getenv('APPWRITE_FUNCTION_NAME') ?: '',
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => \getenv('APPWRITE_FUNCTION_DEPLOYMENT') ?: '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $context->req->headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
|
||||
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
|
||||
'UNICODE_TEST' => "êä",
|
||||
'GLOBAL_VARIABLE' => \getenv('GLOBAL_VARIABLE') ?: '',
|
||||
'APPWRITE_FUNCTION_CPUS' => \getenv('APPWRITE_FUNCTION_CPUS') ?: '',
|
||||
'APPWRITE_FUNCTION_MEMORY' => \getenv('APPWRITE_FUNCTION_MEMORY') ?: '',
|
||||
], \intval($statusCode));
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user