Compare commits

..
Author SHA1 Message Date
Prem Palanisamy d5c4934163 align composer.json with main, keep migration 1.* for JSON import/export 2026-03-26 23:27:52 +00:00
Prem Palanisamy e00cd52a03 Merge remote-tracking branch 'origin/main' into import-export-json 2026-03-26 23:06:14 +00:00
Eldad A. FuxandGitHub a5fa09b4ce Merge pull request #11185 from Shivangisharma4/doc-fix-typos-contributing
doc: Fix typos in CONTRIBUTING.md
2026-02-27 20:42:57 +01:00
10e2edac90 Update CONTRIBUTING.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-24 14:03:11 +05:30
Shivangisharma4 340081a67d doc: Fix typos in CONTRIBUTING.md 2026-01-24 13:58:20 +05:30
Darshan e4558faf2b regen: specs. 2026-01-20 19:26:20 +05:30
Darshan 3db634610c add: missing doc. 2026-01-20 19:26:00 +05:30
Darshan 511ca2a860 specs. 2026-01-20 18:30:13 +05:30
Darshan 0ad004e1a1 update: comment. 2026-01-20 18:29:12 +05:30
Darshan 43cd9cc086 add: tests;
fix: tests.
2026-01-20 18:28:47 +05:30
Darshan fca1563db7 fix: endpoint. 2026-01-20 18:23:04 +05:30
Darshan 9182326033 update: wire JSON import/export. 2026-01-20 17:17:41 +05:30
7016 changed files with 12016 additions and 54022 deletions
-31
View File
@@ -1,31 +0,0 @@
# PR Workflow for Appwrite
## Branch Targeting
**Important:** Appwrite does NOT use `main` as the primary development branch.
PRs should target the **current version branch** (e.g., `1.8.x`, `1.9.x`), not `main`.
### How to determine the correct target branch
1. Check which version branch you're currently on: `git branch --show-current`
2. Look for branches matching the pattern `X.Y.x` (e.g., `1.8.x`, `1.9.x`)
3. The current active development branch is typically the highest version number with the `.x` suffix
### When creating PRs
Always use the version branch as the base:
```bash
# Correct - targets the version branch
gh pr create --base 1.8.x --title "Your PR title" --body "..."
# Wrong - do not target main
gh pr create --base main ...
```
### Branch naming convention
- `X.Y.x` branches (e.g., `1.8.x`) - Active development branches for each minor version
- `main` - Not used for regular PRs; reserved for release management
- Feature branches should be created from and merged back into the current version branch
+2 -6
View File
@@ -2,7 +2,6 @@ _APP_ENV=development
_APP_EDITION=self-hosted
_APP_LOCALE=en
_APP_WORKER_PER_CORE=6
_APP_COMPRESSION_ENABLED=enabled
_APP_COMPRESSION_MIN_SIZE_BYTES=1024
_APP_CONSOLE_WHITELIST_ROOT=disabled
_APP_CONSOLE_WHITELIST_EMAILS=
@@ -10,7 +9,6 @@ _APP_CONSOLE_SESSION_ALERTS=enabled
_APP_CONSOLE_WHITELIST_IPS=
_APP_CONSOLE_COUNTRIES_DENYLIST=AQ
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
_APP_MIGRATION_HOST=appwrite
_APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io
_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
@@ -26,9 +24,8 @@ _APP_OPENSSL_KEY_V1=your-secret-key
_APP_DNS=172.16.238.100 # CoreDNS
_APP_DOMAIN=appwrite.test
_APP_CONSOLE_DOMAIN=localhost
_APP_CONSOLE_TRUSTED_PROJECTS=trusted-project,another-trusted-project
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_SITES=sites.localhost,rebranded.localhost
_APP_DOMAIN_SITES=sites.localhost
_APP_DOMAIN_TARGET_CNAME=cname.localhost
_APP_DOMAIN_TARGET_A=203.0.0.1
_APP_DOMAIN_TARGET_AAAA=::1
@@ -104,7 +101,7 @@ _APP_USAGE_AGGREGATION_INTERVAL=30
_APP_STATS_RESOURCES_INTERVAL=30
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_INTERVAL_DOMAIN_VERIFICATION=120
_APP_INTERVAL_DOMAIN_VERIFICATION=60
_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS=300
_APP_USAGE_STATS=enabled
_APP_LOGGING_CONFIG=
@@ -132,4 +129,3 @@ _APP_PROJECT_REGIONS=default
_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000
_APP_STATS_USAGE_DUAL_WRITING_DBS=database_db_main
_APP_TRUSTED_HEADERS=x-forwarded-for
_APP_POOL_ADAPTER=stack
+57 -35
View File
@@ -111,7 +111,7 @@ $ git push origin [name_of_your_new_branch]
## Setup From Source
To set up a working **development environment**, just fork the project git repository and install the backend and frontend dependencies using the proper package manager and create run the docker-compose stack.
To set up a working **development environment**, just fork the project git repository and install the backend and frontend dependencies using the proper package manager and run the docker-compose stack.
> If you just want to install Appwrite for day-to-day use and not as a contributor, you can reference the [installation guide](https://github.com/appwrite/appwrite#installation), the [getting started guide](https://appwrite.io/docs/quick-starts), or the main [README](README.md) file.
@@ -173,12 +173,12 @@ Learn more at our [Technology Stack](#technology-stack) section.
- [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) - Appwrite console architecture
##### Container Namespace Conventions
To keep our services easy to understand within Docker we follow a naming convention for all our containers depending on it's intended use.
To keep our services easy to understand within Docker we follow a naming convention for all our containers depending on its intended use.
`appwrite-worker-X` - Workers (`src/Appwrite/Platform/Workers/*`)
`appwrite-task-X` - Tasks (`src/Appwrite/Platform/Tasks/*`)
Other containes should be named the same as their service, for example `redis` should just be called `redis`.
Other containers should be named the same as their service, for example `redis` should just be called `redis`.
##### Security
@@ -189,7 +189,7 @@ Other containes should be named the same as their service, for example `redis` s
## Modules
As Appwrite grows, we noticed approach of having all service endpoints in `app/controllers/api/[service].php` is not maintainable. Not only it creates massive files, it also doesnt contain all product's features such as workers or tasks. While there might still be some occurances of those controller files, we avoid it in all new development, and gradually migrate existing controllers to **HTTP modules**.
As Appwrite grows, we noticed approach of having all service endpoints in `app/controllers/api/[service].php` is not maintainable. Not only it creates massive files, it also doesn't contain all product's features such as workers or tasks. While there might still be some occurrences of those controller files, we avoid it in all new development, and gradually migrate existing controllers to **HTTP modules**.
### HTTP Endpoints
@@ -204,7 +204,7 @@ Tips and tricks:
1. If endpoint doesn't have resource, use service name as resource name too
> Example: `Modules/Sites/Http/Sites/Get.php`
2. If there are multiple resources, use then all in folder structure
2. If there are multiple resources, use them all in folder structure
> Example: `Modules/Sites/Http/Deployments/Builds/Create.php`
3. Action can only be `Get`, `Create`, `Update`, `Delete` or `XList`
@@ -222,51 +222,73 @@ Appwrite's current structure is a combination of both [Monolithic](https://en.wi
```bash
.
├── app # Main application
│ ├── assets
│ │ ├── dbip
│ │ ├── fonts
│ │ └── security
│ ├── config # Config files
│ │ ├── avatars
│ │ ├── collections
│ │ ├── locale
│ │ ├── specs
│ │ ├── storage
│ │ └── templates
│ ├── controllers # API & dashboard controllers
│ │ ├── api
│ │ ├── shared
│ │ └── web
│ ├── db # DB schemas
├── sdks # SDKs generated copies (used for generating code examples)
── tasks # Server CLI commands
├── views # HTML server-side templates
└── workers # Background workers
│ ├── init # DB schemas
│ └── database
── views # HTML server-side templates
├── general
└── install
├── bin # Server executables (tasks & workers)
├── docker # Docker related resources and configs
├── dev # Debugger config
├── docs # Docs and tutorials
│ ├── examples
│ ├── lists
│ ├── references
│ ├── sdks
│ ├── services
│ ├── specs
│ └── tutorials
├── public # Public files
│ ├── dist
│ ├── fonts
│ ├── images
│ ├── scripts
── styles
├── src # Supporting libraries (each lib has one role, common libs are released as individual projects)
│ └── Appwrite
├── Auth
├── Detector
├── Docker
| ├── DSN
├── Event
├── Extend
├── GraphQL
├── Messaging
├── Migration
├── Network
├── OpenSSL
├── Promises
├── Specification
├── Task
├── Template
├── URL
── Utopia
│ ├── sdk-console
── sdk-project
│ └── sdk-web
├── src # Supporting libraries (each lib has one role, common libs are released as
│ ├── Appwrite
├── Auth
├── Certificates
├── Deletes
├── Detector
├── Docker
├── Event
├── Extend
├── Functions/Validator
├── GraphQL
├── Hooks
├── Messaging
├── Migration
├── Network
├── OpenSSL
├── Platform
── Promises
│ │ ├── PubSub
│ │ ├── SDK
│ │ ├── Task/Validator
│ │ ├── Template
│ │ ├── Transformation
│ │ ├── URL
│ │ ├── Utopia
│ │ └── Vcs
│ └── Executor
└── tests # End to end & unit tests
├── benchmarks
├── e2e
├── extensions
├── resources
└── unit
```
@@ -395,7 +417,7 @@ These are the current metrics we collect usage stats for:
> Note: The curly brackets in the metric name represents a template and is replaced with a value when the metric is processed.
Metrics are collected within 3 scopes Daily, monthly, an infinity. Adding new usage metric in order to aggregate usage stats is very simple, but very much dependent on where do you want to collect
Metrics are collected within 3 scopes Daily, monthly, and infinity. Adding new usage metric in order to aggregate usage stats is very simple, but very much dependent on where do you want to collect
statistics ,via API or via background worker. For both cases you will need to add a `const` variable in `app/init.php` under the usage metrics list using the naming convention `METRIC_<RESOURCE_NAME>` as shown below.
```php
@@ -661,7 +683,7 @@ docker compose exec redis redis-cli FLUSHALL
## Using preview domains locally
Appwrite Functions are automatically given a domain you can visit to execute the function. This domain has format `[SOMETHING].functions.localhost` unless you changed `_APP_DOMAIN_FUNCTIONS` environment variable. This default value works great when running Appwrite locally, but it can be impossible to use preview domains with Cloud woekspaces such as Gitpod or GitHub Codespaces.
Appwrite Functions are automatically given a domain you can visit to execute the function. This domain has format `[SOMETHING].functions.localhost` unless you changed `_APP_DOMAIN_FUNCTIONS` environment variable. This default value works great when running Appwrite locally, but it can be impossible to use preview domains with Cloud workspaces such as Gitpod or GitHub Codespaces.
To use preview domains on Cloud workspaces, you can visit hostname provided by them, and supply function's preview domain as URL parameter:
+1 -2
View File
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM appwrite/base:0.11.5 AS base
FROM appwrite/base:0.10.6 AS base
LABEL maintainer="team@appwrite.io"
@@ -81,7 +81,6 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/worker-certificates && \
chmod +x /usr/local/bin/worker-databases && \
chmod +x /usr/local/bin/worker-deletes && \
chmod +x /usr/local/bin/worker-executions && \
chmod +x /usr/local/bin/worker-functions && \
chmod +x /usr/local/bin/worker-mails && \
chmod +x /usr/local/bin/worker-messaging && \
-6
View File
@@ -30,9 +30,6 @@ use Utopia\Pools\Group;
use Utopia\Queue\Broker\Pool as BrokerPool;
use Utopia\Queue\Publisher;
use Utopia\Registry\Registry;
use Utopia\Span\Exporter;
use Utopia\Span\Span;
use Utopia\Span\Storage;
use Utopia\System\System;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
@@ -44,7 +41,6 @@ Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
// require controllers after overwriting runtimes
require_once __DIR__ . '/controllers/general.php';
global $register;
CLI::setResource('register', fn () => $register);
CLI::setResource('cache', function ($pools) {
@@ -329,6 +325,4 @@ $cli
$cli->shutdown()->action(fn () => Timer::clearAll());
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
Span::setStorage(new Storage\Coroutine());
Span::addExporter(new Exporter\Stdout());
run($cli->run(...));
+2 -2
View File
@@ -26,8 +26,8 @@ unset($common['files']);
$collections = [
'buckets' => $buckets,
'databases' => $databases,
'projects' => array_merge_recursive($projects, $common),
'console' => array_merge_recursive($platform, $common),
'projects' => array_merge($projects, $common),
'console' => array_merge($platform, $common),
'logs' => $logs,
];
+37 -8
View File
@@ -633,7 +633,29 @@ $platformCollections = [
'name' => 'keys',
'attributes' => [
[
'$id' => ID::custom('resourceType'),
'$id' => ID::custom('projectInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('projectId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => 0,
'array' => false,
'filters' => [],
],
[
'$id' => 'resourceType',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@@ -644,7 +666,7 @@ $platformCollections = [
'filters' => [],
],
[
'$id' => ID::custom('resourceId'),
'$id' => 'resourceId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@@ -655,7 +677,7 @@ $platformCollections = [
'filters' => [],
],
[
'$id' => ID::custom('resourceInternalId'),
'$id' => 'resourceInternalId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@@ -734,14 +756,21 @@ $platformCollections = [
],
'indexes' => [
[
'$id' => ID::custom('_key_resource'),
'$id' => ID::custom('_key_project'),
'type' => Database::INDEX_KEY,
'attributes' => ['resourceType', 'resourceInternalId'],
'lengths' => [],
'orders' => [],
'attributes' => ['projectInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_accessedAt'),
'$id' => '_key_resource',
'type' => Database::INDEX_KEY,
'attributes' => ['resourceType', 'resourceInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_accessedAt',
'type' => Database::INDEX_KEY,
'attributes' => ['accessedAt'],
'lengths' => [],
-30
View File
@@ -357,11 +357,6 @@ return [
'description' => 'API key and session used in the same request. Use either `setSession` or `setKey`. Learn about which authentication method to use in the SSR docs: https://appwrite.io/docs/products/auth/server-side-rendering',
'code' => 403,
],
Exception::USER_JWT_AND_COOKIE_SET => [
'name' => Exception::USER_JWT_AND_COOKIE_SET,
'description' => 'JWT and cookie used in the same request. Use either `setJWT` or `setCookie`. Learn about which authentication method to use in the SSR docs: https://appwrite.io/docs/products/auth/server-side-rendering',
'code' => 403,
],
Exception::API_KEY_EXPIRED => [
'name' => Exception::API_KEY_EXPIRED,
'description' => 'The dynamic API key has expired. Please don\'t use dynamic API keys for more than duration of the execution.',
@@ -1079,11 +1074,6 @@ return [
'description' => 'The project key has expired. Please generate a new key using the Appwrite console.',
'code' => 401,
],
Exception::ACCOUNT_KEY_EXPIRED => [
'name' => Exception::ACCOUNT_KEY_EXPIRED,
'description' => 'The account API key has expired. Please generate a new key using the Appwrite console.',
'code' => 401,
],
Exception::ROUTER_HOST_NOT_FOUND => [
'name' => Exception::ROUTER_HOST_NOT_FOUND,
'description' => 'Host is not trusted. This could occur because you have not configured a custom domain. Add a custom domain to your project first and try again.',
@@ -1139,11 +1129,6 @@ return [
'description' => 'Key with the requested ID could not be found.',
'code' => 404,
],
Exception::KEY_ALREADY_EXISTS => [
'name' => Exception::KEY_ALREADY_EXISTS,
'description' => 'Key with the same ID already exists. Try again with a different ID.',
'code' => 409,
],
Exception::PLATFORM_NOT_FOUND => [
'name' => Exception::PLATFORM_NOT_FOUND,
'description' => 'Platform with the requested ID could not be found.',
@@ -1343,19 +1328,4 @@ return [
'description' => 'Target has an invalid provider type.',
'code' => 400,
],
Exception::USER_ID_MISSING => [
'name' => Exception::USER_ID_MISSING,
'description' => 'When using account API key, make sure to pass x-appwrite-user header with your user ID.',
'code' => 403,
],
Exception::ORGANIZATION_ID_MISSING => [
'name' => Exception::ORGANIZATION_ID_MISSING,
'description' => 'When using organization API key, make sure to pass x-appwrite-organization header with your organization ID.',
'code' => 403,
],
Exception::PROJECT_ID_MISSING => [
'name' => Exception::PROJECT_ID_MISSING,
'description' => 'When using project API key, make sure to pass x-appwrite-project header with your project ID.',
'code' => 403,
],
];
+38 -21
View File
@@ -50,12 +50,17 @@
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Poppins';
src: url('https://assets.appwrite.io/fonts/poppins/poppins-v23-latin-regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
</style>
<style>
.main {
max-width: 650px;
margin: 0 auto;
margin-top: 32px;
body {
padding: 32px;
line-height: 1.5;
color: #616b7c;
@@ -63,12 +68,15 @@
font-weight: 400;
font-family: "Inter", sans-serif;
background-color: #ffffff;
margin: 0;
padding: 0;
line-height: 150%;
}
.main a {
a {
color: currentColor;
word-break: break-all;
}
.main a.button {
a.button {
box-sizing: border-box;
display: inline-block;
text-align: center;
@@ -79,8 +87,8 @@
border: 1px solid #414146;
border-radius: 8px;
}
.main a.button:hover,
.main a.button:focus {
a.button:hover,
a.button:focus {
opacity: 0.8;
}
table {
@@ -94,6 +102,11 @@
td {
vertical-align: top;
}
.main {
max-width: 650px;
margin: 0 auto;
margin-top: 32px;
}
h1 {
font-size: 22px;
margin-bottom: 0px;
@@ -125,6 +138,9 @@
border: none;
border-top: 1px solid #e8e9f0;
}
h* {
font-family: 'Poppins', sans-serif;
}
p {
margin-bottom: 10px;
}
@@ -139,20 +155,21 @@
<body style="direction: {{direction}}">
<div style="display: none; overflow: hidden; max-height: 0; max-width: 0; opacity: 0; line-height: 1px;">
{{preview}}
<div>{{previewWhitespace}}</div>
</div>
<div style="display: none; overflow: hidden; max-height: 0; max-width: 0; opacity: 0; line-height: 1px;">
{{preview}}
<div>{{previewWhitespace}}</div>
</div>
<div class="main" style="max-width:650px; word-wrap: break-word; overflow-wrap: break-word; word-break: normal; margin:0 auto;">
<table style="margin-top: 32px">
<tr>
<td>
{{body}}
</td>
</tr>
</table>
</div>
<div style="max-width:650px; word-wrap: break-word; overflow-wrap: break-word;
word-break: normal; margin:0 auto;">
<table style="margin-top: 32px">
<tr>
<td>
{{body}}
</td>
</tr>
</table>
</div>
</body>
</html>
+15 -15
View File
@@ -57,21 +57,21 @@
"emails.recovery.thanks": "Thanks,",
"emails.recovery.buttonText": "Reset password",
"emails.recovery.signature": "{{project}} team",
"emails.csvExport.success.subject": "Your CSV export is ready",
"emails.csvExport.success.preview": "Your data export has been completed successfully.",
"emails.csvExport.success.hello": "Hello {{user}},",
"emails.csvExport.success.body": "Your CSV export is ready to download. Click the button below to download your data export.",
"emails.csvExport.success.footer": "This download link will expire in 1 hour.",
"emails.csvExport.success.thanks": "Thanks,",
"emails.csvExport.success.buttonText": "Download CSV",
"emails.csvExport.success.signature": "Appwrite team",
"emails.csvExport.failure.subject": "Your CSV export failed - file too large",
"emails.csvExport.failure.preview": "Your data export failed because the file size exceeds your plan limit.",
"emails.csvExport.failure.hello": "Hello {{user}},",
"emails.csvExport.failure.body": "Your CSV export could not be completed because the export file size ({{size}}MB) exceeds your plan limit. Please consider upgrading your plan or exporting a smaller dataset.",
"emails.csvExport.failure.footer": "If you have any questions, please contact our support team.",
"emails.csvExport.failure.thanks": "Thanks,",
"emails.csvExport.failure.signature": "{{project}} team",
"emails.dataExport.success.subject": "Your {{type}} export is ready",
"emails.dataExport.success.preview": "Your data export has been completed successfully.",
"emails.dataExport.success.hello": "Hello {{user}},",
"emails.dataExport.success.body": "Your {{type}} export is ready to download. Click the button below to download your data export.",
"emails.dataExport.success.footer": "This download link will expire in 1 hour.",
"emails.dataExport.success.thanks": "Thanks,",
"emails.dataExport.success.buttonText": "Download {{type}}",
"emails.dataExport.success.signature": "{{platform}} team",
"emails.dataExport.failure.subject": "Your {{type}} export failed - file too large",
"emails.dataExport.failure.preview": "Your data export failed because the file size exceeds your plan limit.",
"emails.dataExport.failure.hello": "Hello {{user}},",
"emails.dataExport.failure.body": "Your {{type}} export could not be completed because the export file size ({{size}}MB) exceeds your plan limit. Please consider upgrading your plan or exporting a smaller dataset.",
"emails.dataExport.failure.footer": "If you have any questions, please contact our support team.",
"emails.dataExport.failure.thanks": "Thanks,",
"emails.dataExport.failure.signature": "{{project}} team",
"emails.invitation.subject": "Invitation to {{team}} Team at {{project}}",
"emails.invitation.preview": "{{owner}} invited you to join {{team}} at {{project}}",
"emails.invitation.hello": "Hello {{user}},",
-14
View File
@@ -2,17 +2,6 @@
use Utopia\System\System;
// For now, take first domain as primary (for previews)
// Later-on this can become platform-specific with new env var (appwrite=this,imagine=that)
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
if (\str_contains($sitesDomain, ',')) {
$sitesDomain = explode(',', $sitesDomain)[0];
}
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (\str_contains($functionsDomain, ',')) {
$functionsDomain = explode(',', $functionsDomain)[0];
}
/**
* Platform configuration
*/
@@ -22,7 +11,6 @@ return [
'hostnames' => array_filter(array_unique([
System::getEnv('_APP_DOMAIN', 'localhost'),
System::getEnv('_APP_CONSOLE_DOMAIN', 'localhost'),
System::getEnv('_APP_MIGRATION_HOST'),
])),
'platformName' => APP_EMAIL_PLATFORM_NAME,
'logoUrl' => APP_EMAIL_LOGO_URL,
@@ -35,6 +23,4 @@ return [
'privacyUrl' => APP_EMAIL_PRIVACY_URL,
'websiteUrl' => 'https://' . APP_DOMAIN,
'emailSenderName' => APP_EMAIL_PLATFORM_NAME,
'sitesDomain' => $sitesDomain,
'functionsDomain' => $functionsDomain,
];
-15
View File
@@ -1,15 +0,0 @@
<?php
// List of scopes for Account API keys (Tokens)
return [
"account" => [
"description" => 'Access to manage account, its organizations, sessions, tokens, and billing.',
],
"teams.read" => [
"description" => 'Access to read account\'s organizations.',
],
"teams.write" => [
"description" => 'Access to create, update and delete account\'s organizations and its memberships.',
],
];
-42
View File
@@ -1,42 +0,0 @@
<?php
// List of scopes for organization (teams) API keys
return [
"platforms.read" => [
"description" => 'Access to read project\'s platforms',
],
"platforms.write" => [
"description" =>
'Access to create, update, and delete project\'s platforms',
],
"projects.read" => [
"description" => 'Access to read organization\'s projects',
],
"projects.write" => [
"description" =>
"Access to create, update, and delete projects in organization",
],
"keys.read" => [
"description" => 'Access to read project\'s API keys',
],
"keys.write" => [
"description" =>
"Access to create, update, and delete project\'s API keys",
],
"devKeys.read" => [
"description" => 'Access to read project\'s development keys',
],
"devKeys.write" => [
"description" =>
"Access to create, update, and delete project\'s development keys",
],
"webhooks.read" => [
"description" =>
"Access to read project\'s webhooks",
],
"webhooks.write" => [
"description" =>
"Access to create, update, and delete project\'s webhooks",
],
];
+15 -35
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '22.0.0',
'version' => '21.5.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -60,7 +60,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '21.0.0',
'version' => '20.3.3',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -79,7 +79,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '14.0.0',
'version' => '13.5.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -117,7 +117,7 @@ return [
'key' => 'android',
'name' => 'Android',
'namespace' => 'io.appwrite',
'version' => '12.0.0',
'version' => '11.4.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -140,7 +140,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.20.0',
'version' => '0.19.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -208,7 +208,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '0.3.0',
'version' => '0.2.0',
'url' => '',
'package' => '',
'enabled' => true,
@@ -227,7 +227,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '13.3.1',
'version' => '13.0.1',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -250,26 +250,6 @@ return [
],
],
],
[
'key' => 'markdown',
'name' => 'Markdown',
'version' => '0.2.0',
'url' => 'https://github.com/appwrite/sdk-for-md.git',
'package' => 'https://www.npmjs.com/package/@appwrite.io/docs',
'enabled' => true,
'beta' => false,
'dev' => false,
'hidden' => false,
'family' => APP_SDK_PLATFORM_CONSOLE,
'prism' => 'markdown',
'source' => \realpath(__DIR__ . '/../sdks/console-md'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-md.git',
'gitRepoName' => 'sdk-for-md',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'repoBranch' => 'main',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/md/CHANGELOG.md'),
],
],
],
@@ -283,7 +263,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '22.0.0',
'version' => '21.1.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -302,7 +282,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '20.0.0',
'version' => '19.1.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -321,7 +301,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '15.0.0',
'version' => '14.1.0',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -340,7 +320,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '21.0.0',
'version' => '20.1.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -378,7 +358,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.25.0',
'version' => '0.24.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -397,7 +377,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '21.0.0',
'version' => '20.1.1',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -417,7 +397,7 @@ return [
'key' => 'kotlin',
'name' => 'Kotlin',
'namespace' => 'io.appwrite',
'version' => '14.0.0',
'version' => '13.1.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -440,7 +420,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '15.0.0',
'version' => '14.1.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
+61 -61
View File
@@ -555,7 +555,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 250,
"weight": 255,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -627,7 +627,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 252,
"weight": 257,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -751,7 +751,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 253,
"weight": 258,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -891,7 +891,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 254,
"weight": 259,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1015,7 +1015,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 258,
"weight": 263,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1149,7 +1149,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 259,
"weight": 264,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1287,7 +1287,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 251,
"weight": 256,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1388,7 +1388,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 257,
"weight": 262,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1487,7 +1487,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 255,
"weight": 260,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1586,7 +1586,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 256,
"weight": 261,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -4051,7 +4051,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 261,
"weight": 266,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4179,7 +4179,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 260,
"weight": 265,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4313,7 +4313,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 264,
"weight": 269,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4373,7 +4373,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 262,
"weight": 267,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -4863,7 +4863,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 263,
"weight": 268,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -4947,7 +4947,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 266,
"weight": 271,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5041,7 +5041,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 265,
"weight": 270,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5135,7 +5135,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 267,
"weight": 272,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -5888,7 +5888,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 343,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -5955,7 +5955,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 339,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -6026,7 +6026,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 340,
"weight": 337,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6090,7 +6090,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 341,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6168,7 +6168,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 342,
"weight": 339,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6234,7 +6234,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 344,
"weight": 341,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6319,7 +6319,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 294,
"weight": 299,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6431,7 +6431,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 286,
"weight": 291,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6592,7 +6592,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 287,
"weight": 292,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6703,7 +6703,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 290,
"weight": 295,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6858,7 +6858,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 288,
"weight": 293,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -6970,7 +6970,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 292,
"weight": 297,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7077,7 +7077,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 297,
"weight": 302,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7206,7 +7206,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 296,
"weight": 301,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7335,7 +7335,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 444,
"weight": 433,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7422,7 +7422,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 442,
"weight": 431,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7540,7 +7540,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 443,
"weight": 432,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7615,7 +7615,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 187,
"weight": 190,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7669,7 +7669,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 186,
"weight": 189,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8155,7 +8155,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 234,
"weight": 239,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8239,7 +8239,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 238,
"weight": 243,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -8315,7 +8315,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 540,
"weight": 529,
"cookies": false,
"type": "",
"demo": "storage\/list-files.md",
@@ -8414,7 +8414,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 538,
"weight": 527,
"cookies": false,
"type": "upload",
"demo": "storage\/create-file.md",
@@ -8516,7 +8516,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 539,
"weight": 528,
"cookies": false,
"type": "",
"demo": "storage\/get-file.md",
@@ -8590,7 +8590,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 541,
"weight": 530,
"cookies": false,
"type": "",
"demo": "storage\/update-file.md",
@@ -8682,7 +8682,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 542,
"weight": 531,
"cookies": false,
"type": "",
"demo": "storage\/delete-file.md",
@@ -8751,7 +8751,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 544,
"weight": 533,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-download.md",
@@ -8831,7 +8831,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 543,
"weight": 532,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-preview.md",
@@ -9061,7 +9061,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 545,
"weight": 534,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-view.md",
@@ -9148,7 +9148,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 416,
"weight": 405,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9218,7 +9218,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 412,
"weight": 401,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9292,7 +9292,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 413,
"weight": 402,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9359,7 +9359,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 414,
"weight": 403,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9440,7 +9440,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 415,
"weight": 404,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9509,7 +9509,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 417,
"weight": 406,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9597,7 +9597,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 408,
"weight": 397,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9708,7 +9708,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 400,
"weight": 389,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9864,7 +9864,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 401,
"weight": 390,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9974,7 +9974,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 404,
"weight": 393,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10124,7 +10124,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 402,
"weight": 391,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10235,7 +10235,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 406,
"weight": 395,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10341,7 +10341,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 411,
"weight": 400,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10469,7 +10469,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 410,
"weight": 399,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+61 -61
View File
@@ -555,7 +555,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 250,
"weight": 255,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -627,7 +627,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 252,
"weight": 257,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -751,7 +751,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 253,
"weight": 258,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -891,7 +891,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 254,
"weight": 259,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1015,7 +1015,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 258,
"weight": 263,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1149,7 +1149,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 259,
"weight": 264,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1287,7 +1287,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 251,
"weight": 256,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1388,7 +1388,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 257,
"weight": 262,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1487,7 +1487,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 255,
"weight": 260,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1586,7 +1586,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 256,
"weight": 261,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -4051,7 +4051,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 261,
"weight": 266,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4179,7 +4179,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 260,
"weight": 265,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4313,7 +4313,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 264,
"weight": 269,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4373,7 +4373,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 262,
"weight": 267,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -4863,7 +4863,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 263,
"weight": 268,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -4947,7 +4947,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 266,
"weight": 271,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5041,7 +5041,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 265,
"weight": 270,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5135,7 +5135,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 267,
"weight": 272,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -5888,7 +5888,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 343,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -5955,7 +5955,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 339,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -6026,7 +6026,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 340,
"weight": 337,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6090,7 +6090,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 341,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6168,7 +6168,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 342,
"weight": 339,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6234,7 +6234,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 344,
"weight": 341,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6319,7 +6319,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 294,
"weight": 299,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6431,7 +6431,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 286,
"weight": 291,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6592,7 +6592,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 287,
"weight": 292,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6703,7 +6703,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 290,
"weight": 295,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6858,7 +6858,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 288,
"weight": 293,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -6970,7 +6970,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 292,
"weight": 297,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7077,7 +7077,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 297,
"weight": 302,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7206,7 +7206,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 296,
"weight": 301,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7335,7 +7335,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 444,
"weight": 433,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7422,7 +7422,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 442,
"weight": 431,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7540,7 +7540,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 443,
"weight": 432,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7615,7 +7615,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 187,
"weight": 190,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7669,7 +7669,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 186,
"weight": 189,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8155,7 +8155,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 234,
"weight": 239,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8239,7 +8239,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 238,
"weight": 243,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -8315,7 +8315,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 540,
"weight": 529,
"cookies": false,
"type": "",
"demo": "storage\/list-files.md",
@@ -8414,7 +8414,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 538,
"weight": 527,
"cookies": false,
"type": "upload",
"demo": "storage\/create-file.md",
@@ -8516,7 +8516,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 539,
"weight": 528,
"cookies": false,
"type": "",
"demo": "storage\/get-file.md",
@@ -8590,7 +8590,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 541,
"weight": 530,
"cookies": false,
"type": "",
"demo": "storage\/update-file.md",
@@ -8682,7 +8682,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 542,
"weight": 531,
"cookies": false,
"type": "",
"demo": "storage\/delete-file.md",
@@ -8751,7 +8751,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 544,
"weight": 533,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-download.md",
@@ -8831,7 +8831,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 543,
"weight": 532,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-preview.md",
@@ -9061,7 +9061,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 545,
"weight": 534,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-view.md",
@@ -9148,7 +9148,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 416,
"weight": 405,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9218,7 +9218,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 412,
"weight": 401,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9292,7 +9292,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 413,
"weight": 402,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9359,7 +9359,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 414,
"weight": 403,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9440,7 +9440,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 415,
"weight": 404,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9509,7 +9509,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 417,
"weight": 406,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9597,7 +9597,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 408,
"weight": 397,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9708,7 +9708,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 400,
"weight": 389,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9864,7 +9864,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 401,
"weight": 390,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9974,7 +9974,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 404,
"weight": 393,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10124,7 +10124,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 402,
"weight": 391,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10235,7 +10235,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 406,
"weight": 395,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10341,7 +10341,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 411,
"weight": 400,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10469,7 +10469,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 410,
"weight": 399,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+61 -61
View File
@@ -612,7 +612,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 250,
"weight": 255,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -687,7 +687,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 252,
"weight": 257,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -811,7 +811,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 253,
"weight": 258,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -952,7 +952,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 254,
"weight": 259,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1076,7 +1076,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 258,
"weight": 263,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1213,7 +1213,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 259,
"weight": 264,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1353,7 +1353,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 251,
"weight": 256,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1454,7 +1454,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 257,
"weight": 262,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1555,7 +1555,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 255,
"weight": 260,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1656,7 +1656,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 256,
"weight": 261,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -4203,7 +4203,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 261,
"weight": 266,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4329,7 +4329,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 260,
"weight": 265,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4461,7 +4461,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 264,
"weight": 269,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4525,7 +4525,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 262,
"weight": 267,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -5013,7 +5013,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 263,
"weight": 268,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -5097,7 +5097,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 266,
"weight": 271,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5189,7 +5189,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 265,
"weight": 270,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5281,7 +5281,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 267,
"weight": 272,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -5994,7 +5994,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 343,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -6061,7 +6061,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 339,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -6132,7 +6132,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 340,
"weight": 337,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6195,7 +6195,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 341,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6274,7 +6274,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 342,
"weight": 339,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6339,7 +6339,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 344,
"weight": 341,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6420,7 +6420,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 294,
"weight": 299,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6524,7 +6524,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 286,
"weight": 291,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6683,7 +6683,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 287,
"weight": 292,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6786,7 +6786,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 290,
"weight": 295,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6937,7 +6937,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 288,
"weight": 293,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -7047,7 +7047,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 292,
"weight": 297,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7148,7 +7148,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 297,
"weight": 302,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7271,7 +7271,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 296,
"weight": 301,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7392,7 +7392,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 444,
"weight": 433,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7475,7 +7475,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 442,
"weight": 431,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7594,7 +7594,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 443,
"weight": 432,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7666,7 +7666,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 187,
"weight": 190,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7741,7 +7741,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 186,
"weight": 189,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8240,7 +8240,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 234,
"weight": 239,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8325,7 +8325,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 238,
"weight": 243,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -8396,7 +8396,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 540,
"weight": 529,
"cookies": false,
"type": "",
"demo": "storage\/list-files.md",
@@ -8489,7 +8489,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 538,
"weight": 527,
"cookies": false,
"type": "upload",
"demo": "storage\/create-file.md",
@@ -8580,7 +8580,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 539,
"weight": 528,
"cookies": false,
"type": "",
"demo": "storage\/get-file.md",
@@ -8651,7 +8651,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 541,
"weight": 530,
"cookies": false,
"type": "",
"demo": "storage\/update-file.md",
@@ -8742,7 +8742,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 542,
"weight": 531,
"cookies": false,
"type": "",
"demo": "storage\/delete-file.md",
@@ -8813,7 +8813,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 544,
"weight": 533,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-download.md",
@@ -8893,7 +8893,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 543,
"weight": 532,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-preview.md",
@@ -9101,7 +9101,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 545,
"weight": 534,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-view.md",
@@ -9181,7 +9181,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 416,
"weight": 405,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9251,7 +9251,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 412,
"weight": 401,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9325,7 +9325,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 413,
"weight": 402,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9391,7 +9391,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 414,
"weight": 403,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9473,7 +9473,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 415,
"weight": 404,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9541,7 +9541,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 417,
"weight": 406,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9625,7 +9625,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 408,
"weight": 397,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9728,7 +9728,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 400,
"weight": 389,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9882,7 +9882,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 401,
"weight": 390,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9984,7 +9984,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 404,
"weight": 393,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10130,7 +10130,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 402,
"weight": 391,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10239,7 +10239,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 406,
"weight": 395,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10339,7 +10339,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 411,
"weight": 400,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10461,7 +10461,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 410,
"weight": 399,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+61 -61
View File
@@ -612,7 +612,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 250,
"weight": 255,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -687,7 +687,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 252,
"weight": 257,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -811,7 +811,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 253,
"weight": 258,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -952,7 +952,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 254,
"weight": 259,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1076,7 +1076,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 258,
"weight": 263,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1213,7 +1213,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 259,
"weight": 264,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1353,7 +1353,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 251,
"weight": 256,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1454,7 +1454,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 257,
"weight": 262,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1555,7 +1555,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 255,
"weight": 260,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1656,7 +1656,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 256,
"weight": 261,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -4203,7 +4203,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 261,
"weight": 266,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4329,7 +4329,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 260,
"weight": 265,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4461,7 +4461,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 264,
"weight": 269,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4525,7 +4525,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 262,
"weight": 267,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -5013,7 +5013,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 263,
"weight": 268,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -5097,7 +5097,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 266,
"weight": 271,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5189,7 +5189,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 265,
"weight": 270,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5281,7 +5281,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 267,
"weight": 272,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -5994,7 +5994,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 343,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -6061,7 +6061,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 339,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -6132,7 +6132,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 340,
"weight": 337,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6195,7 +6195,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 341,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6274,7 +6274,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 342,
"weight": 339,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6339,7 +6339,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 344,
"weight": 341,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6420,7 +6420,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 294,
"weight": 299,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6524,7 +6524,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 286,
"weight": 291,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6683,7 +6683,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 287,
"weight": 292,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6786,7 +6786,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 290,
"weight": 295,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6937,7 +6937,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 288,
"weight": 293,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -7047,7 +7047,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 292,
"weight": 297,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7148,7 +7148,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 297,
"weight": 302,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7271,7 +7271,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 296,
"weight": 301,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7392,7 +7392,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 444,
"weight": 433,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7475,7 +7475,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 442,
"weight": 431,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7594,7 +7594,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 443,
"weight": 432,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7666,7 +7666,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 187,
"weight": 190,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7741,7 +7741,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 186,
"weight": 189,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8240,7 +8240,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 234,
"weight": 239,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8325,7 +8325,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 238,
"weight": 243,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -8396,7 +8396,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 540,
"weight": 529,
"cookies": false,
"type": "",
"demo": "storage\/list-files.md",
@@ -8489,7 +8489,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 538,
"weight": 527,
"cookies": false,
"type": "upload",
"demo": "storage\/create-file.md",
@@ -8580,7 +8580,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 539,
"weight": 528,
"cookies": false,
"type": "",
"demo": "storage\/get-file.md",
@@ -8651,7 +8651,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 541,
"weight": 530,
"cookies": false,
"type": "",
"demo": "storage\/update-file.md",
@@ -8742,7 +8742,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 542,
"weight": 531,
"cookies": false,
"type": "",
"demo": "storage\/delete-file.md",
@@ -8813,7 +8813,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 544,
"weight": 533,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-download.md",
@@ -8893,7 +8893,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 543,
"weight": 532,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-preview.md",
@@ -9101,7 +9101,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 545,
"weight": 534,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-view.md",
@@ -9181,7 +9181,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 416,
"weight": 405,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9251,7 +9251,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 412,
"weight": 401,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9325,7 +9325,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 413,
"weight": 402,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9391,7 +9391,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 414,
"weight": 403,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9473,7 +9473,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 415,
"weight": 404,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9541,7 +9541,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 417,
"weight": 406,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9625,7 +9625,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 408,
"weight": 397,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9728,7 +9728,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 400,
"weight": 389,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9882,7 +9882,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 401,
"weight": 390,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9984,7 +9984,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 404,
"weight": 393,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10130,7 +10130,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 402,
"weight": 391,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10239,7 +10239,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 406,
"weight": 395,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10339,7 +10339,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 411,
"weight": 400,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10461,7 +10461,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 410,
"weight": 399,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+40 -68
View File
@@ -20,34 +20,6 @@ function getRuntimes($runtimes, $commands, $entrypoint, $providerRootDirectory,
}));
}
class FunctionUseCases
{
public const STARTER = 'starter';
public const DATABASES = 'databases';
public const AI = 'ai';
public const MESSAGING = 'messaging';
public const UTILITIES = 'utilities';
public const DEV_TOOLS = 'dev-tools';
public const AUTH = 'auth';
/**
* @var array<string>
*/
public static function getAll(): array
{
return [
self::STARTER,
self::DATABASES,
self::AI,
self::MESSAGING,
self::UTILITIES,
self::DEV_TOOLS,
self::AUTH,
];
}
}
return [
[
'icon' => 'icon-lightning-bolt',
@@ -60,7 +32,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::STARTER],
'useCases' => ['starter'],
'runtimes' => [
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/starter', $allowList),
...getRuntimes(
@@ -101,7 +73,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DATABASES],
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -147,7 +119,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DATABASES],
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -192,7 +164,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DATABASES],
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -246,7 +218,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DATABASES],
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -285,7 +257,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DATABASES],
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -354,7 +326,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -420,7 +392,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::MESSAGING],
'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -487,7 +459,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -525,7 +497,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -576,7 +548,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::UTILITIES],
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf', $allowList)
],
@@ -599,7 +571,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DEV_TOOLS],
'useCases' => ['dev-tools'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -644,7 +616,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::UTILITIES],
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -697,7 +669,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DATABASES],
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -782,7 +754,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::DATABASES],
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -881,7 +853,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::MESSAGING],
'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -974,7 +946,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::MESSAGING],
'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1032,7 +1004,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::UTILITIES],
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1119,7 +1091,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::UTILITIES],
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1162,7 +1134,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::UTILITIES],
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1221,7 +1193,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1257,7 +1229,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1293,7 +1265,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1353,7 +1325,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1413,7 +1385,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1476,7 +1448,7 @@ return [
],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1536,7 +1508,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1573,7 +1545,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1617,7 +1589,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1660,7 +1632,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1697,7 +1669,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1762,7 +1734,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1827,7 +1799,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1884,7 +1856,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1927,7 +1899,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -1970,7 +1942,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -2014,7 +1986,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
'useCases' => [FunctionUseCases::AI],
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -2051,7 +2023,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::UTILITIES],
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -2108,7 +2080,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::UTILITIES],
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
@@ -2181,7 +2153,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
'useCases' => [FunctionUseCases::AUTH],
'useCases' => ['auth'],
'runtimes' => [
...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/sign_in_with_apple', $allowList)
],
+78 -96
View File
@@ -13,7 +13,7 @@ $hostname = $platform['consoleHostname'] ?? '';
$url = $protocol . '://' . $hostname;
class SiteUseCases
class UseCases
{
public const PORTFOLIO = 'portfolio';
public const STARTER = 'starter';
@@ -21,27 +21,9 @@ class SiteUseCases
public const ECOMMERCE = 'ecommerce';
public const DOCUMENTATION = 'documentation';
public const BLOG = 'blog';
public const AI = 'ai';
public const AI = 'artificial intelligence';
public const FORMS = 'forms';
public const DASHBOARD = 'dashboard';
/**
* @var array<string>
*/
public static function getAll(): array
{
return [
self::PORTFOLIO,
self::STARTER,
self::EVENTS,
self::ECOMMERCE,
self::DOCUMENTATION,
self::BLOG,
self::AI,
self::FORMS,
self::DASHBOARD,
];
}
}
const TEMPLATE_FRAMEWORKS = [
@@ -206,7 +188,7 @@ return [
'name' => 'Documentation template',
'tagline' => 'Modern site to store your knowledge with a clean design, full-text search, dark mode, and more.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::DOCUMENTATION],
'useCases' => [UseCases::DOCUMENTATION],
'screenshotDark' => $url . '/images/sites/templates/template-for-documentation-dark.png',
'screenshotLight' => $url . '/images/sites/templates/template-for-documentation-light.png',
'frameworks' => [
@@ -227,7 +209,7 @@ return [
// When we add Lynx with Appwrite SDK, use following tagline for it:
// 'tagline' => 'Sample application built with Lynx, a cross-platform framework focused on performance.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-lynx-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-lynx-light.png',
'frameworks' => [
@@ -238,7 +220,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -246,7 +228,7 @@ return [
'name' => 'Vitepress',
'tagline' => 'Platform for documentation and knowledge sharing powered by Vite.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::DOCUMENTATION],
'useCases' => [UseCases::DOCUMENTATION],
'screenshotDark' => $url . '/images/sites/templates/vitepress-dark.png',
'screenshotLight' => $url . '/images/sites/templates/vitepress-light.png',
'frameworks' => [
@@ -261,7 +243,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -269,7 +251,7 @@ return [
'name' => 'Vuepress',
'tagline' => 'Platform for documentation and knowledge sharing powered by Vue.',
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::DOCUMENTATION],
'useCases' => [UseCases::DOCUMENTATION],
'screenshotDark' => $url . '/images/sites/templates/vuepress-dark.png',
'screenshotLight' => $url . '/images/sites/templates/vuepress-light.png',
'frameworks' => [
@@ -284,7 +266,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -292,7 +274,7 @@ return [
'name' => 'Docusaurus',
'tagline' => 'Platform for documentation and knowledge sharing powered by React.',
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::DOCUMENTATION],
'useCases' => [UseCases::DOCUMENTATION],
'screenshotDark' => $url . '/images/sites/templates/docusaurus-dark.png',
'screenshotLight' => $url . '/images/sites/templates/docusaurus-light.png',
'frameworks' => [
@@ -307,7 +289,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -315,7 +297,7 @@ return [
'name' => 'Nxt Lnk',
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::PORTFOLIO],
'useCases' => [UseCases::PORTFOLIO],
'screenshotDark' => $url . '/images/sites/templates/nxt-lnk-dark.png',
'screenshotLight' => $url . '/images/sites/templates/nxt-lnk-light.png',
'frameworks' => [
@@ -326,7 +308,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -334,7 +316,7 @@ return [
'name' => 'Magic Portfolio',
'tagline' => 'Complex personal website to showcase your projects, articles, and more.',
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::PORTFOLIO],
'useCases' => [UseCases::PORTFOLIO],
'screenshotDark' => $url . '/images/sites/templates/magic-portfolio-dark.png',
'screenshotLight' => $url . '/images/sites/templates/magic-portfolio-light.png',
'frameworks' => [
@@ -345,7 +327,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -353,7 +335,7 @@ return [
'name' => 'LittleLink',
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::PORTFOLIO],
'useCases' => [UseCases::PORTFOLIO],
'screenshotDark' => $url . '/images/sites/templates/littlelink-dark.png',
'screenshotLight' => $url . '/images/sites/templates/littlelink-light.png',
'frameworks' => [
@@ -364,7 +346,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -372,7 +354,7 @@ return [
'name' => 'Logspot',
'tagline' => 'Website to publish changelogs of your application.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::BLOG],
'useCases' => [UseCases::BLOG],
'screenshotDark' => $url . '/images/sites/templates/logspot-dark.png',
'screenshotLight' => $url . '/images/sites/templates/logspot-light.png',
'frameworks' => [
@@ -386,7 +368,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -394,7 +376,7 @@ return [
'name' => 'Astro Nano',
'tagline' => 'Minimal personal website to showcase your projects, articles, and more.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::PORTFOLIO],
'useCases' => [UseCases::PORTFOLIO],
'screenshotDark' => $url . '/images/sites/templates/astro-nano-dark.png',
'screenshotLight' => $url . '/images/sites/templates/astro-nano-light.png',
'frameworks' => [
@@ -407,7 +389,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -415,7 +397,7 @@ return [
'name' => 'Astro Starlight',
'tagline' => 'Platform for documentation and knowledge sharing powered by Astro.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::DOCUMENTATION],
'useCases' => [UseCases::DOCUMENTATION],
'screenshotDark' => $url . '/images/sites/templates/astro-starlight-dark.png',
'screenshotLight' => $url . '/images/sites/templates/astro-starlight-light.png',
'frameworks' => [
@@ -428,7 +410,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -436,7 +418,7 @@ return [
'name' => 'Astro Sphere',
'tagline' => 'Modern personal website to showcase your projects, articles, and more.',
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::PORTFOLIO],
'useCases' => [UseCases::PORTFOLIO],
'screenshotDark' => $url . '/images/sites/templates/astro-sphere-dark.png',
'screenshotLight' => $url . '/images/sites/templates/astro-sphere-light.png',
'frameworks' => [
@@ -449,7 +431,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -457,7 +439,7 @@ return [
'name' => 'Astro Starlog',
'tagline' => 'Platform for publishing written content and media powered by Astro.',
'score' => 5, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::BLOG],
'useCases' => [UseCases::BLOG],
'screenshotDark' => $url . '/images/sites/templates/astro-starlog-dark.png',
'screenshotLight' => $url . '/images/sites/templates/astro-starlog-light.png',
'frameworks' => [
@@ -470,7 +452,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -478,7 +460,7 @@ return [
'name' => 'Onelink',
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::PORTFOLIO],
'useCases' => [UseCases::PORTFOLIO],
'screenshotDark' => $url . '/images/sites/templates/onelink-dark.png',
'screenshotLight' => $url . '/images/sites/templates/onelink-light.png',
'frameworks' => [
@@ -492,13 +474,13 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
'key' => 'starter-for-flutter',
'name' => 'Flutter starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Flutter application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-flutter-dark.png',
@@ -543,7 +525,7 @@ return [
[
'key' => 'starter-for-js',
'name' => 'JavaScript starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple JavaScript application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-js-dark.png',
@@ -587,7 +569,7 @@ return [
[
'key' => 'starter-for-angular',
'name' => 'Angular starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Angular application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-angular-dark.png',
@@ -633,7 +615,7 @@ return [
[
'key' => 'starter-for-astro',
'name' => 'Astro starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Astro application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-astro-dark.png',
@@ -678,7 +660,7 @@ return [
[
'key' => 'starter-for-analog',
'name' => 'Analog starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Analog application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-analog-dark.png',
@@ -722,7 +704,7 @@ return [
[
'key' => 'starter-for-remix',
'name' => 'Remix starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Remix application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-remix-dark.png',
@@ -766,7 +748,7 @@ return [
[
'key' => 'starter-for-svelte',
'name' => 'Svelte starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Svelte application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-svelte-dark.png',
@@ -810,7 +792,7 @@ return [
[
'key' => 'starter-for-react',
'name' => 'React starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple React application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-dark.png',
@@ -854,7 +836,7 @@ return [
[
'key' => 'starter-for-vue',
'name' => 'Vue starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Vue application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-vue-dark.png',
@@ -898,7 +880,7 @@ return [
[
'key' => 'starter-for-react-native',
'name' => 'React Native starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple React Native application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-native-dark.png',
@@ -942,7 +924,7 @@ return [
[
'key' => 'starter-for-nextjs',
'name' => 'Next.js starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Next.js application integrated with Appwrite SDK.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-nextjs-dark.png',
@@ -986,7 +968,7 @@ return [
[
'key' => 'starter-for-tanstack-start',
'name' => 'TanStack Start starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple TanStack Start application integrated with Appwrite SDK.',
'score' => 9, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-tanstack-start-dark.png',
@@ -1030,7 +1012,7 @@ return [
[
'key' => 'starter-for-nuxt',
'name' => 'Nuxt starter',
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple Nuxt application integrated with Appwrite SDK.',
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-nuxt-dark.png',
@@ -1076,7 +1058,7 @@ return [
'name' => 'Event template',
'tagline' => 'Hackathon landing page with support for project submissions.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::EVENTS],
'useCases' => [UseCases::EVENTS],
'screenshotDark' => $url . '/images/sites/templates/template-for-event-dark.png',
'screenshotLight' => $url . '/images/sites/templates/template-for-event-light.png',
'frameworks' => [
@@ -1114,7 +1096,7 @@ return [
'name' => 'Portfolio template',
'tagline' => 'Simple personal website to showcase your projects, articles, and more.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::PORTFOLIO],
'useCases' => [UseCases::PORTFOLIO],
'screenshotDark' => $url . '/images/sites/templates/template-for-portfolio-dark.png',
'screenshotLight' => $url . '/images/sites/templates/template-for-portfolio-light.png',
'frameworks' => [
@@ -1133,7 +1115,7 @@ return [
'name' => 'Store template',
'tagline' => 'E-commerce platform for selling products with Stripe integration.',
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::ECOMMERCE],
'useCases' => [UseCases::ECOMMERCE],
'screenshotDark' => $url . '/images/sites/templates/template-for-store-dark.png',
'screenshotLight' => $url . '/images/sites/templates/template-for-store-light.png',
'frameworks' => [
@@ -1177,7 +1159,7 @@ return [
'name' => 'Blog template',
'tagline' => 'Platform for publishing written content and media.',
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::BLOG],
'useCases' => [UseCases::BLOG],
'screenshotDark' => $url . '/images/sites/templates/template-for-blog-dark.png',
'screenshotLight' => $url . '/images/sites/templates/template-for-blog-light.png',
'frameworks' => [
@@ -1196,7 +1178,7 @@ return [
'name' => 'Astro playground',
'tagline' => 'A basic Astro website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-astro-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-astro-light.png',
'frameworks' => [
@@ -1207,7 +1189,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1215,7 +1197,7 @@ return [
'name' => 'Remix playground',
'tagline' => 'A basic Remix website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-remix-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-remix-light.png',
'frameworks' => [
@@ -1226,7 +1208,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1234,7 +1216,7 @@ return [
'name' => 'Next.js playground',
'tagline' => 'A basic Next.js website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-nextjs-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-nextjs-light.png',
'frameworks' => [
@@ -1245,7 +1227,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1253,7 +1235,7 @@ return [
'name' => 'Flutter playground',
'tagline' => 'A basic Flutter website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-flutter-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-flutter-light.png',
'frameworks' => [
@@ -1264,7 +1246,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1272,7 +1254,7 @@ return [
'name' => 'Vite playground',
'tagline' => 'A basic Vite website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-vite-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-vite-light.png',
'frameworks' => [
@@ -1283,7 +1265,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1291,7 +1273,7 @@ return [
'name' => 'Angular playground',
'tagline' => 'A basic Angular website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-angular-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-angular-light.png',
'frameworks' => [
@@ -1303,7 +1285,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1311,7 +1293,7 @@ return [
'name' => 'Analog playground',
'tagline' => 'A basic Analog website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-analog-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-analog-light.png',
'frameworks' => [
@@ -1322,7 +1304,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1330,7 +1312,7 @@ return [
'name' => 'Svelte playground',
'tagline' => 'A basic Svelte website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-svelte-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-svelte-light.png',
'frameworks' => [
@@ -1341,7 +1323,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
@@ -1350,7 +1332,7 @@ return [
'name' => 'React playground',
'tagline' => 'A basic React website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-react-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-react-light.png',
'frameworks' => [
@@ -1362,7 +1344,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
@@ -1371,7 +1353,7 @@ return [
'name' => 'Vue playground',
'tagline' => 'A basic Vue website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-vue-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-vue-light.png',
'frameworks' => [
@@ -1382,7 +1364,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1390,7 +1372,7 @@ return [
'name' => 'Nuxt playground',
'tagline' => 'A basic Nuxt website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-nuxt-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-nuxt-light.png',
'frameworks' => [
@@ -1401,7 +1383,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1409,7 +1391,7 @@ return [
'name' => 'TanStack Start playground',
'tagline' => 'A basic TanStack Start website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-tanstack-start-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-tanstack-start-light.png',
'frameworks' => [
@@ -1420,7 +1402,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.5.*',
'variables' => [],
],
[
@@ -1428,7 +1410,7 @@ return [
'name' => 'React Native playground',
'tagline' => 'A basic React Native website without Appwrite SDK integration.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/playground-for-react-native-dark.png',
'screenshotLight' => $url . '/images/sites/templates/playground-for-react-native-light.png',
'frameworks' => [
@@ -1439,7 +1421,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => [],
],
[
@@ -1447,7 +1429,7 @@ return [
'name' => 'Lynx gallery',
'tagline' => 'A Lynx website showcasing gallery with smooth animations.',
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::STARTER],
'useCases' => [UseCases::STARTER],
'screenshotDark' => $url . '/images/sites/templates/gallery-for-lynx-dark.png',
'screenshotLight' => $url . '/images/sites/templates/gallery-for-lynx-light.png',
'frameworks' => [
@@ -1458,7 +1440,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.3.*',
'variables' => []
],
[
@@ -1466,7 +1448,7 @@ return [
'name' => 'Text-to-speech with ElevenLabs',
'tagline' => 'Next.js app that transforms text into natural, human-like speech using ElevenLabs',
'score' => 10, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::AI],
'useCases' => [UseCases::AI],
'screenshotDark' => $url . '/images/sites/templates/text-to-speech-dark.png',
'screenshotLight' => $url . '/images/sites/templates/text-to-speech-light.png',
'frameworks' => [
@@ -1477,7 +1459,7 @@ return [
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.7.*',
'providerVersion' => '0.6.*',
'variables' => [
[
'name' => 'ELEVENLABS_API_KEY',
@@ -1494,7 +1476,7 @@ return [
'name' => 'CRM dashboard with React Admin',
'tagline' => 'A React-based admin dashboard template with CRM features.',
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::DASHBOARD],
'useCases' => [UseCases::DASHBOARD],
'screenshotDark' => $url . '/images/sites/templates/crm-dashboard-react-admin-dark.png',
'screenshotLight' => $url . '/images/sites/templates/crm-dashboard-react-admin-light.png',
'frameworks' => [
@@ -1597,7 +1579,7 @@ return [
'name' => 'Job applications form with Formspree',
'tagline' => 'A simple form submission template using Formspree.',
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [SiteUseCases::FORMS],
'useCases' => [UseCases::FORMS],
'screenshotDark' => $url . '/images/sites/templates/job-applications-formspree-dark.png',
'screenshotLight' => $url . '/images/sites/templates/job-applications-formspree-light.png',
'frameworks' => [
+57 -71
View File
@@ -36,6 +36,7 @@ use Appwrite\Utopia\Response;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberUtil;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Auth\Hashes\Sha;
use Utopia\Auth\Proofs\Code as ProofsCode;
@@ -60,7 +61,6 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Emails\Email;
use Utopia\Http;
use Utopia\Locale\Locale;
use Utopia\Storage\Validator\FileName;
use Utopia\System\System;
@@ -195,7 +195,7 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, arr
->setPreview($preview)
->setBody($body)
->setBodyTemplate($bodyTemplate)
->appendVariables($emailVariables)
->setVariables($emailVariables)
->setRecipient($email);
// since this is console project, set email sender name!
@@ -344,7 +344,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$response->dynamic($session, Response::MODEL_SESSION);
};
Http::post('/v1/account')
App::post('/v1/account')
->desc('Create account')
->groups(['api', 'account', 'auth'])
->label('scope', 'sessions.write')
@@ -507,7 +507,7 @@ Http::post('/v1/account')
->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::get('/v1/account')
App::get('/v1/account')
->desc('Get account')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -535,7 +535,7 @@ Http::get('/v1/account')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::delete('/v1/account')
App::delete('/v1/account')
->desc('Delete account')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -590,7 +590,7 @@ Http::delete('/v1/account')
$response->noContent();
});
Http::get('/v1/account/sessions')
App::get('/v1/account/sessions')
->desc('List sessions')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -635,7 +635,7 @@ Http::get('/v1/account/sessions')
]), Response::MODEL_SESSION_LIST);
});
Http::delete('/v1/account/sessions')
App::delete('/v1/account/sessions')
->desc('Delete sessions')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -710,7 +710,7 @@ Http::delete('/v1/account/sessions')
$response->noContent();
});
Http::get('/v1/account/sessions/:sessionId')
App::get('/v1/account/sessions/:sessionId')
->desc('Get session')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -758,7 +758,7 @@ Http::get('/v1/account/sessions/:sessionId')
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
});
Http::delete('/v1/account/sessions/:sessionId')
App::delete('/v1/account/sessions/:sessionId')
->desc('Delete session')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
@@ -845,7 +845,7 @@ Http::delete('/v1/account/sessions/:sessionId')
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
});
Http::patch('/v1/account/sessions/:sessionId')
App::patch('/v1/account/sessions/:sessionId')
->desc('Update session')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -934,7 +934,7 @@ Http::patch('/v1/account/sessions/:sessionId')
return $response->dynamic($session, Response::MODEL_SESSION);
});
Http::post('/v1/account/sessions/email')
App::post('/v1/account/sessions/email')
->alias('/v1/account/sessions')
->desc('Create email password session')
->groups(['api', 'account', 'auth', 'session'])
@@ -1086,7 +1086,7 @@ Http::post('/v1/account/sessions/email')
$response->dynamic($session, Response::MODEL_SESSION);
});
Http::post('/v1/account/sessions/anonymous')
App::post('/v1/account/sessions/anonymous')
->desc('Create anonymous session')
->groups(['api', 'account', 'auth', 'session'])
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -1237,7 +1237,7 @@ Http::post('/v1/account/sessions/anonymous')
$response->dynamic($session, Response::MODEL_SESSION);
});
Http::post('/v1/account/sessions/token')
App::post('/v1/account/sessions/token')
->desc('Create session')
->label('event', 'users.[userId].sessions.[sessionId].create')
->groups(['api', 'account', 'session'])
@@ -1280,7 +1280,7 @@ Http::post('/v1/account/sessions/token')
->inject('authorization')
->action($createSession);
Http::get('/v1/account/sessions/oauth2/:provider')
App::get('/v1/account/sessions/oauth2/:provider')
->desc('Create OAuth2 session')
->groups(['api', 'account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -1374,7 +1374,7 @@ Http::get('/v1/account/sessions/oauth2/:provider')
->redirect($oauth2->getLoginURL());
});
Http::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('Get OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -1409,7 +1409,7 @@ Http::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
. \http_build_query($params));
});
Http::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('Create OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -1445,7 +1445,7 @@ Http::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
. \http_build_query($params));
});
Http::get('/v1/account/sessions/oauth2/:provider/redirect')
App::get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('Get OAuth2 redirect')
->groups(['api', 'account', 'session'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -1469,14 +1469,13 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('devKey')
->inject('user')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('geodb')
->inject('queueForEvents')
->inject('store')
->inject('proofForPassword')
->inject('proofForToken')
->inject('authorization')
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Database $dbForPlatform, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) {
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
@@ -1513,25 +1512,6 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
$state = $defaultState;
}
// Allow redirect to rule URL if related to project
//Check if $redirectValidator is instance of Redirect class
if ($redirectValidator instanceof Redirect) {
$rules = $authorization->skip(fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [
parse_url($state['success'], PHP_URL_HOST),
parse_url($state['failure'], PHP_URL_HOST)
]),
Query::equal('projectInternalId', [$project->getSequence()]),
Query::limit(2)
]));
foreach ($rules as $rule) {
$allowedHostnames = $redirectValidator->getAllowedHostnames();
$allowedHostnames[] = $rule->getAttribute('domain', '');
$redirectValidator->setAllowedHostnames($allowedHostnames);
}
}
if ($devKey->isEmpty() && !$redirectValidator->isValid($state['success'])) {
throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL);
}
@@ -1968,7 +1948,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
;
});
Http::get('/v1/account/tokens/oauth2/:provider')
App::get('/v1/account/tokens/oauth2/:provider')
->desc('Create OAuth2 token')
->groups(['api', 'account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -2063,7 +2043,7 @@ Http::get('/v1/account/tokens/oauth2/:provider')
->redirect($oauth2->getLoginURL());
});
Http::post('/v1/account/tokens/magic-url')
App::post('/v1/account/tokens/magic-url')
->alias('/v1/account/sessions/magic-url')
->desc('Create magic URL token')
->groups(['api', 'account', 'auth'])
@@ -2321,7 +2301,7 @@ Http::post('/v1/account/tokens/magic-url')
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->appendVariables($emailVariables)
->setVariables($emailVariables)
->setRecipient($email);
if ($project->getId() === 'console') {
@@ -2344,7 +2324,7 @@ Http::post('/v1/account/tokens/magic-url')
->dynamic($token, Response::MODEL_TOKEN);
});
Http::post('/v1/account/tokens/email')
App::post('/v1/account/tokens/email')
->desc('Create email token (OTP)')
->groups(['api', 'account', 'auth'])
->label('scope', 'sessions.write')
@@ -2622,7 +2602,7 @@ Http::post('/v1/account/tokens/email')
->setPreview($preview)
->setBody($body)
->setBodyTemplate($bodyTemplate)
->appendVariables($emailVariables)
->setVariables($emailVariables)
->setRecipient($email);
// since this is console project, set email sender name!
@@ -2646,7 +2626,7 @@ Http::post('/v1/account/tokens/email')
->dynamic($token, Response::MODEL_TOKEN);
});
Http::put('/v1/account/sessions/magic-url')
App::put('/v1/account/sessions/magic-url')
->desc('Update magic URL session')
->label('event', 'users.[userId].sessions.[sessionId].create')
->groups(['api', 'account', 'session'])
@@ -2696,7 +2676,7 @@ Http::put('/v1/account/sessions/magic-url')
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode, $authorization);
});
Http::put('/v1/account/sessions/phone')
App::put('/v1/account/sessions/phone')
->desc('Update phone session')
->label('event', 'users.[userId].sessions.[sessionId].create')
->groups(['api', 'account', 'session'])
@@ -2742,7 +2722,7 @@ Http::put('/v1/account/sessions/phone')
->inject('authorization')
->action($createSession);
Http::post('/v1/account/tokens/phone')
App::post('/v1/account/tokens/phone')
->alias('/v1/account/sessions/phone')
->desc('Create phone token')
->groups(['api', 'account', 'auth'])
@@ -2962,7 +2942,7 @@ Http::post('/v1/account/tokens/phone')
->dynamic($token, Response::MODEL_TOKEN);
});
Http::post('/v1/account/jwts')
App::post('/v1/account/jwts')
->alias('/v1/account/jwt')
->desc('Create JWT')
->groups(['api', 'account', 'auth'])
@@ -3009,7 +2989,7 @@ Http::post('/v1/account/jwts')
]), Response::MODEL_JWT);
});
Http::get('/v1/account/prefs')
App::get('/v1/account/prefs')
->desc('Get account preferences')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -3036,7 +3016,7 @@ Http::get('/v1/account/prefs')
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
});
Http::get('/v1/account/logs')
App::get('/v1/account/logs')
->desc('List logs')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -3107,7 +3087,7 @@ Http::get('/v1/account/logs')
]), Response::MODEL_LOG_LIST);
});
Http::patch('/v1/account/name')
App::patch('/v1/account/name')
->desc('Update name')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.name')
@@ -3144,7 +3124,7 @@ Http::patch('/v1/account/name')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::patch('/v1/account/password')
App::patch('/v1/account/password')
->desc('Update password')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.password')
@@ -3237,7 +3217,7 @@ Http::patch('/v1/account/password')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::patch('/v1/account/email')
App::patch('/v1/account/email')
->desc('Update email')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.email')
@@ -3349,7 +3329,7 @@ Http::patch('/v1/account/email')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::patch('/v1/account/phone')
App::patch('/v1/account/phone')
->desc('Update phone')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.phone')
@@ -3438,7 +3418,7 @@ Http::patch('/v1/account/phone')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::patch('/v1/account/prefs')
App::patch('/v1/account/prefs')
->desc('Update preferences')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.prefs')
@@ -3476,7 +3456,7 @@ Http::patch('/v1/account/prefs')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::patch('/v1/account/status')
App::patch('/v1/account/status')
->desc('Update status')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.status')
@@ -3526,7 +3506,7 @@ Http::patch('/v1/account/status')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
Http::post('/v1/account/recovery')
App::post('/v1/account/recovery')
->desc('Create password recovery')
->groups(['api', 'account'])
->label('scope', 'sessions.write')
@@ -3698,7 +3678,7 @@ Http::post('/v1/account/recovery')
->setRecipient($profile->getAttribute('email', ''))
->setName($profile->getAttribute('name', ''))
->setBody($body)
->appendVariables($emailVariables)
->setVariables($emailVariables)
->setSubject($subject)
->setPreview($preview);
@@ -3721,7 +3701,7 @@ Http::post('/v1/account/recovery')
->dynamic($recovery, Response::MODEL_TOKEN);
});
Http::put('/v1/account/recovery')
App::put('/v1/account/recovery')
->desc('Update password recovery (confirmation)')
->groups(['api', 'account'])
->label('scope', 'sessions.write')
@@ -3818,7 +3798,7 @@ Http::put('/v1/account/recovery')
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
});
Http::post('/v1/account/verifications/email')
App::post('/v1/account/verifications/email')
->alias('/v1/account/verification')
->desc('Create email verification')
->groups(['api', 'account'])
@@ -4029,7 +4009,7 @@ Http::post('/v1/account/verifications/email')
->setPreview($preview)
->setBody($body)
->setBodyTemplate($bodyTemplate)
->appendVariables($emailVariables)
->setVariables($emailVariables)
->setRecipient($user->getAttribute('email'))
->setName($user->getAttribute('name') ?? '');
@@ -4051,7 +4031,7 @@ Http::post('/v1/account/verifications/email')
->dynamic($verification, Response::MODEL_TOKEN);
});
Http::put('/v1/account/verifications/email')
App::put('/v1/account/verifications/email')
->alias('/v1/account/verification')
->desc('Update email verification (confirmation)')
->groups(['api', 'account'])
@@ -4141,7 +4121,7 @@ Http::put('/v1/account/verifications/email')
$response->dynamic($verification, Response::MODEL_TOKEN);
});
Http::post('/v1/account/verifications/phone')
App::post('/v1/account/verifications/phone')
->alias('/v1/account/verification/phone')
->desc('Create phone verification')
->groups(['api', 'account', 'auth'])
@@ -4288,7 +4268,7 @@ Http::post('/v1/account/verifications/phone')
->dynamic($verification, Response::MODEL_TOKEN);
});
Http::put('/v1/account/verifications/phone')
App::put('/v1/account/verifications/phone')
->alias('/v1/account/verification/phone')
->desc('Update phone verification (confirmation)')
->groups(['api', 'account'])
@@ -4356,7 +4336,7 @@ Http::put('/v1/account/verifications/phone')
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
});
Http::post('/v1/account/targets/push')
App::post('/v1/account/targets/push')
->desc('Create push target')
->groups(['api', 'account'])
->label('scope', 'targets.write')
@@ -4440,7 +4420,7 @@ Http::post('/v1/account/targets/push')
->dynamic($target, Response::MODEL_TARGET);
});
Http::put('/v1/account/targets/:targetId/push')
App::put('/v1/account/targets/:targetId/push')
->desc('Update push target')
->groups(['api', 'account'])
->label('scope', 'targets.write')
@@ -4506,7 +4486,7 @@ Http::put('/v1/account/targets/:targetId/push')
->dynamic($target, Response::MODEL_TARGET);
});
Http::delete('/v1/account/targets/:targetId/push')
App::delete('/v1/account/targets/:targetId/push')
->desc('Delete push target')
->groups(['api', 'account'])
->label('scope', 'targets.write')
@@ -4561,7 +4541,7 @@ Http::delete('/v1/account/targets/:targetId/push')
$response->noContent();
});
Http::get('/v1/account/identities')
App::get('/v1/account/identities')
->desc('List identities')
->groups(['api', 'account'])
->label('scope', 'account')
@@ -4594,10 +4574,16 @@ Http::get('/v1/account/identities')
$queries[] = Query::equal('userInternalId', [$user->getSequence()]);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -4627,7 +4613,7 @@ Http::get('/v1/account/identities')
]), Response::MODEL_IDENTITY_LIST);
});
Http::delete('/v1/account/identities/:identityId')
App::delete('/v1/account/identities/:identityId')
->desc('Delete identity')
->groups(['api', 'account'])
->label('scope', 'account')
+7 -7
View File
@@ -18,14 +18,14 @@ use GraphQL\Validator\Rules\DisableIntrospection;
use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\Rules\QueryDepth;
use Swoole\Coroutine\WaitGroup;
use Utopia\App;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Http;
use Utopia\System\System;
use Utopia\Validator\JSON;
use Utopia\Validator\Text;
Http::init()
App::init()
->groups(['graphql'])
->inject('project')
->inject('authorization')
@@ -39,7 +39,7 @@ Http::init()
}
});
Http::get('/v1/graphql')
App::get('/v1/graphql')
->desc('GraphQL endpoint')
->groups(['graphql'])
->label('scope', 'graphql')
@@ -86,7 +86,7 @@ Http::get('/v1/graphql')
->json($output);
});
Http::post('/v1/graphql/mutation')
App::post('/v1/graphql/mutation')
->desc('GraphQL endpoint')
->groups(['graphql'])
->label('scope', 'graphql')
@@ -137,7 +137,7 @@ Http::post('/v1/graphql/mutation')
->json($output);
});
Http::post('/v1/graphql')
App::post('/v1/graphql')
->desc('GraphQL endpoint')
->groups(['graphql'])
->label('scope', 'graphql')
@@ -232,7 +232,7 @@ function execute(
$validations[] = new QueryComplexity($maxComplexity);
$validations[] = new QueryDepth($maxDepth);
}
if (Http::getMode() === Http::MODE_TYPE_PRODUCTION) {
if (App::getMode() === App::MODE_TYPE_PRODUCTION) {
$flags = DebugFlag::NONE;
}
@@ -333,7 +333,7 @@ function processResult($result, $debugFlags): array
);
}
Http::shutdown()
App::shutdown()
->groups(['schema'])
->inject('project')
->action(function (Document $project) {
+9 -9
View File
@@ -6,12 +6,12 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Http;
use Utopia\Locale\Locale;
Http::get('/v1/locale')
App::get('/v1/locale')
->desc('Get user locale')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
@@ -70,7 +70,7 @@ Http::get('/v1/locale')
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
});
Http::get('/v1/locale/codes')
App::get('/v1/locale/codes')
->desc('List locale codes')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
@@ -96,7 +96,7 @@ Http::get('/v1/locale/codes')
]), Response::MODEL_LOCALE_CODE_LIST);
});
Http::get('/v1/locale/countries')
App::get('/v1/locale/countries')
->desc('List countries')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
@@ -133,7 +133,7 @@ Http::get('/v1/locale/countries')
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
});
Http::get('/v1/locale/countries/eu')
App::get('/v1/locale/countries/eu')
->desc('List EU countries')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
@@ -172,7 +172,7 @@ Http::get('/v1/locale/countries/eu')
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
});
Http::get('/v1/locale/countries/phones')
App::get('/v1/locale/countries/phones')
->desc('List countries phone codes')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
@@ -210,7 +210,7 @@ Http::get('/v1/locale/countries/phones')
$response->dynamic(new Document(['phones' => $output, 'total' => \count($output)]), Response::MODEL_PHONE_LIST);
});
Http::get('/v1/locale/continents')
App::get('/v1/locale/continents')
->desc('List continents')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
@@ -246,7 +246,7 @@ Http::get('/v1/locale/continents')
$response->dynamic(new Document(['continents' => $output, 'total' => \count($output)]), Response::MODEL_CONTINENT_LIST);
});
Http::get('/v1/locale/currencies')
App::get('/v1/locale/currencies')
->desc('List currencies')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
@@ -273,7 +273,7 @@ Http::get('/v1/locale/currencies')
});
Http::get('/v1/locale/languages')
App::get('/v1/locale/languages')
->desc('List languages')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
+89 -64
View File
@@ -25,6 +25,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Targets;
use Appwrite\Utopia\Database\Validator\Queries\Topics;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -43,7 +44,6 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\Roles;
use Utopia\Database\Validator\UID;
use Utopia\Http;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
@@ -57,7 +57,7 @@ use Utopia\Validator\WhiteList;
use function Swoole\Coroutine\batch;
Http::post('/v1/messaging/providers/mailgun')
App::post('/v1/messaging/providers/mailgun')
->desc('Create Mailgun provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -151,7 +151,7 @@ Http::post('/v1/messaging/providers/mailgun')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/sendgrid')
App::post('/v1/messaging/providers/sendgrid')
->desc('Create Sendgrid provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -233,7 +233,7 @@ Http::post('/v1/messaging/providers/sendgrid')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/resend')
App::post('/v1/messaging/providers/resend')
->desc('Create Resend provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -315,7 +315,7 @@ Http::post('/v1/messaging/providers/resend')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/smtp')
App::post('/v1/messaging/providers/smtp')
->desc('Create SMTP provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -430,7 +430,7 @@ Http::post('/v1/messaging/providers/smtp')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/msg91')
App::post('/v1/messaging/providers/msg91')
->desc('Create Msg91 provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -513,7 +513,7 @@ Http::post('/v1/messaging/providers/msg91')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/telesign')
App::post('/v1/messaging/providers/telesign')
->desc('Create Telesign provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -597,7 +597,7 @@ Http::post('/v1/messaging/providers/telesign')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/textmagic')
App::post('/v1/messaging/providers/textmagic')
->desc('Create Textmagic provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -681,7 +681,7 @@ Http::post('/v1/messaging/providers/textmagic')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/twilio')
App::post('/v1/messaging/providers/twilio')
->desc('Create Twilio provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -765,7 +765,7 @@ Http::post('/v1/messaging/providers/twilio')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/vonage')
App::post('/v1/messaging/providers/vonage')
->desc('Create Vonage provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -849,7 +849,7 @@ Http::post('/v1/messaging/providers/vonage')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/fcm')
App::post('/v1/messaging/providers/fcm')
->desc('Create FCM provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -939,7 +939,7 @@ Http::post('/v1/messaging/providers/fcm')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::post('/v1/messaging/providers/apns')
App::post('/v1/messaging/providers/apns')
->desc('Create APNS provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
@@ -1052,7 +1052,7 @@ Http::post('/v1/messaging/providers/apns')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::get('/v1/messaging/providers')
App::get('/v1/messaging/providers')
->desc('List providers')
->groups(['api', 'messaging'])
->label('scope', 'providers.read')
@@ -1087,10 +1087,15 @@ Http::get('/v1/messaging/providers')
$queries[] = Query::search('search', $search);
}
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor !== false) {
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -1117,7 +1122,7 @@ Http::get('/v1/messaging/providers')
]), Response::MODEL_PROVIDER_LIST);
});
Http::get('/v1/messaging/providers/:providerId/logs')
App::get('/v1/messaging/providers/:providerId/logs')
->desc('List provider logs')
->groups(['api', 'messaging'])
->label('scope', 'providers.read')
@@ -1213,7 +1218,7 @@ Http::get('/v1/messaging/providers/:providerId/logs')
]), Response::MODEL_LOG_LIST);
});
Http::get('/v1/messaging/providers/:providerId')
App::get('/v1/messaging/providers/:providerId')
->desc('Get provider')
->groups(['api', 'messaging'])
->label('scope', 'providers.read')
@@ -1244,7 +1249,7 @@ Http::get('/v1/messaging/providers/:providerId')
$response->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/mailgun/:providerId')
App::patch('/v1/messaging/providers/mailgun/:providerId')
->desc('Update Mailgun provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -1357,7 +1362,7 @@ Http::patch('/v1/messaging/providers/mailgun/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/sendgrid/:providerId')
App::patch('/v1/messaging/providers/sendgrid/:providerId')
->desc('Update Sendgrid provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -1455,7 +1460,7 @@ Http::patch('/v1/messaging/providers/sendgrid/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/resend/:providerId')
App::patch('/v1/messaging/providers/resend/:providerId')
->desc('Update Resend provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -1553,7 +1558,7 @@ Http::patch('/v1/messaging/providers/resend/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/smtp/:providerId')
App::patch('/v1/messaging/providers/smtp/:providerId')
->desc('Update SMTP provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -1702,7 +1707,7 @@ Http::patch('/v1/messaging/providers/smtp/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/msg91/:providerId')
App::patch('/v1/messaging/providers/msg91/:providerId')
->desc('Update Msg91 provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -1789,7 +1794,7 @@ Http::patch('/v1/messaging/providers/msg91/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/telesign/:providerId')
App::patch('/v1/messaging/providers/telesign/:providerId')
->desc('Update Telesign provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -1878,7 +1883,7 @@ Http::patch('/v1/messaging/providers/telesign/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/textmagic/:providerId')
App::patch('/v1/messaging/providers/textmagic/:providerId')
->desc('Update Textmagic provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -1967,7 +1972,7 @@ Http::patch('/v1/messaging/providers/textmagic/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/twilio/:providerId')
App::patch('/v1/messaging/providers/twilio/:providerId')
->desc('Update Twilio provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -2056,7 +2061,7 @@ Http::patch('/v1/messaging/providers/twilio/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/vonage/:providerId')
App::patch('/v1/messaging/providers/vonage/:providerId')
->desc('Update Vonage provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -2145,7 +2150,7 @@ Http::patch('/v1/messaging/providers/vonage/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::patch('/v1/messaging/providers/fcm/:providerId')
App::patch('/v1/messaging/providers/fcm/:providerId')
->desc('Update FCM provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -2241,7 +2246,7 @@ Http::patch('/v1/messaging/providers/fcm/:providerId')
});
Http::patch('/v1/messaging/providers/apns/:providerId')
App::patch('/v1/messaging/providers/apns/:providerId')
->desc('Update APNS provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
@@ -2363,7 +2368,7 @@ Http::patch('/v1/messaging/providers/apns/:providerId')
->dynamic($provider, Response::MODEL_PROVIDER);
});
Http::delete('/v1/messaging/providers/:providerId')
App::delete('/v1/messaging/providers/:providerId')
->desc('Delete provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.delete')
@@ -2406,7 +2411,7 @@ Http::delete('/v1/messaging/providers/:providerId')
->noContent();
});
Http::post('/v1/messaging/topics')
App::post('/v1/messaging/topics')
->desc('Create topic')
->groups(['api', 'messaging'])
->label('audits.event', 'topic.create')
@@ -2456,7 +2461,7 @@ Http::post('/v1/messaging/topics')
->dynamic($topic, Response::MODEL_TOPIC);
});
Http::get('/v1/messaging/topics')
App::get('/v1/messaging/topics')
->desc('List topics')
->groups(['api', 'messaging'])
->label('scope', 'topics.read')
@@ -2491,10 +2496,15 @@ Http::get('/v1/messaging/topics')
$queries[] = Query::search('search', $search);
}
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor !== false) {
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -2521,7 +2531,7 @@ Http::get('/v1/messaging/topics')
]), Response::MODEL_TOPIC_LIST);
});
Http::get('/v1/messaging/topics/:topicId/logs')
App::get('/v1/messaging/topics/:topicId/logs')
->desc('List topic logs')
->groups(['api', 'messaging'])
->label('scope', 'topics.read')
@@ -2618,7 +2628,7 @@ Http::get('/v1/messaging/topics/:topicId/logs')
]), Response::MODEL_LOG_LIST);
});
Http::get('/v1/messaging/topics/:topicId')
App::get('/v1/messaging/topics/:topicId')
->desc('Get topic')
->groups(['api', 'messaging'])
->label('scope', 'topics.read')
@@ -2650,7 +2660,7 @@ Http::get('/v1/messaging/topics/:topicId')
->dynamic($topic, Response::MODEL_TOPIC);
});
Http::patch('/v1/messaging/topics/:topicId')
App::patch('/v1/messaging/topics/:topicId')
->desc('Update topic')
->groups(['api', 'messaging'])
->label('audits.event', 'topic.update')
@@ -2701,7 +2711,7 @@ Http::patch('/v1/messaging/topics/:topicId')
->dynamic($topic, Response::MODEL_TOPIC);
});
Http::delete('/v1/messaging/topics/:topicId')
App::delete('/v1/messaging/topics/:topicId')
->desc('Delete topic')
->groups(['api', 'messaging'])
->label('audits.event', 'topic.delete')
@@ -2749,7 +2759,7 @@ Http::delete('/v1/messaging/topics/:topicId')
->noContent();
});
Http::post('/v1/messaging/topics/:topicId/subscribers')
App::post('/v1/messaging/topics/:topicId/subscribers')
->desc('Create subscriber')
->groups(['api', 'messaging'])
->label('audits.event', 'subscriber.create')
@@ -2850,7 +2860,7 @@ Http::post('/v1/messaging/topics/:topicId/subscribers')
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
});
Http::get('/v1/messaging/topics/:topicId/subscribers')
App::get('/v1/messaging/topics/:topicId/subscribers')
->desc('List subscribers')
->groups(['api', 'messaging'])
->label('scope', 'subscribers.read')
@@ -2894,10 +2904,15 @@ Http::get('/v1/messaging/topics/:topicId/subscribers')
$queries[] = Query::equal('topicInternalId', [$topic->getSequence()]);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor !== false) {
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -2936,7 +2951,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers')
]), Response::MODEL_SUBSCRIBER_LIST);
});
Http::get('/v1/messaging/subscribers/:subscriberId/logs')
App::get('/v1/messaging/subscribers/:subscriberId/logs')
->desc('List subscriber logs')
->groups(['api', 'messaging'])
->label('scope', 'subscribers.read')
@@ -3033,7 +3048,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs')
]), Response::MODEL_LOG_LIST);
});
Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->desc('Get subscriber')
->groups(['api', 'messaging'])
->label('scope', 'subscribers.read')
@@ -3080,7 +3095,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
});
Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->desc('Delete subscriber')
->groups(['api', 'messaging'])
->label('audits.event', 'subscriber.delete')
@@ -3148,7 +3163,7 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->noContent();
});
Http::post('/v1/messaging/messages/email')
App::post('/v1/messaging/messages/email')
->desc('Create email')
->groups(['api', 'messaging'])
->label('audits.event', 'message.create')
@@ -3307,7 +3322,7 @@ Http::post('/v1/messaging/messages/email')
->dynamic($message, Response::MODEL_MESSAGE);
});
Http::post('/v1/messaging/messages/sms')
App::post('/v1/messaging/messages/sms')
->desc('Create SMS')
->groups(['api', 'messaging'])
->label('audits.event', 'message.create')
@@ -3450,7 +3465,7 @@ Http::post('/v1/messaging/messages/sms')
->dynamic($message, Response::MODEL_MESSAGE);
});
Http::post('/v1/messaging/messages/push')
App::post('/v1/messaging/messages/push')
->desc('Create push notification')
->groups(['api', 'messaging'])
->label('audits.event', 'message.create')
@@ -3669,7 +3684,7 @@ Http::post('/v1/messaging/messages/push')
->dynamic($message, Response::MODEL_MESSAGE);
});
Http::get('/v1/messaging/messages')
App::get('/v1/messaging/messages')
->desc('List messages')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
@@ -3704,10 +3719,15 @@ Http::get('/v1/messaging/messages')
$queries[] = Query::search('search', $search);
}
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor !== false) {
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -3734,7 +3754,7 @@ Http::get('/v1/messaging/messages')
]), Response::MODEL_MESSAGE_LIST);
});
Http::get('/v1/messaging/messages/:messageId/logs')
App::get('/v1/messaging/messages/:messageId/logs')
->desc('List message logs')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
@@ -3831,7 +3851,7 @@ Http::get('/v1/messaging/messages/:messageId/logs')
]), Response::MODEL_LOG_LIST);
});
Http::get('/v1/messaging/messages/:messageId/targets')
App::get('/v1/messaging/messages/:messageId/targets')
->desc('List message targets')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
@@ -3879,10 +3899,15 @@ Http::get('/v1/messaging/messages/:messageId/targets')
$queries[] = Query::equal('$id', $targetIDs);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor !== false) {
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -3909,7 +3934,7 @@ Http::get('/v1/messaging/messages/:messageId/targets')
]), Response::MODEL_TARGET_LIST);
});
Http::get('/v1/messaging/messages/:messageId')
App::get('/v1/messaging/messages/:messageId')
->desc('Get message')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
@@ -3940,7 +3965,7 @@ Http::get('/v1/messaging/messages/:messageId')
$response->dynamic($message, Response::MODEL_MESSAGE);
});
Http::patch('/v1/messaging/messages/email/:messageId')
App::patch('/v1/messaging/messages/email/:messageId')
->desc('Update email')
->groups(['api', 'messaging'])
->label('audits.event', 'message.update')
@@ -4147,7 +4172,7 @@ Http::patch('/v1/messaging/messages/email/:messageId')
->dynamic($message, Response::MODEL_MESSAGE);
});
Http::patch('/v1/messaging/messages/sms/:messageId')
App::patch('/v1/messaging/messages/sms/:messageId')
->desc('Update SMS')
->groups(['api', 'messaging'])
->label('audits.event', 'message.update')
@@ -4329,7 +4354,7 @@ Http::patch('/v1/messaging/messages/sms/:messageId')
->dynamic($message, Response::MODEL_MESSAGE);
});
Http::patch('/v1/messaging/messages/push/:messageId')
App::patch('/v1/messaging/messages/push/:messageId')
->desc('Update push notification')
->groups(['api', 'messaging'])
->label('audits.event', 'message.update')
@@ -4590,7 +4615,7 @@ Http::patch('/v1/messaging/messages/push/:messageId')
->dynamic($message, Response::MODEL_MESSAGE);
});
Http::delete('/v1/messaging/messages/:messageId')
App::delete('/v1/messaging/messages/:messageId')
->desc('Delete message')
->groups(['api', 'messaging'])
->label('audits.event', 'message.delete')
+293 -18
View File
@@ -11,6 +11,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\CompoundUID;
use Appwrite\Utopia\Database\Validator\Queries\Migrations;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
@@ -21,11 +22,11 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Queries\Documents;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Http;
use Utopia\Migration\Resource;
use Utopia\Migration\Sources\Appwrite;
use Utopia\Migration\Sources\CSV;
use Utopia\Migration\Sources\Firebase;
use Utopia\Migration\Sources\JSON;
use Utopia\Migration\Sources\NHost;
use Utopia\Migration\Sources\Supabase;
use Utopia\Migration\Transfer;
@@ -43,7 +44,7 @@ use Utopia\Validator\WhiteList;
include_once __DIR__ . '/../shared/api.php';
Http::post('/v1/migrations/appwrite')
App::post('/v1/migrations/appwrite')
->groups(['api', 'migrations'])
->desc('Create Appwrite migration')
->label('scope', 'migrations.write')
@@ -106,7 +107,7 @@ Http::post('/v1/migrations/appwrite')
->dynamic($migration, Response::MODEL_MIGRATION);
});
Http::post('/v1/migrations/firebase')
App::post('/v1/migrations/firebase')
->groups(['api', 'migrations'])
->desc('Create Firebase migration')
->label('scope', 'migrations.write')
@@ -175,7 +176,7 @@ Http::post('/v1/migrations/firebase')
->dynamic($migration, Response::MODEL_MIGRATION);
});
Http::post('/v1/migrations/supabase')
App::post('/v1/migrations/supabase')
->groups(['api', 'migrations'])
->desc('Create Supabase migration')
->label('scope', 'migrations.write')
@@ -244,7 +245,7 @@ Http::post('/v1/migrations/supabase')
->dynamic($migration, Response::MODEL_MIGRATION);
});
Http::post('/v1/migrations/nhost')
App::post('/v1/migrations/nhost')
->groups(['api', 'migrations'])
->desc('Create NHost migration')
->label('scope', 'migrations.write')
@@ -315,7 +316,7 @@ Http::post('/v1/migrations/nhost')
->dynamic($migration, Response::MODEL_MIGRATION);
});
Http::post('/v1/migrations/csv/imports')
App::post('/v1/migrations/csv/imports')
->alias('/v1/migrations/csv')
->groups(['api', 'migrations'])
->desc('Import documents from a CSV')
@@ -461,7 +462,7 @@ Http::post('/v1/migrations/csv/imports')
->dynamic($migration, Response::MODEL_MIGRATION);
});
Http::post('/v1/migrations/csv/exports')
App::post('/v1/migrations/csv/exports')
->groups(['api', 'migrations'])
->desc('Export documents to CSV')
->label('scope', 'migrations.write')
@@ -596,7 +597,275 @@ Http::post('/v1/migrations/csv/exports')
->dynamic($migration, Response::MODEL_MIGRATION);
});
Http::get('/v1/migrations')
App::post('/v1/migrations/json/imports')
->groups(['api', 'migrations'])
->desc('Import documents from a JSON')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createJSONImport',
description: '/docs/references/migrations/migration-json-import.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_ACCEPTED,
model: Response::MODEL_MIGRATION,
)
]
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.')
->param('internalFile', false, new Boolean(), 'Is the file stored in an internal bucket?', true)
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
->inject('project')
->inject('platform')
->inject('deviceForFiles')
->inject('deviceForMigrations')
->inject('queueForEvents')
->inject('queueForMigrations')
->action(function (
string $bucketId,
string $fileId,
string $resourceId,
bool $internalFile,
Response $response,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
Document $project,
array $platform,
Device $deviceForFiles,
Device $deviceForMigrations,
Event $queueForEvents,
Migration $queueForMigrations
) {
$bucket = $authorization->skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) {
if ($internalFile) {
return $dbForPlatform->getDocument('buckets', 'default');
}
return $dbForProject->getDocument('buckets', $bucketId);
});
if ($bucket->isEmpty()) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = $authorization->skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
$path = $file->getAttribute('path', '');
if (!$deviceForFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
}
// No encryption or compression on files above 20MB.
$hasEncryption = !empty($file->getAttribute('openSSLCipher'));
$compression = $file->getAttribute('algorithm', Compression::NONE);
$hasCompression = $compression !== Compression::NONE;
$migrationId = ID::unique();
$newPath = $deviceForMigrations->getPath($migrationId . '_' . $fileId . '.json');
if ($hasEncryption || $hasCompression) {
$source = $deviceForFiles->read($path);
if ($hasEncryption) {
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
System::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
hex2bin($file->getAttribute('openSSLIV')),
hex2bin($file->getAttribute('openSSLTag'))
);
}
if ($hasCompression) {
switch ($compression) {
case Compression::ZSTD:
$source = (new Zstd())->decompress($source);
break;
case Compression::GZIP:
$source = (new GZIP())->decompress($source);
break;
}
}
// Manual write after decryption and/or decompression
if (!$deviceForMigrations->write($newPath, $source, 'application/json')) {
throw new \Exception('Unable to copy file');
}
} elseif (!$deviceForFiles->transfer($path, $newPath, $deviceForMigrations)) {
throw new \Exception('Unable to copy file');
}
$fileSize = $deviceForMigrations->getFileSize($newPath);
$resources = Transfer::extractServices([Transfer::GROUP_DATABASES]);
$migration = $dbForProject->createDocument('migrations', new Document([
'$id' => $migrationId,
'status' => 'pending',
'stage' => 'init',
'source' => JSON::getName(),
'destination' => Appwrite::getName(),
'resources' => $resources,
'resourceId' => $resourceId,
'resourceType' => Resource::TYPE_DATABASE,
'statusCounters' => '{}',
'resourceData' => '{}',
'errors' => [],
'options' => [
'path' => $newPath,
'size' => $fileSize,
],
]));
$queueForEvents->setParam('migrationId', $migration->getId());
$queueForMigrations
->setMigration($migration)
->setProject($project)
->setProject($project)
->trigger();
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::post('/v1/migrations/json/exports')
->groups(['api', 'migrations'])
->desc('Export documents to JSON')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createJSONExport',
description: '/docs/references/migrations/migration-json-export.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_ACCEPTED,
model: Response::MODEL_MIGRATION,
)
]
))
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database to export.')
->param('filename', '', new Text(255), 'The name of the file to be created for the export, excluding the .json extension.')
->param('columns', [], new ArrayList(new Text(Database::LENGTH_KEY)), 'List of attributes to export. If empty, all attributes will be exported. You can use the `*` wildcard to export all attributes from the collection.', true)
->param('queries', [], new ArrayList(new Text(0)), 'Array of query strings generated using the Query class provided by the SDK to filter documents to export. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->param('notify', true, new Boolean(), 'Set to true to receive an email when the export is complete. Default is true.', true)
->inject('user')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
->inject('project')
->inject('platform')
->inject('queueForEvents')
->inject('queueForMigrations')
->action(function (
string $resourceId,
string $filename,
array $columns,
array $queries,
bool $notify,
Document $user,
Response $response,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
Document $project,
array $platform,
Event $queueForEvents,
Migration $queueForMigrations
) {
try {
$parsedQueries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'default'));
if ($bucket->isEmpty()) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
[$databaseId, $collectionId] = \explode(':', $resourceId, 2);
if (empty($databaseId)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
if (empty($collectionId)) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$validator = new Documents(
attributes: $collection->getAttribute('attributes', []),
indexes: $collection->getAttribute('indexes', []),
idAttributeType: $dbForProject->getAdapter()->getIdAttributeType(),
);
if (!$validator->isValid($parsedQueries)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$migration = $dbForProject->createDocument('migrations', new Document([
'$id' => ID::unique(),
'status' => 'pending',
'stage' => 'init',
'source' => Appwrite::getName(),
'destination' => JSON::getName(),
'resources' => Transfer::extractServices([Transfer::GROUP_DATABASES]),
'resourceId' => $resourceId,
'resourceType' => Resource::TYPE_DATABASE,
'statusCounters' => '{}',
'resourceData' => '{}',
'errors' => [],
'options' => [
'bucketId' => 'default', // Always use internal bucket
'filename' => $filename,
'columns' => $columns,
'queries' => $queries,
'notify' => $notify,
'userInternalId' => $user->getSequence(),
],
]));
$queueForEvents->setParam('migrationId', $migration->getId());
$queueForMigrations
->setMigration($migration)
->setProject($project)
->setPlatform($platform)
->trigger();
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::get('/v1/migrations')
->groups(['api', 'migrations'])
->desc('List migrations')
->label('scope', 'migrations.read')
@@ -629,10 +898,16 @@ Http::get('/v1/migrations')
$queries[] = Query::search('search', $search);
}
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -661,7 +936,7 @@ Http::get('/v1/migrations')
]), Response::MODEL_MIGRATION_LIST);
});
Http::get('/v1/migrations/:migrationId')
App::get('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Get migration')
->label('scope', 'migrations.read')
@@ -691,7 +966,7 @@ Http::get('/v1/migrations/:migrationId')
$response->dynamic($migration, Response::MODEL_MIGRATION);
});
Http::get('/v1/migrations/appwrite/report')
App::get('/v1/migrations/appwrite/report')
->groups(['api', 'migrations'])
->desc('Get Appwrite migration report')
->label('scope', 'migrations.write')
@@ -740,7 +1015,7 @@ Http::get('/v1/migrations/appwrite/report')
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
});
Http::get('/v1/migrations/firebase/report')
App::get('/v1/migrations/firebase/report')
->groups(['api', 'migrations'])
->desc('Get Firebase migration report')
->label('scope', 'migrations.write')
@@ -793,7 +1068,7 @@ Http::get('/v1/migrations/firebase/report')
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
});
Http::get('/v1/migrations/supabase/report')
App::get('/v1/migrations/supabase/report')
->groups(['api', 'migrations'])
->desc('Get Supabase migration report')
->label('scope', 'migrations.write')
@@ -842,7 +1117,7 @@ Http::get('/v1/migrations/supabase/report')
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
});
Http::get('/v1/migrations/nhost/report')
App::get('/v1/migrations/nhost/report')
->groups(['api', 'migrations'])
->desc('Get NHost migration report')
->label('scope', 'migrations.write')
@@ -891,7 +1166,7 @@ Http::get('/v1/migrations/nhost/report')
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
});
Http::patch('/v1/migrations/:migrationId')
App::patch('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Update retry migration')
->label('scope', 'migrations.write')
@@ -944,7 +1219,7 @@ Http::patch('/v1/migrations/:migrationId')
$response->noContent();
});
Http::delete('/v1/migrations/:migrationId')
App::delete('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Delete migration')
->label('scope', 'migrations.write')
+7 -7
View File
@@ -6,6 +6,7 @@ use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
@@ -16,13 +17,12 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DateTimeValidator;
use Utopia\Database\Validator\UID;
use Utopia\Http;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
Http::get('/v1/project/usage')
App::get('/v1/project/usage')
->desc('Get project usage stats')
->groups(['api', 'usage'])
->label('scope', 'projects.read')
@@ -385,7 +385,7 @@ Http::get('/v1/project/usage')
// Variables
Http::post('/v1/project/variables')
App::post('/v1/project/variables')
->desc('Create variable')
->groups(['api'])
->label('scope', 'projects.write')
@@ -448,7 +448,7 @@ Http::post('/v1/project/variables')
->dynamic($variable, Response::MODEL_VARIABLE);
});
Http::get('/v1/project/variables')
App::get('/v1/project/variables')
->desc('List variables')
->groups(['api'])
->label('scope', 'projects.read')
@@ -479,7 +479,7 @@ Http::get('/v1/project/variables')
]), Response::MODEL_VARIABLE_LIST);
});
Http::get('/v1/project/variables/:variableId')
App::get('/v1/project/variables/:variableId')
->desc('Get variable')
->groups(['api'])
->label('scope', 'projects.read')
@@ -509,7 +509,7 @@ Http::get('/v1/project/variables/:variableId')
$response->dynamic($variable, Response::MODEL_VARIABLE);
});
Http::put('/v1/project/variables/:variableId')
App::put('/v1/project/variables/:variableId')
->desc('Update variable')
->groups(['api'])
->label('scope', 'projects.write')
@@ -567,7 +567,7 @@ Http::put('/v1/project/variables/:variableId')
$response->dynamic($variable, Response::MODEL_VARIABLE);
});
Http::delete('/v1/project/variables/:variableId')
App::delete('/v1/project/variables/:variableId')
->desc('Delete variable')
->groups(['api'])
->label('scope', 'projects.write')
+448 -101
View File
@@ -6,6 +6,7 @@ use Appwrite\Event\Delete;
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\SDK\AuthType;
@@ -14,25 +15,31 @@ use Appwrite\SDK\Deprecated;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Keys;
use Appwrite\Utopia\Database\Validator\ProjectId;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use PHPMailer\PHPMailer\PHPMailer;
use Utopia\App;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Utopia\Database\Adapter\Pool as DatabasePool;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\Http;
use Utopia\DSN\DSN;
use Utopia\Locale\Locale;
use Utopia\Pools\Group;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
@@ -45,7 +52,7 @@ use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
Http::init()
App::init()
->groups(['projects'])
->inject('project')
->action(function (Document $project) {
@@ -54,7 +61,252 @@ Http::init()
}
});
Http::get('/v1/projects/:projectId')
App::post('/v1/projects')
->desc('Create project')
->groups(['api', 'projects'])
->label('audits.event', 'projects.create')
->label('audits.resource', 'project/{response.$id}')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'create',
description: '/docs/references/projects/create.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_PROJECT,
)
]
))
->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true)
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
->param('logo', '', new Text(1024), 'Project logo.', true)
->param('url', '', new URL(), 'Project URL.', true)
->param('legalName', '', new Text(256), 'Project legal Name. Max length: 256 chars.', true)
->param('legalCountry', '', new Text(256), 'Project legal Country. Max length: 256 chars.', true)
->param('legalState', '', new Text(256), 'Project legal State. Max length: 256 chars.', true)
->param('legalCity', '', new Text(256), 'Project legal City. Max length: 256 chars.', true)
->param('legalAddress', '', new Text(256), 'Project legal Address. Max length: 256 chars.', true)
->param('legalTaxId', '', new Text(256), 'Project legal Tax ID. Max length: 256 chars.', true)
->inject('request')
->inject('response')
->inject('dbForPlatform')
->inject('cache')
->inject('pools')
->inject('hooks')
->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForPlatform, Cache $cache, Group $pools, Hooks $hooks) {
$team = $dbForPlatform->getDocument('teams', $teamId);
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
}
$allowList = \array_filter(\explode(',', System::getEnv('_APP_PROJECT_REGIONS', '')));
if (!empty($allowList) && !\in_array($region, $allowList)) {
throw new Exception(Exception::PROJECT_REGION_UNSUPPORTED, 'Region "' . $region . '" is not supported');
}
$auth = Config::getParam('auth', []);
$auths = [
'limit' => 0,
'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT,
'passwordHistory' => 0,
'passwordDictionary' => false,
'duration' => TOKEN_EXPIRATION_LOGIN_LONG,
'personalDataCheck' => false,
'mockNumbers' => [],
'sessionAlerts' => false,
'membershipsUserName' => false,
'membershipsUserEmail' => false,
'membershipsMfa' => false,
'invalidateSessions' => true
];
foreach ($auth as $method) {
$auths[$method['key'] ?? ''] = true;
}
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
if ($projectId === 'console') {
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
}
$databases = Config::getParam('pools-database', []);
if ($region !== 'default') {
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
$keys = explode(',', $databaseKeys);
$databases = array_filter($keys, function ($value) use ($region) {
return str_contains($value, $region);
});
}
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
$dsn = $databases[$index];
} else {
$dsn = $databases[array_rand($databases)];
}
// TODO: Temporary until all projects are using shared tables.
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn, $sharedTables)) {
$schema = 'appwrite';
$database = 'appwrite';
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
$dsn = $schema . '://' . $dsn . '?database=' . $database;
if (!empty($namespace)) {
$dsn .= '&namespace=' . $namespace;
}
}
try {
$project = $dbForPlatform->createDocument('projects', new Document([
'$id' => $projectId,
'$permissions' => [
Permission::read(Role::team(ID::custom($teamId))),
Permission::update(Role::team(ID::custom($teamId), 'owner')),
Permission::update(Role::team(ID::custom($teamId), 'developer')),
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
],
'name' => $name,
'teamInternalId' => $team->getSequence(),
'teamId' => $team->getId(),
'region' => $region,
'description' => $description,
'logo' => $logo,
'url' => $url,
'version' => APP_VERSION_STABLE,
'legalName' => $legalName,
'legalCountry' => $legalCountry,
'legalState' => $legalState,
'legalCity' => $legalCity,
'legalAddress' => $legalAddress,
'legalTaxId' => ID::custom($legalTaxId),
'services' => new stdClass(),
'platforms' => null,
'oAuthProviders' => [],
'webhooks' => null,
'keys' => null,
'auths' => $auths,
'accessedAt' => DateTime::now(),
'search' => implode(' ', [$projectId, $name]),
'database' => $dsn,
'labels' => [],
]));
} catch (Duplicate) {
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
}
try {
$dsn = new DSN($dsn);
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $dsn);
}
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
if (!$sharedTablesV2) {
$adapter = new DatabasePool($pools->get($dsn->getHost()));
$dbForProject = new Database($adapter, $cache);
$dbForProject->setDatabase(APP_DATABASE);
if ($sharedTables) {
$dbForProject
->setSharedTables(true)
->setTenant($sharedTablesV1 ? (int)$project->getSequence() : null)
->setNamespace($dsn->getParam('namespace'));
} else {
$dbForProject
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
}
$create = true;
try {
$dbForProject->create();
} catch (Duplicate) {
$create = false;
}
if ($create || $projectTables) {
$adapter = new AdapterDatabase($dbForProject);
$audit = new Audit($adapter);
$audit->setup();
}
if (!$create && $sharedTablesV1) {
$adapter = new AdapterDatabase($dbForProject);
$attributes = $adapter->getAttributeDocuments();
$indexes = $adapter->getIndexDocuments();
$dbForProject->createDocument(Database::METADATA, new Document([
'$id' => ID::custom('audit'),
'$permissions' => [Permission::create(Role::any())],
'name' => 'audit',
'attributes' => $attributes,
'indexes' => $indexes,
'documentSecurity' => true
]));
}
if ($create || $sharedTablesV1) {
/** @var array $collections */
$collections = Config::getParam('collections', [])['projects'] ?? [];
foreach ($collections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
}
$attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']);
$indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']);
try {
$dbForProject->createCollection($key, $attributes, $indexes);
} catch (Duplicate) {
$dbForProject->createDocument(Database::METADATA, new Document([
'$id' => ID::custom($key),
'$permissions' => [Permission::create(Role::any())],
'name' => $key,
'attributes' => $attributes,
'indexes' => $indexes,
'documentSecurity' => true
]));
}
}
}
}
// Hook allowing instant project mirroring during migration
// Outside of migration, hook is not registered and has no effect
$hooks->trigger('afterProjectCreation', [$project, $pools, $cache]);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($project, Response::MODEL_PROJECT);
});
App::get('/v1/projects/:projectId')
->desc('Get project')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
@@ -85,7 +337,138 @@ Http::get('/v1/projects/:projectId')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/service')
App::patch('/v1/projects/:projectId')
->desc('Update project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('audits.event', 'projects.update')
->label('audits.resource', 'project/{request.projectId}')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'update',
description: '/docs/references/projects/update.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
->param('logo', '', new Text(1024), 'Project logo.', true)
->param('url', '', new URL(), 'Project URL.', true)
->param('legalName', '', new Text(256), 'Project legal name. Max length: 256 chars.', true)
->param('legalCountry', '', new Text(256), 'Project legal country. Max length: 256 chars.', true)
->param('legalState', '', new Text(256), 'Project legal state. Max length: 256 chars.', true)
->param('legalCity', '', new Text(256), 'Project legal city. Max length: 256 chars.', true)
->param('legalAddress', '', new Text(256), 'Project legal address. Max length: 256 chars.', true)
->param('legalTaxId', '', new Text(256), 'Project legal tax ID. Max length: 256 chars.', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project
->setAttribute('name', $name)
->setAttribute('description', $description)
->setAttribute('logo', $logo)
->setAttribute('url', $url)
->setAttribute('legalName', $legalName)
->setAttribute('legalCountry', $legalCountry)
->setAttribute('legalState', $legalState)
->setAttribute('legalCity', $legalCity)
->setAttribute('legalAddress', $legalAddress)
->setAttribute('legalTaxId', $legalTaxId)
->setAttribute('search', implode(' ', [$projectId, $name])));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/team')
->desc('Update project team')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateTeam',
description: '/docs/references/projects/update-team.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('teamId', '', new UID(), 'Team ID of the team to transfer project to.')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $teamId, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
$team = $dbForPlatform->getDocument('teams', $teamId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
}
$permissions = [
Permission::read(Role::team(ID::custom($teamId))),
Permission::update(Role::team(ID::custom($teamId), 'owner')),
Permission::update(Role::team(ID::custom($teamId), 'developer')),
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
];
$project
->setAttribute('teamId', $teamId)
->setAttribute('teamInternalId', $team->getSequence())
->setAttribute('$permissions', $permissions);
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
$installations = $dbForPlatform->find('installations', [
Query::equal('projectInternalId', [$project->getSequence()]),
]);
foreach ($installations as $installation) {
$installation->getAttribute('$permissions', $permissions);
$dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
}
$repositories = $dbForPlatform->find('repositories', [
Query::equal('projectInternalId', [$project->getSequence()]),
]);
foreach ($repositories as $repository) {
$repository->getAttribute('$permissions', $permissions);
$dbForPlatform->updateDocument('repositories', $repository->getId(), $repository);
}
$vcsComments = $dbForPlatform->find('vcsComments', [
Query::equal('projectInternalId', [$project->getSequence()]),
]);
foreach ($vcsComments as $vcsComment) {
$vcsComment->getAttribute('$permissions', $permissions);
$dbForPlatform->updateDocument('vcsComments', $vcsComment->getId(), $vcsComment);
}
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/service')
->desc('Update service status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -123,7 +506,7 @@ Http::patch('/v1/projects/:projectId/service')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/service/all')
App::patch('/v1/projects/:projectId/service/all')
->desc('Update all service status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -164,7 +547,7 @@ Http::patch('/v1/projects/:projectId/service/all')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/api')
App::patch('/v1/projects/:projectId/api')
->desc('Update API status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -222,7 +605,7 @@ Http::patch('/v1/projects/:projectId/api')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/api/all')
App::patch('/v1/projects/:projectId/api/all')
->desc('Update all API status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -283,7 +666,7 @@ Http::patch('/v1/projects/:projectId/api/all')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/oauth2')
App::patch('/v1/projects/:projectId/oauth2')
->desc('Update project OAuth2')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -334,7 +717,7 @@ Http::patch('/v1/projects/:projectId/oauth2')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/session-alerts')
App::patch('/v1/projects/:projectId/auth/session-alerts')
->desc('Update project sessions emails')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -372,7 +755,7 @@ Http::patch('/v1/projects/:projectId/auth/session-alerts')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/memberships-privacy')
App::patch('/v1/projects/:projectId/auth/memberships-privacy')
->desc('Update project memberships privacy attributes')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -414,7 +797,7 @@ Http::patch('/v1/projects/:projectId/auth/memberships-privacy')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/limit')
App::patch('/v1/projects/:projectId/auth/limit')
->desc('Update project users limit')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -452,7 +835,7 @@ Http::patch('/v1/projects/:projectId/auth/limit')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/duration')
App::patch('/v1/projects/:projectId/auth/duration')
->desc('Update project authentication duration')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -490,7 +873,7 @@ Http::patch('/v1/projects/:projectId/auth/duration')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/:method')
App::patch('/v1/projects/:projectId/auth/:method')
->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -531,7 +914,7 @@ Http::patch('/v1/projects/:projectId/auth/:method')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/password-history')
App::patch('/v1/projects/:projectId/auth/password-history')
->desc('Update authentication password history. Use this endpoint to set the number of password history to save and 0 to disable password history.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -569,7 +952,7 @@ Http::patch('/v1/projects/:projectId/auth/password-history')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/password-dictionary')
App::patch('/v1/projects/:projectId/auth/password-dictionary')
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -607,7 +990,7 @@ Http::patch('/v1/projects/:projectId/auth/password-dictionary')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/personal-data')
App::patch('/v1/projects/:projectId/auth/personal-data')
->desc('Update personal data check')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -645,7 +1028,7 @@ Http::patch('/v1/projects/:projectId/auth/personal-data')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/max-sessions')
App::patch('/v1/projects/:projectId/auth/max-sessions')
->desc('Update project user sessions limit')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -683,7 +1066,7 @@ Http::patch('/v1/projects/:projectId/auth/max-sessions')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/auth/mock-numbers')
App::patch('/v1/projects/:projectId/auth/mock-numbers')
->desc('Update the mock numbers for the project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -729,7 +1112,7 @@ Http::patch('/v1/projects/:projectId/auth/mock-numbers')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::delete('/v1/projects/:projectId')
App::delete('/v1/projects/:projectId')
->desc('Delete project')
->groups(['api', 'projects'])
->label('audits.event', 'projects.delete')
@@ -775,7 +1158,7 @@ Http::delete('/v1/projects/:projectId')
// Webhooks
Http::post('/v1/projects/:projectId/webhooks')
App::post('/v1/projects/:projectId/webhooks')
->desc('Create webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -840,7 +1223,7 @@ Http::post('/v1/projects/:projectId/webhooks')
->dynamic($webhook, Response::MODEL_WEBHOOK);
});
Http::get('/v1/projects/:projectId/webhooks')
App::get('/v1/projects/:projectId/webhooks')
->desc('List webhooks')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
@@ -880,7 +1263,7 @@ Http::get('/v1/projects/:projectId/webhooks')
]), Response::MODEL_WEBHOOK_LIST);
});
Http::get('/v1/projects/:projectId/webhooks/:webhookId')
App::get('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Get webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
@@ -921,7 +1304,7 @@ Http::get('/v1/projects/:projectId/webhooks/:webhookId')
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
});
Http::put('/v1/projects/:projectId/webhooks/:webhookId')
App::put('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Update webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -987,7 +1370,7 @@ Http::put('/v1/projects/:projectId/webhooks/:webhookId')
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
});
Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
->desc('Update webhook signature key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -1033,7 +1416,7 @@ Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
});
Http::delete('/v1/projects/:projectId/webhooks/:webhookId')
App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Delete webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -1081,7 +1464,7 @@ Http::delete('/v1/projects/:projectId/webhooks/:webhookId')
// Keys
Http::post('/v1/projects/:projectId/keys')
App::post('/v1/projects/:projectId/keys')
->desc('Create key')
->groups(['api', 'projects'])
->label('scope', 'keys.write')
@@ -1099,15 +1482,12 @@ Http::post('/v1/projects/:projectId/keys')
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
// TODO: When migrating to Platform API, mark keyId required for consistency
->param('keyId', 'unique()', new CustomId(), 'Key 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.', true)
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
$keyId = $keyId == 'unique()' ? ID::unique() : $keyId;
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
@@ -1116,12 +1496,15 @@ Http::post('/v1/projects/:projectId/keys')
}
$key = new Document([
'$id' => $keyId,
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
// TODO: @hmacr Remove `projectInternalId` and `projectId` column writes before deleting the column.
'projectInternalId' => $project->getSequence(),
'projectId' => $project->getId(),
'resourceInternalId' => $project->getSequence(),
'resourceId' => $project->getId(),
'resourceType' => 'projects',
@@ -1133,11 +1516,7 @@ Http::post('/v1/projects/:projectId/keys')
'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)),
]);
try {
$key = $dbForPlatform->createDocument('keys', $key);
} catch (Duplicate) {
throw new Exception(Exception::KEY_ALREADY_EXISTS);
}
$key = $dbForPlatform->createDocument('keys', $key);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
@@ -1146,7 +1525,7 @@ Http::post('/v1/projects/:projectId/keys')
->dynamic($key, Response::MODEL_KEY);
});
Http::get('/v1/projects/:projectId/keys')
App::get('/v1/projects/:projectId/keys')
->desc('List keys')
->groups(['api', 'projects'])
->label('scope', 'keys.read')
@@ -1164,11 +1543,10 @@ Http::get('/v1/projects/:projectId/keys')
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('queries', [], new Keys(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Keys::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, array $queries, bool $includeTotal, Response $response, Database $dbForPlatform) {
->action(function (string $projectId, bool $includeTotal, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
@@ -1176,50 +1554,19 @@ Http::get('/v1/projects/:projectId/keys')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
// Backwards compatibility
if (\count(Query::getByType($queries, [Query::TYPE_LIMIT])) === 0) {
$queries[] = Query::limit(5000);
}
$queries[] = Query::equal('resourceType', ['projects']);
$queries[] = Query::equal('resourceInternalId', [$project->getSequence()]);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$keyId = $cursor->getValue();
$cursorDocument = $dbForPlatform->getDocument('keys', $keyId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Key '{$keyId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
$keys = $dbForPlatform->find('keys', $queries);
$keys = $dbForPlatform->find('keys', [
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$project->getSequence()]),
Query::limit(5000),
]);
$response->dynamic(new Document([
'keys' => $keys,
'total' => $includeTotal ? $dbForPlatform->count('keys', $filterQueries, APP_LIMIT_COUNT) : 0,
'total' => $includeTotal ? count($keys) : 0,
]), Response::MODEL_KEY_LIST);
});
Http::get('/v1/projects/:projectId/keys/:keyId')
App::get('/v1/projects/:projectId/keys/:keyId')
->desc('Get key')
->groups(['api', 'projects'])
->label('scope', 'keys.read')
@@ -1261,7 +1608,7 @@ Http::get('/v1/projects/:projectId/keys/:keyId')
$response->dynamic($key, Response::MODEL_KEY);
});
Http::put('/v1/projects/:projectId/keys/:keyId')
App::put('/v1/projects/:projectId/keys/:keyId')
->desc('Update key')
->groups(['api', 'projects'])
->label('scope', 'keys.write')
@@ -1281,7 +1628,7 @@ Http::put('/v1/projects/:projectId/keys/:keyId')
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForPlatform')
@@ -1315,7 +1662,7 @@ Http::put('/v1/projects/:projectId/keys/:keyId')
$response->dynamic($key, Response::MODEL_KEY);
});
Http::delete('/v1/projects/:projectId/keys/:keyId')
App::delete('/v1/projects/:projectId/keys/:keyId')
->desc('Delete key')
->groups(['api', 'projects'])
->label('scope', 'keys.write')
@@ -1364,7 +1711,7 @@ Http::delete('/v1/projects/:projectId/keys/:keyId')
// JWT Keys
Http::post('/v1/projects/:projectId/jwts')
App::post('/v1/projects/:projectId/jwts')
->groups(['api', 'projects'])
->desc('Create JWT')
->label('scope', 'projects.write')
@@ -1382,7 +1729,7 @@ Http::post('/v1/projects/:projectId/jwts')
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForPlatform')
@@ -1406,7 +1753,7 @@ Http::post('/v1/projects/:projectId/jwts')
// Platforms
Http::post('/v1/projects/:projectId/platforms')
App::post('/v1/projects/:projectId/platforms')
->desc('Create platform')
->groups(['api', 'projects'])
->label('audits.event', 'platforms.create')
@@ -1486,7 +1833,7 @@ Http::post('/v1/projects/:projectId/platforms')
->dynamic($platform, Response::MODEL_PLATFORM);
});
Http::get('/v1/projects/:projectId/platforms')
App::get('/v1/projects/:projectId/platforms')
->desc('List platforms')
->groups(['api', 'projects'])
->label('scope', 'platforms.read')
@@ -1526,7 +1873,7 @@ Http::get('/v1/projects/:projectId/platforms')
]), Response::MODEL_PLATFORM_LIST);
});
Http::get('/v1/projects/:projectId/platforms/:platformId')
App::get('/v1/projects/:projectId/platforms/:platformId')
->desc('Get platform')
->groups(['api', 'projects'])
->label('scope', 'platforms.read')
@@ -1567,7 +1914,7 @@ Http::get('/v1/projects/:projectId/platforms/:platformId')
$response->dynamic($platform, Response::MODEL_PLATFORM);
});
Http::put('/v1/projects/:projectId/platforms/:platformId')
App::put('/v1/projects/:projectId/platforms/:platformId')
->desc('Update platform')
->groups(['api', 'projects'])
->label('scope', 'platforms.write')
@@ -1621,7 +1968,7 @@ Http::put('/v1/projects/:projectId/platforms/:platformId')
$response->dynamic($platform, Response::MODEL_PLATFORM);
});
Http::delete('/v1/projects/:projectId/platforms/:platformId')
App::delete('/v1/projects/:projectId/platforms/:platformId')
->desc('Delete platform')
->groups(['api', 'projects'])
->label('audits.event', 'platforms.delete')
@@ -1671,7 +2018,7 @@ Http::delete('/v1/projects/:projectId/platforms/:platformId')
// CUSTOM SMTP and Templates
Http::patch('/v1/projects/:projectId/smtp')
App::patch('/v1/projects/:projectId/smtp')
->desc('Update SMTP')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -1789,7 +2136,7 @@ Http::patch('/v1/projects/:projectId/smtp')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::post('/v1/projects/:projectId/smtp/tests')
App::post('/v1/projects/:projectId/smtp/tests')
->desc('Create SMTP test')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -1884,7 +2231,7 @@ Http::post('/v1/projects/:projectId/smtp/tests')
return $response->noContent();
});
Http::get('/v1/projects/:projectId/templates/sms/:type/:locale')
App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
->desc('Get custom SMS template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -1952,7 +2299,7 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale')
});
Http::get('/v1/projects/:projectId/templates/email/:type/:locale')
App::get('/v1/projects/:projectId/templates/email/:type/:locale')
->desc('Get custom email template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -2051,7 +2398,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale')
$response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE);
});
Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
->desc('Update custom SMS template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -2118,7 +2465,7 @@ Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
]), Response::MODEL_SMS_TEMPLATE);
});
Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
->desc('Update custom email templates')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -2175,7 +2522,7 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
]), Response::MODEL_EMAIL_TEMPLATE);
});
Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
->desc('Reset custom SMS template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -2247,7 +2594,7 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
]), Response::MODEL_SMS_TEMPLATE);
});
Http::delete('/v1/projects/:projectId/templates/email/:type/:locale')
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
->desc('Delete custom email template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
@@ -2300,7 +2647,7 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale')
]), Response::MODEL_EMAIL_TEMPLATE);
});
Http::patch('/v1/projects/:projectId/auth/session-invalidation')
App::patch('/v1/projects/:projectId/auth/session-invalidation')
->desc('Update invalidate session option of the project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
+57 -32
View File
@@ -26,6 +26,7 @@ use Appwrite\Utopia\Response;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberUtil;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Auth\Proofs\Password;
use Utopia\Auth\Proofs\Token;
@@ -51,15 +52,15 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Emails\Email;
use Utopia\Http;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
Http::post('/v1/teams')
App::post('/v1/teams')
->desc('Create team')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].create')
@@ -155,7 +156,7 @@ Http::post('/v1/teams')
->dynamic($team, Response::MODEL_TEAM);
});
Http::get('/v1/teams')
App::get('/v1/teams')
->desc('List teams')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
@@ -190,10 +191,16 @@ Http::get('/v1/teams')
$queries[] = Query::search('search', $search);
}
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -223,7 +230,7 @@ Http::get('/v1/teams')
]), Response::MODEL_TEAM_LIST);
});
Http::get('/v1/teams/:teamId')
App::get('/v1/teams/:teamId')
->desc('Get team')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
@@ -254,7 +261,7 @@ Http::get('/v1/teams/:teamId')
$response->dynamic($team, Response::MODEL_TEAM);
});
Http::get('/v1/teams/:teamId/prefs')
App::get('/v1/teams/:teamId/prefs')
->desc('Get team preferences')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
@@ -293,7 +300,7 @@ Http::get('/v1/teams/:teamId/prefs')
$response->dynamic($prefs, Response::MODEL_PREFERENCES);
});
Http::put('/v1/teams/:teamId')
App::put('/v1/teams/:teamId')
->desc('Update name')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].update')
@@ -338,7 +345,7 @@ Http::put('/v1/teams/:teamId')
$response->dynamic($team, Response::MODEL_TEAM);
});
Http::put('/v1/teams/:teamId/prefs')
App::put('/v1/teams/:teamId/prefs')
->desc('Update preferences')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].update.prefs')
@@ -386,7 +393,7 @@ Http::put('/v1/teams/:teamId/prefs')
$response->dynamic($prefs, Response::MODEL_PREFERENCES);
});
Http::delete('/v1/teams/:teamId')
App::delete('/v1/teams/:teamId')
->desc('Delete team')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].delete')
@@ -426,22 +433,15 @@ Http::delete('/v1/teams/:teamId')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB');
}
// Sync delete
$deletes = new Deletes();
$deletes->deleteMemberships($getProjectDB, $team, $project);
// Async delete
if ($project->getId() === 'console') {
$queueForDeletes
->setType(DELETE_TYPE_TEAM_PROJECTS)
->setDocument($team)
->trigger();
->setDocument($team);
}
$queueForDeletes
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($team);
$queueForEvents
->setParam('teamId', $team->getId())
->setPayload($response->output($team, Response::MODEL_TEAM))
@@ -450,7 +450,7 @@ Http::delete('/v1/teams/:teamId')
$response->noContent();
});
Http::post('/v1/teams/:teamId/memberships')
App::post('/v1/teams/:teamId/memberships')
->desc('Create team membership')
->groups(['api', 'teams', 'auth'])
->label('event', 'teams.[teamId].memberships.[membershipId].create')
@@ -477,7 +477,16 @@ Http::post('/v1/teams/:teamId/memberships')
->param('email', '', new EmailValidator(), 'Email of the new team member.', true)
->param('userId', '', new UID(), 'ID of the user to be added to a team.', true)
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('roles', [], 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('roles', [], function (Document $project) {
if ($project->getId() === 'console') {
$roles = array_keys(Config::getParam('roles', []));
$roles = array_filter($roles, function ($role) {
return !in_array($role, [User::ROLE_APPS, User::ROLE_GUESTS, User::ROLE_USERS]);
});
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
}
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 ($redirectValidator) => $redirectValidator, '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, ['redirectValidator']) // 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')
@@ -763,7 +772,7 @@ Http::post('/v1/teams/:teamId/memberships')
->setPreview($preview)
->setRecipient($invitee->getAttribute('email'))
->setName($invitee->getAttribute('name', ''))
->appendVariables($emailVariables)
->setVariables($emailVariables)
->trigger();
} elseif (!empty($phone)) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
@@ -828,7 +837,7 @@ Http::post('/v1/teams/:teamId/memberships')
);
});
Http::get('/v1/teams/:teamId/memberships')
App::get('/v1/teams/:teamId/memberships')
->desc('List team memberships')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
@@ -873,15 +882,22 @@ Http::get('/v1/teams/:teamId/memberships')
// Set internal queries
$queries[] = Query::equal('teamInternalId', [$team->getSequence()]);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$membershipId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('memberships', $membershipId);
@@ -965,7 +981,7 @@ Http::get('/v1/teams/:teamId/memberships')
]), Response::MODEL_MEMBERSHIP_LIST);
});
Http::get('/v1/teams/:teamId/memberships/:membershipId')
App::get('/v1/teams/:teamId/memberships/:membershipId')
->desc('Get team membership')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
@@ -1050,7 +1066,7 @@ Http::get('/v1/teams/:teamId/memberships/:membershipId')
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
});
Http::patch('/v1/teams/:teamId/memberships/:membershipId')
App::patch('/v1/teams/:teamId/memberships/:membershipId')
->desc('Update membership')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].memberships.[membershipId].update')
@@ -1072,7 +1088,16 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId')
))
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings. Use this param to set the user\'s 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('roles', [], function (Document $project) {
if ($project->getId() === 'console') {
$roles = array_keys(Config::getParam('roles', []));
$roles = array_filter($roles, function ($role) {
return !in_array($role, [User::ROLE_APPS, User::ROLE_GUESTS, User::ROLE_USERS]);
});
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
}
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
}, 'An array of strings. Use this param to set the user\'s 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'])
->inject('request')
->inject('response')
->inject('user')
@@ -1151,7 +1176,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId')
);
});
Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->desc('Update team membership status')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].memberships.[membershipId].update.status')
@@ -1318,7 +1343,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
);
});
Http::delete('/v1/teams/:teamId/memberships/:membershipId')
App::delete('/v1/teams/:teamId/memberships/:membershipId')
->desc('Delete team membership')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].memberships.[membershipId].delete')
@@ -1418,7 +1443,7 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId')
$response->noContent();
});
Http::get('/v1/teams/:teamId/logs')
App::get('/v1/teams/:teamId/logs')
->desc('List team logs')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
+70 -57
View File
@@ -29,6 +29,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Users;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Auth\Hash;
use Utopia\Auth\Hashes\Argon2;
@@ -60,7 +61,6 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Emails\Email;
use Utopia\Http;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
@@ -227,7 +227,7 @@ function createUser(Hash $hash, string $userId, ?string $email, ?string $passwor
return $user;
}
Http::post('/v1/users')
App::post('/v1/users')
->desc('Create user')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -264,7 +264,7 @@ Http::post('/v1/users')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/bcrypt')
App::post('/v1/users/bcrypt')
->desc('Create user with bcrypt password')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -302,7 +302,7 @@ Http::post('/v1/users/bcrypt')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/md5')
App::post('/v1/users/md5')
->desc('Create user with MD5 password')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -339,7 +339,7 @@ Http::post('/v1/users/md5')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/argon2')
App::post('/v1/users/argon2')
->desc('Create user with Argon2 password')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -376,7 +376,7 @@ Http::post('/v1/users/argon2')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/sha')
App::post('/v1/users/sha')
->desc('Create user with SHA password')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -417,7 +417,7 @@ Http::post('/v1/users/sha')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/phpass')
App::post('/v1/users/phpass')
->desc('Create user with PHPass password')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -454,7 +454,7 @@ Http::post('/v1/users/phpass')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/scrypt')
App::post('/v1/users/scrypt')
->desc('Create user with Scrypt password')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -502,7 +502,7 @@ Http::post('/v1/users/scrypt')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/scrypt-modified')
App::post('/v1/users/scrypt-modified')
->desc('Create user with Scrypt modified password')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -546,7 +546,7 @@ Http::post('/v1/users/scrypt-modified')
->dynamic($user, Response::MODEL_USER);
});
Http::post('/v1/users/:userId/targets')
App::post('/v1/users/:userId/targets')
->desc('Create user target')
->groups(['api', 'users'])
->label('audits.event', 'target.create')
@@ -641,7 +641,7 @@ Http::post('/v1/users/:userId/targets')
->dynamic($target, Response::MODEL_TARGET);
});
Http::get('/v1/users')
App::get('/v1/users')
->desc('List users')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -675,10 +675,16 @@ Http::get('/v1/users')
$queries[] = Query::search('search', $search);
}
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -714,7 +720,7 @@ Http::get('/v1/users')
]), Response::MODEL_USER_LIST);
});
Http::get('/v1/users/:userId')
App::get('/v1/users/:userId')
->desc('Get user')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -745,7 +751,7 @@ Http::get('/v1/users/:userId')
$response->dynamic($user, Response::MODEL_USER);
});
Http::get('/v1/users/:userId/prefs')
App::get('/v1/users/:userId/prefs')
->desc('Get user preferences')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -778,7 +784,7 @@ Http::get('/v1/users/:userId/prefs')
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
});
Http::get('/v1/users/:userId/targets/:targetId')
App::get('/v1/users/:userId/targets/:targetId')
->desc('Get user target')
->groups(['api', 'users'])
->label('scope', 'targets.read')
@@ -816,7 +822,7 @@ Http::get('/v1/users/:userId/targets/:targetId')
$response->dynamic($target, Response::MODEL_TARGET);
});
Http::get('/v1/users/:userId/sessions')
App::get('/v1/users/:userId/sessions')
->desc('List user sessions')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -860,7 +866,7 @@ Http::get('/v1/users/:userId/sessions')
]), Response::MODEL_SESSION_LIST);
});
Http::get('/v1/users/:userId/memberships')
App::get('/v1/users/:userId/memberships')
->desc('List user memberships')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -915,7 +921,7 @@ Http::get('/v1/users/:userId/memberships')
]), Response::MODEL_MEMBERSHIP_LIST);
});
Http::get('/v1/users/:userId/logs')
App::get('/v1/users/:userId/logs')
->desc('List user logs')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -1002,7 +1008,7 @@ Http::get('/v1/users/:userId/logs')
]), Response::MODEL_LOG_LIST);
});
Http::get('/v1/users/:userId/targets')
App::get('/v1/users/:userId/targets')
->desc('List user targets')
->groups(['api', 'users'])
->label('scope', 'targets.read')
@@ -1036,11 +1042,14 @@ Http::get('/v1/users/:userId/targets')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$queries[] = Query::equal('userId', [$userId]);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
if ($cursor !== false) {
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -1064,7 +1073,7 @@ Http::get('/v1/users/:userId/targets')
]), Response::MODEL_TARGET_LIST);
});
Http::get('/v1/users/identities')
App::get('/v1/users/identities')
->desc('List identities')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -1097,11 +1106,15 @@ Http::get('/v1/users/identities')
if (!empty($search)) {
$queries[] = Query::search('search', $search);
}
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
if ($cursor !== false) {
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
@@ -1126,7 +1139,7 @@ Http::get('/v1/users/identities')
]), Response::MODEL_IDENTITY_LIST);
});
Http::patch('/v1/users/:userId/status')
App::patch('/v1/users/:userId/status')
->desc('Update user status')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.status')
@@ -1168,7 +1181,7 @@ Http::patch('/v1/users/:userId/status')
$response->dynamic($user, Response::MODEL_USER);
});
Http::put('/v1/users/:userId/labels')
App::put('/v1/users/:userId/labels')
->desc('Update user labels')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.labels')
@@ -1211,7 +1224,7 @@ Http::put('/v1/users/:userId/labels')
$response->dynamic($user, Response::MODEL_USER);
});
Http::patch('/v1/users/:userId/verification/phone')
App::patch('/v1/users/:userId/verification/phone')
->desc('Update phone verification')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.verification')
@@ -1252,7 +1265,7 @@ Http::patch('/v1/users/:userId/verification/phone')
$response->dynamic($user, Response::MODEL_USER);
});
Http::patch('/v1/users/:userId/name')
App::patch('/v1/users/:userId/name')
->desc('Update name')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.name')
@@ -1295,7 +1308,7 @@ Http::patch('/v1/users/:userId/name')
$response->dynamic($user, Response::MODEL_USER);
});
Http::patch('/v1/users/:userId/password')
App::patch('/v1/users/:userId/password')
->desc('Update password')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.password')
@@ -1394,7 +1407,7 @@ Http::patch('/v1/users/:userId/password')
$response->dynamic($user, Response::MODEL_USER);
});
Http::patch('/v1/users/:userId/email')
App::patch('/v1/users/:userId/email')
->desc('Update email')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.email')
@@ -1506,7 +1519,7 @@ Http::patch('/v1/users/:userId/email')
$response->dynamic($user, Response::MODEL_USER);
});
Http::patch('/v1/users/:userId/phone')
App::patch('/v1/users/:userId/phone')
->desc('Update phone')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.phone')
@@ -1595,7 +1608,7 @@ Http::patch('/v1/users/:userId/phone')
$response->dynamic($user, Response::MODEL_USER);
});
Http::patch('/v1/users/:userId/verification')
App::patch('/v1/users/:userId/verification')
->desc('Update email verification')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.verification')
@@ -1636,7 +1649,7 @@ Http::patch('/v1/users/:userId/verification')
$response->dynamic($user, Response::MODEL_USER);
});
Http::patch('/v1/users/:userId/prefs')
App::patch('/v1/users/:userId/prefs')
->desc('Update user preferences')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.prefs')
@@ -1675,7 +1688,7 @@ Http::patch('/v1/users/:userId/prefs')
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
});
Http::patch('/v1/users/:userId/targets/:targetId')
App::patch('/v1/users/:userId/targets/:targetId')
->desc('Update user target')
->groups(['api', 'users'])
->label('audits.event', 'target.update')
@@ -1778,7 +1791,7 @@ Http::patch('/v1/users/:userId/targets/:targetId')
->dynamic($target, Response::MODEL_TARGET);
});
Http::patch('/v1/users/:userId/mfa')
App::patch('/v1/users/:userId/mfa')
->desc('Update MFA')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.mfa')
@@ -1842,7 +1855,7 @@ Http::patch('/v1/users/:userId/mfa')
$response->dynamic($user, Response::MODEL_USER);
});
Http::get('/v1/users/:userId/mfa/factors')
App::get('/v1/users/:userId/mfa/factors')
->desc('List factors')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -1901,7 +1914,7 @@ Http::get('/v1/users/:userId/mfa/factors')
$response->dynamic($factors, Response::MODEL_MFA_FACTORS);
});
Http::get('/v1/users/:userId/mfa/recovery-codes')
App::get('/v1/users/:userId/mfa/recovery-codes')
->desc('Get MFA recovery codes')
->groups(['api', 'users'])
->label('scope', 'users.read')
@@ -1962,7 +1975,7 @@ Http::get('/v1/users/:userId/mfa/recovery-codes')
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
});
Http::patch('/v1/users/:userId/mfa/recovery-codes')
App::patch('/v1/users/:userId/mfa/recovery-codes')
->desc('Create MFA recovery codes')
->groups(['api', 'users'])
->label('event', 'users.[userId].create.mfa.recovery-codes')
@@ -2034,7 +2047,7 @@ Http::patch('/v1/users/:userId/mfa/recovery-codes')
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
});
Http::put('/v1/users/:userId/mfa/recovery-codes')
App::put('/v1/users/:userId/mfa/recovery-codes')
->desc('Update MFA recovery codes (regenerate)')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.mfa.recovery-codes')
@@ -2106,7 +2119,7 @@ Http::put('/v1/users/:userId/mfa/recovery-codes')
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
});
Http::delete('/v1/users/:userId/mfa/authenticators/:type')
App::delete('/v1/users/:userId/mfa/authenticators/:type')
->desc('Delete authenticator')
->groups(['api', 'users'])
->label('event', 'users.[userId].delete.mfa')
@@ -2176,7 +2189,7 @@ Http::delete('/v1/users/:userId/mfa/authenticators/:type')
$response->noContent();
});
Http::post('/v1/users/:userId/sessions')
App::post('/v1/users/:userId/sessions')
->desc('Create session')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -2269,7 +2282,7 @@ Http::post('/v1/users/:userId/sessions')
->dynamic($session, Response::MODEL_SESSION);
});
Http::post('/v1/users/:userId/tokens')
App::post('/v1/users/:userId/tokens')
->desc('Create token')
->groups(['api', 'users'])
->label('event', 'users.[userId].tokens.[tokenId].create')
@@ -2334,7 +2347,7 @@ Http::post('/v1/users/:userId/tokens')
->dynamic($token, Response::MODEL_TOKEN);
});
Http::delete('/v1/users/:userId/sessions/:sessionId')
App::delete('/v1/users/:userId/sessions/:sessionId')
->desc('Delete user session')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.[sessionId].delete')
@@ -2385,7 +2398,7 @@ Http::delete('/v1/users/:userId/sessions/:sessionId')
$response->noContent();
});
Http::delete('/v1/users/:userId/sessions')
App::delete('/v1/users/:userId/sessions')
->desc('Delete user sessions')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.delete')
@@ -2435,7 +2448,7 @@ Http::delete('/v1/users/:userId/sessions')
$response->noContent();
});
Http::delete('/v1/users/:userId')
App::delete('/v1/users/:userId')
->desc('Delete user')
->groups(['api', 'users'])
->label('event', 'users.[userId].delete')
@@ -2487,7 +2500,7 @@ Http::delete('/v1/users/:userId')
$response->noContent();
});
Http::delete('/v1/users/:userId/targets/:targetId')
App::delete('/v1/users/:userId/targets/:targetId')
->desc('Delete user target')
->groups(['api', 'users'])
->label('audits.event', 'target.delete')
@@ -2545,7 +2558,7 @@ Http::delete('/v1/users/:userId/targets/:targetId')
$response->noContent();
});
Http::delete('/v1/users/identities/:identityId')
App::delete('/v1/users/identities/:identityId')
->desc('Delete identity')
->groups(['api', 'users'])
->label('event', 'users.[userId].identities.[identityId].delete')
@@ -2588,7 +2601,7 @@ Http::delete('/v1/users/identities/:identityId')
return $response->noContent();
});
Http::post('/v1/users/:userId/jwts')
App::post('/v1/users/:userId/jwts')
->desc('Create user JWT')
->groups(['api', 'users'])
->label('scope', 'users.write')
@@ -2645,7 +2658,7 @@ Http::post('/v1/users/:userId/jwts')
])]), Response::MODEL_JWT);
});
Http::get('/v1/users/usage')
App::get('/v1/users/usage')
->desc('Get users usage stats')
->groups(['api', 'users'])
->label('scope', 'users.read')
File diff suppressed because it is too large Load Diff
+64 -88
View File
@@ -8,7 +8,7 @@ use Appwrite\Auth\Key;
use Appwrite\Event\Certificate;
use Appwrite\Event\Delete as DeleteEvent;
use Appwrite\Event\Event;
use Appwrite\Event\Execution;
use Appwrite\Event\Func;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Network\Cors;
@@ -34,6 +34,7 @@ use Appwrite\Utopia\View;
use Executor\Executor;
use MaxMind\Db\Reader;
use Swoole\Http\Request as SwooleRequest;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
@@ -45,7 +46,6 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Domains\Domain;
use Utopia\DSN\DSN;
use Utopia\Http;
use Utopia\Locale\Locale;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Log;
@@ -60,7 +60,7 @@ Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey, DeleteEvent $queueForDeletes, int $executionsRetentionCount)
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey, DeleteEvent $queueForDeletes, int $executionsRetentionCount)
{
$host = $request->getHostname() ?? '';
if (!empty($previewHostname)) {
@@ -85,32 +85,22 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
$platformHostnames = $platform['hostnames'] ?? [];
if ($rule->isEmpty()) {
$denyDomains = [];
$denyEnvVars = [
System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''),
System::getEnv('_APP_DOMAIN_FUNCTIONS', ''),
System::getEnv('_APP_DOMAIN_SITES', ''),
];
foreach ($denyEnvVars as $denyEnvVar) {
foreach (\explode(',', $denyEnvVar) as $denyDomain) {
if (empty($denyDomain)) {
continue;
}
$denyDomains[] = $denyDomain;
}
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
$appDomainFunctions = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
$appDomainSites = System::getEnv('_APP_DOMAIN_SITES', '');
if (!empty($appDomainFunctionsFallback) && \str_ends_with($host, $appDomainFunctionsFallback)) {
$appDomainFunctions = $appDomainFunctionsFallback;
}
foreach ($denyDomains as $denyDomain) {
if ($host === $denyDomain) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.', view: $errorView);
}
if ($host === $appDomainFunctions || $host === $appDomainSites) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.', view: $errorView);
}
if (\str_ends_with($host, $denyDomain)) {
$exception = new AppwriteException(AppwriteException::RULE_NOT_FOUND, 'This domain is not connected to any Appwrite resources. Visit domains tab under function/site settings to configure it.', view: $errorView);
if (\str_ends_with($host, $appDomainFunctions) || \str_ends_with($host, $appDomainSites)) {
$exception = new AppwriteException(AppwriteException::RULE_NOT_FOUND, 'This domain is not connected to any Appwrite resources. Visit domains tab under function/site settings to configure it.', view: $errorView);
$exception->addCTA('Start with this domain', $url . '/console');
throw $exception;
}
$exception->addCTA('Start with this domain', $url . '/console');
throw $exception;
}
if (!in_array($host, $platformHostnames)) {
@@ -696,11 +686,14 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
throw $th;
}
} finally {
if ($type === 'function' || $type === 'site') {
$queueForExecutions
if ($type === 'function') {
$queueForFunctions
->setType(Func::TYPE_ASYNC_WRITE)
->setExecution($execution)
->setProject($project)
->trigger();
} elseif ($type === 'site') { // TODO: Move it to logs worker later
$dbForProject->createDocument('executions', $execution);
}
}
@@ -836,7 +829,7 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
}
}
Http::init()
App::init()
->groups(['api'])
->inject('project')
->inject('mode')
@@ -846,7 +839,7 @@ Http::init()
}
});
Http::init()
App::init()
->groups(['database', 'functions', 'sites', 'messaging'])
->inject('project')
->inject('request')
@@ -859,7 +852,7 @@ Http::init()
}
});
Http::init()
App::init()
->groups(['api', 'web'])
->inject('utopia')
->inject('swooleRequest')
@@ -874,7 +867,7 @@ Http::init()
->inject('geodb')
->inject('queueForStatsUsage')
->inject('queueForEvents')
->inject('queueForExecutions')
->inject('queueForFunctions')
->inject('executor')
->inject('platform')
->inject('isResourceBlocked')
@@ -885,7 +878,7 @@ Http::init()
->inject('authorization')
->inject('queueForDeletes')
->inject('executionsRetentionCount')
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Execution $queueForExecutions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Func $queueForFunctions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
/*
* Appwrite Router
*/
@@ -893,7 +886,7 @@ Http::init()
$platformHostnames = $platform['hostnames'] ?? [];
// Only run Router when external domain
if (!\in_array($hostname, $platformHostnames) || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -947,15 +940,7 @@ Http::init()
$endDomain->getRegisterable() !== ''
);
$localHosts = ['localhost','localhost:'.$request->getPort()];
$migrationHost = System::getEnv('_APP_MIGRATION_HOST');
if (!empty($migrationHost)) {
$localHosts[] = $migrationHost;
$localHosts[] = $migrationHost.':'.$request->getPort();
}
$isLocalHost = in_array($request->getHostname(), $localHosts);
$isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:' . $request->getPort();
$isIpAddress = filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false;
$isConsoleProject = $project->getAttribute('$id', '') === 'console';
@@ -1002,7 +987,7 @@ Http::init()
}
if (System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if ($request->getProtocol() !== 'https' && !in_array(($swooleRequest->header['host'] ?? ''), $localHosts)) { // localhost allowed for proxy
if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost') { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
if ($request->getMethod() !== Request::METHOD_GET) {
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.');
}
@@ -1017,7 +1002,7 @@ Http::init()
*
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
Http::init()
App::init()
->groups(['api', 'web'])
->inject('request')
->inject('response')
@@ -1057,7 +1042,7 @@ Http::init()
/**
* Automatic certificate generation
*/
Http::init()
App::init()
->groups(['api', 'web'])
->inject('request')
->inject('console')
@@ -1109,28 +1094,19 @@ Http::init()
// 5. Create new rule
$owner = '';
$fallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
$funcDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
$siteDomain = System::getEnv('_APP_DOMAIN_SITES', '');
// Mark owner as Appwrite if its appwrite-owned domain
$appwriteDomains = [];
$appwriteDomainEnvs = [
System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''),
System::getEnv('_APP_DOMAIN_FUNCTIONS', ''),
System::getEnv('_APP_DOMAIN_SITES', ''),
];
foreach ($appwriteDomainEnvs as $appwriteDomainEnv) {
foreach (\explode(',', $appwriteDomainEnv) as $appwriteDomain) {
if (empty($appwriteDomain)) {
continue;
}
$appwriteDomains[] = $appwriteDomain;
}
if (!empty($fallback) && \str_ends_with($domain->get(), $fallback)) {
$funcDomain = $fallback;
}
foreach ($appwriteDomains as $appwriteDomain) {
if (\str_ends_with($domain->get(), $appwriteDomain)) {
$owner = 'Appwrite';
break;
}
if (
(!empty($funcDomain) && \str_ends_with($domain->get(), $funcDomain)) ||
(!empty($siteDomain) && \str_ends_with($domain->get(), $siteDomain))
) {
$owner = 'Appwrite';
}
$ruleId = $isMd5 ? md5($domain->get()) : ID::unique();
@@ -1162,7 +1138,7 @@ Http::init()
});
});
Http::options()
App::options()
->inject('utopia')
->inject('swooleRequest')
->inject('request')
@@ -1172,7 +1148,7 @@ Http::options()
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForExecutions')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
@@ -1185,14 +1161,14 @@ Http::options()
->inject('authorization')
->inject('queueForDeletes')
->inject('executionsRetentionCount')
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
/*
* Appwrite Router
*/
$platformHostnames = $platform['hostnames'] ?? [];
// Only run Router when external domain
if (!in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1216,7 +1192,7 @@ Http::options()
->trigger();
});
Http::error()
App::error()
->inject('error')
->inject('utopia')
->inject('request')
@@ -1227,7 +1203,7 @@ Http::error()
->inject('queueForStatsUsage')
->inject('devKey')
->inject('authorization')
->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage, Document $devKey, Authorization $authorization) {
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage, Document $devKey, Authorization $authorization) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
$class = \get_class($error);
@@ -1252,7 +1228,7 @@ Http::error()
}
switch ($class) {
case Utopia\Exception::class:
case 'Utopia\Exception':
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error);
switch ($code) {
case 400:
@@ -1263,10 +1239,10 @@ Http::error()
break;
}
break;
case Utopia\Database\Exception\Authorization::class:
case 'Utopia\Database\Exception\Authorization':
$error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED);
break;
case Utopia\Database\Exception\Timeout::class:
case 'Utopia\Database\Exception\Timeout':
$error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error);
break;
}
@@ -1503,7 +1479,7 @@ Http::error()
$type = $error->getType();
$output = Http::isDevelopment() ? [
$output = App::isDevelopment() ? [
'message' => $message,
'code' => $code,
'file' => $file,
@@ -1527,7 +1503,7 @@ Http::error()
$template = $error->getView() ?? (($route) ? $route->getLabel('error', null) : null);
// TODO: Ideally use group 'api' here, but all wildcard routes seem to have 'api' at the moment
if (empty($route) || !\str_starts_with($route->getPath(), '/v1')) {
if (!\str_starts_with($route->getPath(), '/v1')) {
$template = __DIR__ . '/../views/general/error.phtml';
}
@@ -1536,7 +1512,7 @@ Http::error()
$layout
->setParam('title', $project->getAttribute('name') . ' - Error')
->setParam('development', Http::isDevelopment())
->setParam('development', App::isDevelopment())
->setParam('projectName', $project->getAttribute('name'))
->setParam('projectURL', $project->getAttribute('url'))
->setParam('message', $output['message'] ?? '')
@@ -1555,7 +1531,7 @@ Http::error()
);
});
Http::get('/robots.txt')
App::get('/robots.txt')
->desc('Robots.txt File')
->label('scope', 'public')
->label('docs', false)
@@ -1568,7 +1544,7 @@ Http::get('/robots.txt')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForExecutions')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
@@ -1578,19 +1554,19 @@ Http::get('/robots.txt')
->inject('authorization')
->inject('queueForDeletes')
->inject('executionsRetentionCount')
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
$platformHostnames = $platform['hostnames'] ?? [];
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
$utopia->getRoute()?->label('router', true);
}
}
});
Http::get('/humans.txt')
App::get('/humans.txt')
->desc('Humans.txt File')
->label('scope', 'public')
->label('docs', false)
@@ -1603,7 +1579,7 @@ Http::get('/humans.txt')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForExecutions')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
@@ -1613,19 +1589,19 @@ Http::get('/humans.txt')
->inject('authorization')
->inject('queueForDeletes')
->inject('executionsRetentionCount')
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
$platformHostnames = $platform['hostnames'] ?? [];
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
$utopia->getRoute()?->label('router', true);
}
}
});
Http::get('/.well-known/acme-challenge/*')
App::get('/.well-known/acme-challenge/*')
->desc('SSL Verification')
->label('scope', 'public')
->label('docs', false)
@@ -1678,7 +1654,7 @@ Http::get('/.well-known/acme-challenge/*')
include_once __DIR__ . '/shared/api.php';
include_once __DIR__ . '/shared/api/auth.php';
Http::get('/v1/ping')
App::get('/v1/ping')
->groups(['api', 'general'])
->desc('Test the connection between the Appwrite and the SDK.')
->label('scope', 'global')
@@ -1728,7 +1704,7 @@ Http::get('/v1/ping')
});
// Preview authorization
Http::get('/_appwrite/authorize')
App::get('/_appwrite/authorize')
->inject('request')
->inject('response')
->inject('previewHostname')
@@ -1755,7 +1731,7 @@ Http::get('/_appwrite/authorize')
->redirect($protocol . '://' . $host . $path);
});
Http::wildcard()
App::wildcard()
->groups(['api'])
->label('scope', 'global')
->action(function () {
+16 -13
View File
@@ -5,6 +5,7 @@ global $utopia, $request, $response;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -12,14 +13,13 @@ use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\UID;
use Utopia\Http;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub;
Http::get('/v1/mock/tests/general/oauth2')
App::get('/v1/mock/tests/general/oauth2')
->desc('OAuth Login')
->groups(['mock'])
->label('scope', 'public')
@@ -35,7 +35,7 @@ Http::get('/v1/mock/tests/general/oauth2')
$response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state]));
});
Http::get('/v1/mock/tests/locale')
App::get('/v1/mock/tests/locale')
->desc('Mock locale translation key')
->groups(['mock'])
->label('scope', 'public')
@@ -54,7 +54,7 @@ Http::get('/v1/mock/tests/locale')
$response->send($locale->getText('mock'));
});
Http::get('/v1/mock/tests/general/oauth2/token')
App::get('/v1/mock/tests/general/oauth2/token')
->desc('OAuth2 Token')
->groups(['mock'])
->label('scope', 'public')
@@ -100,7 +100,7 @@ Http::get('/v1/mock/tests/general/oauth2/token')
}
});
Http::get('/v1/mock/tests/general/oauth2/user')
App::get('/v1/mock/tests/general/oauth2/user')
->desc('OAuth2 User')
->groups(['mock'])
->label('scope', 'public')
@@ -121,7 +121,7 @@ Http::get('/v1/mock/tests/general/oauth2/user')
]);
});
Http::get('/v1/mock/tests/general/oauth2/user-unverified')
App::get('/v1/mock/tests/general/oauth2/user-unverified')
->desc('OAuth2 User Unverified')
->groups(['mock'])
->label('scope', 'public')
@@ -142,7 +142,7 @@ Http::get('/v1/mock/tests/general/oauth2/user-unverified')
]);
});
Http::get('/v1/mock/tests/general/oauth2/success')
App::get('/v1/mock/tests/general/oauth2/success')
->desc('OAuth2 Success')
->groups(['mock'])
->label('scope', 'public')
@@ -155,7 +155,7 @@ Http::get('/v1/mock/tests/general/oauth2/success')
]);
});
Http::get('/v1/mock/tests/general/oauth2/failure')
App::get('/v1/mock/tests/general/oauth2/failure')
->desc('OAuth2 Failure')
->groups(['mock'])
->label('scope', 'public')
@@ -170,7 +170,7 @@ Http::get('/v1/mock/tests/general/oauth2/failure')
]);
});
Http::post('/v1/mock/api-key-unprefixed')
App::post('/v1/mock/api-key-unprefixed')
->desc('Create API Key (without standard prefix)')
->groups(['mock', 'api', 'projects'])
->label('scope', 'public')
@@ -191,7 +191,7 @@ Http::post('/v1/mock/api-key-unprefixed')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$scopes = array_keys(Config::getParam('projectScopes'));
$scopes = array_keys(Config::getParam('scopes'));
$key = new Document([
'$id' => ID::unique(),
@@ -200,6 +200,9 @@ Http::post('/v1/mock/api-key-unprefixed')
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
// TODO: @hmacr Remove `projectInternalId` and `projectId` column writes before deleting the column.
'projectInternalId' => $project->getSequence(),
'projectId' => $project->getId(),
'resourceInternalId' => $project->getSequence(),
'resourceId' => $project->getId(),
'resourceType' => 'projects',
@@ -220,7 +223,7 @@ Http::post('/v1/mock/api-key-unprefixed')
->dynamic($key, Response::MODEL_KEY);
});
Http::get('/v1/mock/github/callback')
App::get('/v1/mock/github/callback')
->desc('Create installation document using GitHub installation id')
->groups(['mock', 'api', 'vcs'])
->label('scope', 'public')
@@ -280,12 +283,12 @@ Http::get('/v1/mock/github/callback')
]);
});
Http::shutdown()
App::shutdown()
->groups(['mock'])
->inject('utopia')
->inject('response')
->inject('request')
->action(function (Http $utopia, Response $response, Request $request) {
->action(function (App $utopia, Response $response, Request $request) {
$result = [];
$route = $utopia->getRoute();
+29 -49
View File
@@ -21,6 +21,7 @@ use Appwrite\Utopia\Database\Documents\User;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
@@ -30,7 +31,6 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Http;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Validator\WhiteList;
@@ -74,7 +74,7 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
return $label;
};
Http::init()
App::init()
->groups(['api'])
->inject('utopia')
->inject('request')
@@ -89,7 +89,7 @@ Http::init()
->inject('team')
->inject('apiKey')
->inject('authorization')
->action(function (Http $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) {
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) {
$route = $utopia->getRoute();
/**
@@ -157,6 +157,10 @@ Http::init()
// Step 5: API Key Authentication
if (!empty($apiKey)) {
// Verify no user session exists simultaneously
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
}
// Check if key is expired
if ($apiKey->isExpired()) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
@@ -185,38 +189,23 @@ Http::init()
}
// For standard keys, update last accessed time
if (\in_array($apiKey->getType(), [API_KEY_STANDARD, API_KEY_ORGANIZATION, API_KEY_ACCOUNT])) {
$dbKey = null;
if (!empty($apiKey->getProjectId())) {
$dbKey = $project->find(
key: 'secret',
find: $request->getHeader('x-appwrite-key', ''),
subject: 'keys'
);
} elseif (!empty($apiKey->getUserId())) {
$dbKey = $user->find(
key: 'secret',
find: $request->getHeader('x-appwrite-key', ''),
subject: 'keys'
);
} elseif (!empty($apiKey->getTeamId())) {
$dbKey = $team->find(
key: 'secret',
find: $request->getHeader('x-appwrite-key', ''),
subject: 'keys'
);
}
if ($apiKey->getType() === API_KEY_STANDARD) {
$dbKey = $project->find(
key: 'secret',
find: $request->getHeader('x-appwrite-key', ''),
subject: 'keys'
);
if (!$dbKey) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$updates = new Document();
$accessedAt = $dbKey->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$updates->setAttribute('accessedAt', DateTime::now());
$dbKey->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
@@ -227,21 +216,12 @@ Http::init()
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$dbKey->setAttribute('sdks', $sdks);
$updates->setAttribute('sdks', $sdks);
$updates->setAttribute('accessedAt', Datetime::now());
}
}
if (!$updates->isEmpty()) {
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->updateDocument('keys', $dbKey->getId(), $updates));
if (!empty($apiKey->getProjectId())) {
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->purgeCachedDocument('projects', $project->getId()));
} elseif (!empty($apiKey->getUserId())) {
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->purgeCachedDocument('users', $user->getId()));
} elseif (!empty($apiKey->getTeamId())) {
$dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->purgeCachedDocument('teams', $team->getId()));
/** Update access time as well */
$dbKey->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
@@ -356,7 +336,7 @@ Http::init()
}
});
Http::init()
App::init()
->groups(['api'])
->inject('utopia')
->inject('request')
@@ -382,7 +362,7 @@ Http::init()
->inject('telemetry')
->inject('platform')
->inject('authorization')
->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) {
$route = $utopia->getRoute();
@@ -592,7 +572,7 @@ Http::init()
}
});
Http::init()
App::init()
->groups(['session'])
->inject('user')
->inject('request')
@@ -612,14 +592,14 @@ Http::init()
* Delete older sessions if the number of sessions have crossed
* the session limit set for the project
*/
Http::shutdown()
App::shutdown()
->groups(['session'])
->inject('utopia')
->inject('request')
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (Http $utopia, Request $request, Response $response, Document $project, Database $dbForProject) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Database $dbForProject) {
$sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT;
$session = $response->getPayload();
$userId = $session['userId'] ?? '';
@@ -646,7 +626,7 @@ Http::shutdown()
$dbForProject->purgeCachedDocument('users', $userId);
});
Http::shutdown()
App::shutdown()
->groups(['api'])
->inject('utopia')
->inject('request')
@@ -667,7 +647,7 @@ Http::shutdown()
->inject('authorization')
->inject('timelimit')
->inject('eventProcessor')
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor) use ($parseLabel) {
->action(function (App $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor) use ($parseLabel) {
$responsePayload = $response->getPayload();
@@ -892,7 +872,7 @@ Http::shutdown()
}
});
Http::init()
App::init()
->groups(['usage'])
->action(function () {
if (System::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
+4 -4
View File
@@ -4,14 +4,14 @@ use Appwrite\Extend\Exception;
use Appwrite\Utopia\Database\Documents\User;
use Appwrite\Utopia\Request;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Http;
use Utopia\System\System;
Http::init()
App::init()
->groups(['mfaProtected'])
->inject('session')
->action(function (Document $session) {
@@ -30,14 +30,14 @@ Http::init()
}
});
Http::init()
App::init()
->groups(['auth'])
->inject('utopia')
->inject('request')
->inject('project')
->inject('geodb')
->inject('authorization')
->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
->action(function (App $utopia, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
if (!empty($denylist && $project->getId() === 'console')) {
$countries = explode(',', $denylist);
+2 -2
View File
@@ -1,10 +1,10 @@
<?php
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Http;
Http::get('/versions')
App::get('/versions')
->desc('Get Version')
->groups(['home', 'web'])
->label('scope', 'public')
+14 -46
View File
@@ -11,6 +11,7 @@ use Swoole\Http\Server;
use Swoole\Process;
use Swoole\Table;
use Swoole\Timer;
use Utopia\App;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Adapter\SQL as AuditAdapterSQL;
use Utopia\Audit\Audit;
@@ -26,7 +27,6 @@ use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Http;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Pools\Group;
@@ -100,19 +100,11 @@ function dispatch(Server $server, int $fd, int $type, $data = null): int
$risky = false;
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
$risky = true;
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
$risky = true;
} elseif ($domains->get(md5($domain), 'value') === 1) {
// executions request coming from custom domain
$risky = true;
} else {
foreach (\explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS')) as $riskyDomain) {
if (empty($riskyDomain)) {
continue;
}
if (str_ends_with($domain, $riskyDomain)) {
$risky = true;
break;
}
}
}
if ($risky) {
@@ -181,7 +173,7 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server) {
include __DIR__ . '/controllers/general.php';
function createDatabase(Http $app, string $resourceKey, string $dbName, array $collections, mixed $pools, ?callable $extraSetup = null): void
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
{
$max = 10;
$sleep = 1;
@@ -254,12 +246,12 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
}
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
$app = new Http('UTC');
$app = new App('UTC');
go(function () use ($register, $app) {
$pools = $register->get('pools');
/** @var Group $pools */
Http::setResource('pools', fn () => $pools);
App::setResource('pools', fn () => $pools);
/** @var array $collections */
$collections = Config::getParam('collections', []);
@@ -441,8 +433,8 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
});
$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register) {
Http::setResource('swooleRequest', fn () => $swooleRequest);
Http::setResource('swooleResponse', fn () => $swooleResponse);
App::setResource('swooleRequest', fn () => $swooleRequest);
App::setResource('swooleResponse', fn () => $swooleResponse);
$request = new Request($swooleRequest);
$response = new Response($swooleResponse);
@@ -459,12 +451,12 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
return;
}
$app = new Http('UTC');
$app = new App('UTC');
$app->setCompression(System::getEnv('_APP_COMPRESSION_ENABLED', 'enabled') === 'enabled');
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
$pools = $register->get('pools');
Http::setResource('pools', fn () => $pools);
App::setResource('pools', fn () => $pools);
try {
$authorization = $app->getResource('authorization');
@@ -546,7 +538,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$swooleResponse->setStatusCode(500);
$output = ((Http::isDevelopment())) ? [
$output = ((App::isDevelopment())) ? [
'message' => 'Error: ' . $th->getMessage(),
'code' => 500,
'file' => $th->getFile(),
@@ -567,8 +559,8 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
$lastSyncUpdate = null;
$pools = $register->get('pools');
Http::setResource('pools', fn () => $pools);
$app = new Http('UTC');
App::setResource('pools', fn () => $pools);
$app = new App('UTC');
/** @var Utopia\Database\Database $dbForPlatform */
$dbForPlatform = $app->getResource('dbForPlatform');
@@ -599,33 +591,9 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
$sum = count($results);
foreach ($results as $document) {
$domain = $document->getAttribute('domain');
$denyDomains = [];
$denyEnvVars = [
System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''),
System::getEnv('_APP_DOMAIN_FUNCTIONS', ''),
System::getEnv('_APP_DOMAIN_SITES', ''),
];
foreach ($denyEnvVars as $denyEnvVar) {
foreach (\explode(',', $denyEnvVar) as $denyDomain) {
if (empty($denyDomain)) {
continue;
}
$denyDomains[] = $denyDomain;
}
}
$isDenyDomain = false;
foreach ($denyDomains as $denyDomain) {
if (str_ends_with($domain, $denyDomain)) {
$isDenyDomain = true;
}
}
if ($isDenyDomain) {
if (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS')) || str_ends_with($domain, System::getEnv('_APP_DOMAIN_SITES'))) {
continue;
}
$domains->set(md5($domain), ['value' => 1]);
}
$latestDocument = !empty(array_key_last($results)) ? $results[array_key_last($results)] : null;
+1 -3
View File
@@ -22,9 +22,7 @@ Config::load('collections', __DIR__ . '/../config/collections.php', $configAdapt
Config::load('frameworks', __DIR__ . '/../config/frameworks.php', $configAdapter);
Config::load('usage', __DIR__ . '/../config/usage.php', $configAdapter);
Config::load('roles', __DIR__ . '/../config/roles.php', $configAdapter); // User roles and scopes
Config::load('projectScopes', __DIR__ . '/../config/scopes/project.php', $configAdapter);
Config::load('organizationScopes', __DIR__ . '/../config/scopes/organization.php', $configAdapter);
Config::load('accountScopes', __DIR__ . '/../config/scopes/account.php', $configAdapter);
Config::load('scopes', __DIR__ . '/../config/scopes.php', $configAdapter); // User roles and scopes
Config::load('services', __DIR__ . '/../config/services.php', $configAdapter); // List of services
Config::load('variables', __DIR__ . '/../config/variables.php', $configAdapter); // List of env variables
Config::load('regions', __DIR__ . '/../config/regions.php', $configAdapter); // List of available regions
-10
View File
@@ -5,8 +5,6 @@ use Appwrite\Platform\Modules\Compute\Specification;
const APP_NAME = 'Appwrite';
const APP_DOMAIN = 'appwrite.io';
const APP_VIEWS_DIR = __DIR__ . '/../views';
// Email
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
const APP_EMAIL_SECURITY = ''; // Default security email address
@@ -196,7 +194,6 @@ const DELETE_TYPE_SITES = 'sites';
const DELETE_TYPE_FUNCTIONS = 'functions';
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
const DELETE_TYPE_USERS = 'users';
const DELETE_TYPE_TEAMS = 'teams';
const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects';
const DELETE_TYPE_EXECUTIONS = 'executions';
const DELETE_TYPE_EXECUTIONS_LIMIT = 'executionsLimit';
@@ -252,8 +249,6 @@ const MESSAGE_TYPE_PUSH = 'push';
// API key types
const API_KEY_STANDARD = 'standard';
const API_KEY_DYNAMIC = 'dynamic';
const API_KEY_ORGANIZATION = 'organization';
const API_KEY_ACCOUNT = 'account';
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
@@ -371,7 +366,6 @@ const RESOURCE_TYPE_TOPICS = 'topics';
const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers';
const RESOURCE_TYPE_MESSAGES = 'messages';
const RESOURCE_TYPE_EXECUTIONS = 'executions';
const RESOURCE_TYPE_VCS = 'vcs';
// Resource types for Tokens
const TOKENS_RESOURCE_TYPE_FILES = 'files';
@@ -386,7 +380,3 @@ const SCHEDULE_RESOURCE_TYPE_MESSAGE = 'message';
/** Preview cookie */
const COOKIE_NAME_PREVIEW = 'a_jwt_console';
// Cache Reconnect
const CACHE_RECONNECT_MAX_RETRIES = 2;
const CACHE_RECONNECT_RETRY_DELAY = 1000;
-31
View File
@@ -433,34 +433,3 @@ Database::addFilter(
return $value;
}
);
Database::addFilter(
'subQueryOrganizationKeys',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
->find('keys', [
Query::equal('resourceType', ['teams']),
Query::equal('resourceInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryAccountKeys',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
->find('keys', [
Query::equal('resourceType', ['users']),
Query::equal('resourceInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
+1 -17
View File
@@ -20,15 +20,11 @@ use Appwrite\Utopia\Response\Model\AttributeInteger;
use Appwrite\Utopia\Response\Model\AttributeIP;
use Appwrite\Utopia\Response\Model\AttributeLine;
use Appwrite\Utopia\Response\Model\AttributeList;
use Appwrite\Utopia\Response\Model\AttributeLongtext;
use Appwrite\Utopia\Response\Model\AttributeMediumtext;
use Appwrite\Utopia\Response\Model\AttributePoint;
use Appwrite\Utopia\Response\Model\AttributePolygon;
use Appwrite\Utopia\Response\Model\AttributeRelationship;
use Appwrite\Utopia\Response\Model\AttributeString;
use Appwrite\Utopia\Response\Model\AttributeText;
use Appwrite\Utopia\Response\Model\AttributeURL;
use Appwrite\Utopia\Response\Model\AttributeVarchar;
use Appwrite\Utopia\Response\Model\AuthProvider;
use Appwrite\Utopia\Response\Model\BaseList;
use Appwrite\Utopia\Response\Model\Branch;
@@ -45,15 +41,11 @@ use Appwrite\Utopia\Response\Model\ColumnInteger;
use Appwrite\Utopia\Response\Model\ColumnIP;
use Appwrite\Utopia\Response\Model\ColumnLine;
use Appwrite\Utopia\Response\Model\ColumnList;
use Appwrite\Utopia\Response\Model\ColumnLongtext;
use Appwrite\Utopia\Response\Model\ColumnMediumtext;
use Appwrite\Utopia\Response\Model\ColumnPoint;
use Appwrite\Utopia\Response\Model\ColumnPolygon;
use Appwrite\Utopia\Response\Model\ColumnRelationship;
use Appwrite\Utopia\Response\Model\ColumnString;
use Appwrite\Utopia\Response\Model\ColumnText;
use Appwrite\Utopia\Response\Model\ColumnURL;
use Appwrite\Utopia\Response\Model\ColumnVarchar;
use Appwrite\Utopia\Response\Model\ConsoleVariables;
use Appwrite\Utopia\Response\Model\Continent;
use Appwrite\Utopia\Response\Model\Country;
@@ -185,7 +177,7 @@ Response::setModel(new BaseList('Deployments List', Response::MODEL_DEPLOYMENT_L
Response::setModel(new BaseList('Executions List', Response::MODEL_EXECUTION_LIST, 'executions', Response::MODEL_EXECUTION));
Response::setModel(new BaseList('Projects List', Response::MODEL_PROJECT_LIST, 'projects', Response::MODEL_PROJECT, true, false));
Response::setModel(new BaseList('Webhooks List', Response::MODEL_WEBHOOK_LIST, 'webhooks', Response::MODEL_WEBHOOK, true, false));
Response::setModel(new BaseList('API Keys List', Response::MODEL_KEY_LIST, 'keys', Response::MODEL_KEY, true, true));
Response::setModel(new BaseList('API Keys List', Response::MODEL_KEY_LIST, 'keys', Response::MODEL_KEY, true, false));
Response::setModel(new BaseList('Dev Keys List', Response::MODEL_DEV_KEY_LIST, 'devKeys', Response::MODEL_DEV_KEY, true, false));
Response::setModel(new BaseList('Auth Providers List', Response::MODEL_AUTH_PROVIDER_LIST, 'platforms', Response::MODEL_AUTH_PROVIDER, true, false));
Response::setModel(new BaseList('Platforms List', Response::MODEL_PLATFORM_LIST, 'platforms', Response::MODEL_PLATFORM, true, false));
@@ -230,10 +222,6 @@ Response::setModel(new AttributeRelationship());
Response::setModel(new AttributePoint());
Response::setModel(new AttributeLine());
Response::setModel(new AttributePolygon());
Response::setModel(new AttributeVarchar());
Response::setModel(new AttributeText());
Response::setModel(new AttributeMediumtext());
Response::setModel(new AttributeLongtext());
// Table API Models
Response::setModel(new Table());
@@ -252,10 +240,6 @@ Response::setModel(new ColumnRelationship());
Response::setModel(new ColumnPoint());
Response::setModel(new ColumnLine());
Response::setModel(new ColumnPolygon());
Response::setModel(new ColumnVarchar());
Response::setModel(new ColumnText());
Response::setModel(new ColumnMediumtext());
Response::setModel(new ColumnLongtext());
Response::setModel(new Index());
Response::setModel(new ColumnIndex());
Response::setModel(new Row());
+5 -17
View File
@@ -8,6 +8,7 @@ use Appwrite\URL\URL as AppwriteURL;
use MaxMind\Db\Reader;
use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Database\PDOProxy;
use Utopia\App;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
@@ -17,26 +18,22 @@ use Utopia\Database\Adapter\SQL;
use Utopia\Database\PDO;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
use Utopia\Http;
use Utopia\Logger\Adapter\AppSignal;
use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Logger;
use Utopia\Pools\Adapter\Stack as StackPool;
use Utopia\Pools\Adapter\Swoole as SwoolePool;
use Utopia\Pools\Group;
use Utopia\Pools\Pool;
use Utopia\Queue;
use Utopia\Registry\Registry;
use Utopia\System\System;
global $register;
$register = new Registry();
Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION));
App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
if (!Http::isProduction()) {
if (!App::isProduction()) {
// Allow specific domains to skip public domain validation in dev environment
// Useful for existing tests involving webhooks
PublicDomain::allow(['request-catcher-sms']);
@@ -288,9 +285,7 @@ $register->set('pools', function () {
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'),
};
$poolAdapter = System::getEnv('_APP_POOL_ADAPTER', default: 'stack') === 'swoole' ? new SwoolePool() : new StackPool();
$pool = new Pool($poolAdapter, $name, $poolSize, function () use ($type, $resource, $dsn) {
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) {
// Get Adapter
switch ($type) {
case 'database':
@@ -314,17 +309,10 @@ $register->set('pools', function () {
default => null
};
case 'cache':
$adapter = match ($dsn->getScheme()) {
return match ($dsn->getScheme()) {
'redis' => new RedisCache($resource()),
default => null
};
if ($adapter !== null) {
$adapter->setMaxRetries(CACHE_RECONNECT_MAX_RETRIES);
$adapter->setRetryDelay(CACHE_RECONNECT_RETRY_DELAY);
}
return $adapter;
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
}
+91 -187
View File
@@ -10,7 +10,6 @@ use Appwrite\Event\Certificate;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Execution;
use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
@@ -32,6 +31,7 @@ use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Executor\Executor;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\App;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Audit;
use Utopia\Auth\Hashes\Argon2;
@@ -52,7 +52,6 @@ use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Http;
use Utopia\Locale\Locale;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
@@ -75,118 +74,114 @@ use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
// Runtime Execution
Http::setResource('log', fn () => new Log());
Http::setResource('logger', function ($register) {
App::setResource('log', fn () => new Log());
App::setResource('logger', function ($register) {
return $register->get('logger');
}, ['register']);
Http::setResource('hooks', function ($register) {
App::setResource('hooks', function ($register) {
return $register->get('hooks');
}, ['register']);
global $register;
Http::setResource('register', fn () => $register);
Http::setResource('locale', function () {
App::setResource('register', fn () => $register);
App::setResource('locale', function () {
$locale = new Locale(System::getEnv('_APP_LOCALE', 'en'));
$locale->setFallback(System::getEnv('_APP_LOCALE', 'en'));
return $locale;
});
Http::setResource('localeCodes', function () {
App::setResource('localeCodes', function () {
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
});
// Queues
Http::setResource('publisher', function (Group $pools) {
App::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
Http::setResource('publisherDatabases', function (Publisher $publisher) {
App::setResource('publisherDatabases', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('publisherFunctions', function (Publisher $publisher) {
App::setResource('publisherFunctions', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('publisherMigrations', function (Publisher $publisher) {
App::setResource('publisherMigrations', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('publisherStatsUsage', function (Publisher $publisher) {
App::setResource('publisherStatsUsage', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('publisherMails', function (Publisher $publisher) {
App::setResource('publisherMails', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('publisherDeletes', function (Publisher $publisher) {
App::setResource('publisherDeletes', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('publisherMessaging', function (Publisher $publisher) {
App::setResource('publisherMessaging', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('publisherWebhooks', function (Publisher $publisher) {
App::setResource('publisherWebhooks', function (Publisher $publisher) {
return $publisher;
}, ['publisher']);
Http::setResource('queueForMessaging', function (Publisher $publisher) {
App::setResource('queueForMessaging', function (Publisher $publisher) {
return new Messaging($publisher);
}, ['publisher']);
Http::setResource('queueForMails', function (Publisher $publisher) {
App::setResource('queueForMails', function (Publisher $publisher) {
return new Mail($publisher);
}, ['publisher']);
Http::setResource('queueForBuilds', function (Publisher $publisher) {
App::setResource('queueForBuilds', function (Publisher $publisher) {
return new Build($publisher);
}, ['publisher']);
Http::setResource('queueForScreenshots', function (Publisher $publisher) {
App::setResource('queueForScreenshots', function (Publisher $publisher) {
return new Screenshot($publisher);
}, ['publisher']);
Http::setResource('queueForDatabase', function (Publisher $publisher) {
App::setResource('queueForDatabase', function (Publisher $publisher) {
return new EventDatabase($publisher);
}, ['publisher']);
Http::setResource('queueForDeletes', function (Publisher $publisher) {
App::setResource('queueForDeletes', function (Publisher $publisher) {
return new Delete($publisher);
}, ['publisher']);
Http::setResource('queueForEvents', function (Publisher $publisher) {
App::setResource('queueForEvents', function (Publisher $publisher) {
return new Event($publisher);
}, ['publisher']);
Http::setResource('queueForWebhooks', function (Publisher $publisher) {
App::setResource('queueForWebhooks', function (Publisher $publisher) {
return new Webhook($publisher);
}, ['publisher']);
Http::setResource('queueForRealtime', function () {
App::setResource('queueForRealtime', function () {
return new Realtime();
}, []);
Http::setResource('queueForStatsUsage', function (Publisher $publisher) {
App::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
Http::setResource('queueForAudits', function (Publisher $publisher) {
App::setResource('queueForAudits', function (Publisher $publisher) {
return new AuditEvent($publisher);
}, ['publisher']);
Http::setResource('queueForFunctions', function (Publisher $publisher) {
App::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
}, ['publisher']);
Http::setResource('queueForExecutions', function (Publisher $publisher) {
return new Execution($publisher);
}, ['publisher']);
Http::setResource('eventProcessor', function () {
App::setResource('eventProcessor', function () {
return new EventProcessor();
}, []);
Http::setResource('queueForCertificates', function (Publisher $publisher) {
App::setResource('queueForCertificates', function (Publisher $publisher) {
return new Certificate($publisher);
}, ['publisher']);
Http::setResource('queueForMigrations', function (Publisher $publisher) {
App::setResource('queueForMigrations', function (Publisher $publisher) {
return new Migration($publisher);
}, ['publisher']);
Http::setResource('queueForStatsResources', function (Publisher $publisher) {
App::setResource('queueForStatsResources', function (Publisher $publisher) {
return new StatsResources($publisher);
}, ['publisher']);
/**
* Platform configuration
*/
Http::setResource('platform', function () {
App::setResource('platform', function () {
return Config::getParam('platform', []);
}, []);
/**
* List of allowed request hostnames for the request.
*/
Http::setResource('allowedHostnames', function (array $platform, Document $project, Document $rule, Document $devKey, Request $request) {
App::setResource('allowedHostnames', function (array $platform, Document $project, Document $rule, Document $devKey, Request $request) {
$allowed = [...($platform['hostnames'] ?? [])];
/* Add platform configured hostnames */
@@ -202,26 +197,15 @@ Http::setResource('allowedHostnames', function (array $platform, Document $proje
}
$originHostname = parse_url($request->getOrigin(), PHP_URL_HOST);
$refererHostname = parse_url($request->getReferer(), PHP_URL_HOST);
$hostname = $originHostname;
if (empty($hostname)) {
$hostname = $refererHostname;
}
/* Add request hostname for preflight requests */
if ($request->getMethod() === 'OPTIONS') {
$allowed[] = $hostname;
$allowed[] = $originHostname;
}
/* Allow the request origin of rule */
if (!$rule->isEmpty() && !empty($rule->getAttribute('domain', ''))) {
$allowed[] = $rule->getAttribute('domain', '');
}
/* Allow the request origin if a dev key is found */
if (!$devKey->isEmpty() && !empty($hostname)) {
$allowed[] = $hostname;
/* Allow the request origin if a dev key or rule is found */
if ((!$rule->isEmpty() || !$devKey->isEmpty()) && !empty($originHostname)) {
$allowed[] = $originHostname;
}
return array_unique($allowed);
@@ -230,7 +214,7 @@ Http::setResource('allowedHostnames', function (array $platform, Document $proje
/**
* List of allowed request schemes for the request.
*/
Http::setResource('allowedSchemes', function (Document $project) {
App::setResource('allowedSchemes', function (Document $project) {
$allowed = [];
if (!$project->isEmpty() && $project->getId() !== 'console') {
@@ -250,13 +234,8 @@ Http::setResource('allowedSchemes', function (Document $project) {
/**
* Rule associated with a request origin.
*/
Http::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
App::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
$domain = \parse_url($request->getOrigin(), PHP_URL_HOST);
if (empty($domain)) {
$domain = \parse_url($request->getReferer(), PHP_URL_HOST);
}
if (empty($domain)) {
return new Document();
}
@@ -273,24 +252,7 @@ Http::setResource('rule', function (Request $request, Database $dbForPlatform, D
]) ?? new Document();
});
$permitsCurrentProject = $rule->getAttribute('projectInternalId', '') === $project->getSequence();
// Temporary implementation until custom wildcard domains are an official feature
// Allow trusted projects; Used for Console (website) previews
if (!$permitsCurrentProject && !$rule->isEmpty() && !empty($rule->getAttribute('projectId', ''))) {
$trustedProjects = [];
foreach (\explode(',', System::getEnv('_APP_CONSOLE_TRUSTED_PROJECTS', '')) as $trustedProject) {
if (empty($trustedProject)) {
continue;
}
$trustedProjects[] = $trustedProject;
}
if (\in_array($rule->getAttribute('projectId', ''), $trustedProjects)) {
$permitsCurrentProject = true;
}
}
if (!$permitsCurrentProject) {
if ($rule->getAttribute('projectInternalId') !== $project->getSequence()) {
return new Document();
}
@@ -300,7 +262,7 @@ Http::setResource('rule', function (Request $request, Database $dbForPlatform, D
/**
* CORS service
*/
Http::setResource('cors', fn (array $allowedHostnames) => new Cors(
App::setResource('cors', fn (array $allowedHostnames) => new Cors(
$allowedHostnames,
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: [
@@ -349,21 +311,21 @@ Http::setResource('cors', fn (array $allowedHostnames) => new Cors(
],
), ['allowedHostnames']);
Http::setResource('originValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
App::setResource('originValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
if (!$devKey->isEmpty()) {
return new URL();
}
return new Origin($allowedHostnames, $allowedSchemes);
}, ['devKey', 'allowedHostnames', 'allowedSchemes']);
Http::setResource('redirectValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
App::setResource('redirectValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
if (!$devKey->isEmpty()) {
return new URL();
}
return new Redirect($allowedHostnames, $allowedSchemes);
}, ['devKey', 'allowedHostnames', 'allowedSchemes']);
Http::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) {
App::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) {
/**
* Handles user authentication and session validation.
*
@@ -381,7 +343,6 @@ Http::setResource('user', function (string $mode, Document $project, Document $c
* 5. Regardless of the results from steps 1-4, attempts to fetch the JWT token.
* 6. If the JWT user has a valid session ID, updates the user variable with the user from `projectDB`,
* overwriting the previous value.
* 7. If account API key is passed, use user of the account API key as long as user ID header matches too
*/
$authorization->setDefaultStatus(true);
@@ -458,17 +419,12 @@ Http::setResource('user', function (string $mode, Document $project, Document $c
// }
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_JWT_AND_COOKIE_SET);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$payload = $jwt->decode($authJWT);
} catch (JWTException $error) {
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
}
$jwtUserId = $payload['userId'] ?? '';
if (!empty($jwtUserId)) {
if ($mode === APP_MODE_ADMIN) {
@@ -484,41 +440,13 @@ Http::setResource('user', function (string $mode, Document $project, Document $c
}
}
}
// Account based on account API key
$accountKey = $request->getHeader('x-appwrite-key', '');
$accountKeyUserId = $request->getHeader('x-appwrite-user', '');
if (!empty($accountKeyUserId) && !empty($accountKey)) {
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
}
$accountKeyUser = $dbForPlatform->getAuthorization()->skip(fn () => $dbForPlatform->getDocument('users', $accountKeyUserId));
if (!$accountKeyUser->isEmpty()) {
$key = $accountKeyUser->find(
key: 'secret',
find: $accountKey,
subject: 'keys'
);
if (!empty($key)) {
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) {
throw new Exception(Exception::ACCOUNT_KEY_EXPIRED);
}
$user = $accountKeyUser;
}
}
}
$dbForProject->setMetadata('user', $user->getId());
$dbForPlatform->setMetadata('user', $user->getId());
return $user;
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken', 'authorization']);
Http::setResource('project', function ($dbForPlatform, $request, $console, $authorization) {
App::setResource('project', function ($dbForPlatform, $request, $console, $authorization) {
/** @var Appwrite\Utopia\Request $request */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var Utopia\Database\Document $console */
@@ -534,7 +462,7 @@ Http::setResource('project', function ($dbForPlatform, $request, $console, $auth
return $project;
}, ['dbForPlatform', 'request', 'console', 'authorization']);
Http::setResource('session', function (User $user, Store $store, Token $proofForToken) {
App::setResource('session', function (User $user, Store $store, Token $proofForToken) {
if ($user->isEmpty()) {
return;
}
@@ -555,11 +483,11 @@ Http::setResource('session', function (User $user, Store $store, Token $proofFor
return;
}, ['user', 'store', 'proofForToken']);
Http::setResource('store', function (): Store {
App::setResource('store', function (): Store {
return new Store();
});
Http::setResource('proofForPassword', function (): Password {
App::setResource('proofForPassword', function (): Password {
$hash = new Argon2();
$hash
->setMemoryCost(7168)
@@ -573,27 +501,27 @@ Http::setResource('proofForPassword', function (): Password {
return $password;
});
Http::setResource('proofForToken', function (): Token {
App::setResource('proofForToken', function (): Token {
$token = new Token();
$token->setHash(new Sha());
return $token;
});
Http::setResource('proofForCode', function (): Code {
App::setResource('proofForCode', function (): Code {
$code = new Code();
$code->setHash(new Sha());
return $code;
});
Http::setResource('console', function () {
App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
Http::setResource('authorization', function () {
App::setResource('authorization', function () {
return new Authorization();
}, []);
Http::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Response $response, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Func $queueForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Authorization $authorization) {
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Response $response, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Func $queueForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Authorization $authorization) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -835,7 +763,7 @@ Http::setResource('dbForProject', function (Group $pools, Database $dbForPlatfor
return $database;
}, ['pools', 'dbForPlatform', 'cache', 'project', 'response', 'publisher', 'publisherFunctions', 'publisherWebhooks', 'queueForEvents', 'queueForFunctions', 'queueForWebhooks', 'queueForRealtime', 'queueForStatsUsage', 'authorization']);
Http::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
$adapter = new DatabasePool($pools->get('console'));
$database = new Database($adapter, $cache);
@@ -854,7 +782,7 @@ Http::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authori
return $database;
}, ['pools', 'cache', 'authorization']);
Http::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
$databases = [];
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
@@ -910,7 +838,7 @@ Http::setResource('getProjectDB', function (Group $pools, Database $dbForPlatfor
};
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
Http::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $authorization, &$database) {
@@ -939,14 +867,14 @@ Http::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorizati
};
}, ['pools', 'cache', 'authorization']);
Http::setResource('audit', function ($dbForProject) {
App::setResource('audit', function ($dbForProject) {
$adapter = new AdapterDatabase($dbForProject);
return new Audit($adapter);
}, ['dbForProject']);
Http::setResource('telemetry', fn () => new NoTelemetry());
App::setResource('telemetry', fn () => new NoTelemetry());
Http::setResource('cache', function (Group $pools, Telemetry $telemetry) {
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
$list = Config::getParam('pools-cache', []);
$adapters = [];
@@ -959,7 +887,7 @@ Http::setResource('cache', function (Group $pools, Telemetry $telemetry) {
return $cache;
}, ['pools', 'telemetry']);
Http::setResource('redis', function () {
App::setResource('redis', function () {
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
$port = System::getEnv('_APP_REDIS_PORT', 6379);
$pass = System::getEnv('_APP_REDIS_PASS', '');
@@ -974,28 +902,28 @@ Http::setResource('redis', function () {
return $redis;
});
Http::setResource('timelimit', function (\Redis $redis) {
App::setResource('timelimit', function (\Redis $redis) {
return function (string $key, int $limit, int $time) use ($redis) {
return new TimeLimitRedis($key, $limit, $time, $redis);
};
}, ['redis']);
Http::setResource('deviceForLocal', function (Telemetry $telemetry) {
App::setResource('deviceForLocal', function (Telemetry $telemetry) {
return new Device\Telemetry($telemetry, new Local());
}, ['telemetry']);
Http::setResource('deviceForFiles', function ($project, Telemetry $telemetry) {
App::setResource('deviceForFiles', function ($project, Telemetry $telemetry) {
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()));
}, ['project', 'telemetry']);
Http::setResource('deviceForSites', function ($project, Telemetry $telemetry) {
App::setResource('deviceForSites', function ($project, Telemetry $telemetry) {
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()));
}, ['project', 'telemetry']);
Http::setResource('deviceForMigrations', function ($project, Telemetry $telemetry) {
App::setResource('deviceForMigrations', function ($project, Telemetry $telemetry) {
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()));
}, ['project', 'telemetry']);
Http::setResource('deviceForFunctions', function ($project, Telemetry $telemetry) {
App::setResource('deviceForFunctions', function ($project, Telemetry $telemetry) {
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()));
}, ['project', 'telemetry']);
Http::setResource('deviceForBuilds', function ($project, Telemetry $telemetry) {
App::setResource('deviceForBuilds', function ($project, Telemetry $telemetry) {
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()));
}, ['project', 'telemetry']);
@@ -1099,7 +1027,7 @@ function getDevice(string $root, string $connection = ''): Device
}
}
Http::setResource('mode', function ($request) {
App::setResource('mode', function ($request) {
/** @var Appwrite\Utopia\Request $request */
/**
@@ -1110,18 +1038,18 @@ Http::setResource('mode', function ($request) {
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
}, ['request']);
Http::setResource('geodb', function ($register) {
App::setResource('geodb', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('geodb');
}, ['register']);
Http::setResource('passwordsDictionary', function ($register) {
App::setResource('passwordsDictionary', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary');
}, ['register']);
Http::setResource('servers', function () {
App::setResource('servers', function () {
$platforms = Config::getParam('sdks');
$server = $platforms[APP_SDK_PLATFORM_SERVER];
@@ -1132,11 +1060,11 @@ Http::setResource('servers', function () {
return $languages;
});
Http::setResource('promiseAdapter', function ($register) {
App::setResource('promiseAdapter', function ($register) {
return $register->get('promiseAdapter');
}, ['register']);
Http::setResource('schema', function ($utopia, $dbForProject, $authorization) {
App::setResource('schema', function ($utopia, $dbForProject, $authorization) {
$complexity = function (int $complexity, array $args) {
$queries = Query::parseQueries($args['queries'] ?? []);
@@ -1223,11 +1151,11 @@ Http::setResource('schema', function ($utopia, $dbForProject, $authorization) {
);
}, ['utopia', 'dbForProject', 'authorization']);
Http::setResource('gitHub', function (Cache $cache) {
App::setResource('gitHub', function (Cache $cache) {
return new VcsGitHub($cache);
}, ['cache']);
Http::setResource('requestTimestamp', function ($request) {
App::setResource('requestTimestamp', function ($request) {
//TODO: Move this to the Request class itself
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
$requestTimestamp = null;
@@ -1241,15 +1169,15 @@ Http::setResource('requestTimestamp', function ($request) {
return $requestTimestamp;
}, ['request']);
Http::setResource('plan', function (array $plan = []) {
App::setResource('plan', function (array $plan = []) {
return [];
});
Http::setResource('smsRates', function () {
App::setResource('smsRates', function () {
return [];
});
Http::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform, Authorization $authorization) {
App::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform, Authorization $authorization) {
$devKey = $request->getHeader('x-appwrite-dev-key', $request->getParam('devKey', ''));
// Check if given key match project's development keys
@@ -1293,13 +1221,13 @@ Http::setResource('devKey', function (Request $request, Document $project, array
return $key;
}, ['request', 'project', 'servers', 'dbForPlatform', 'authorization']);
Http::setResource('team', function (Document $project, Database $dbForPlatform, Http $utopia, Request $request, Authorization $authorization) {
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request, Authorization $authorization) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
$teamInternalId = $project->getAttribute('teamInternalId', '');
} else {
$route = $utopia->match($request);
$path = !empty($route) ? $route->getPath() : $request->getURI();
$path = $route->getPath();
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
@@ -1330,15 +1258,15 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform,
return $team;
}, ['project', 'dbForPlatform', 'utopia', 'request', 'authorization']);
Http::setResource(
App::setResource(
'isResourceBlocked',
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
Http::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
App::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
$allowed = false;
if (Http::isDevelopment()) {
if (App::isDevelopment()) {
$allowed = true;
} elseif (!\is_null($apiKey) && $apiKey->getHostnameOverride() === true) {
$allowed = true;
@@ -1354,43 +1282,19 @@ Http::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
return '';
}, ['request', 'apiKey']);
Http::setResource('apiKey', function (Request $request, Document $project, Document $team, Document $user): ?Key {
App::setResource('apiKey', function (Request $request, Document $project): ?Key {
$key = $request->getHeader('x-appwrite-key');
if (empty($key)) {
return null;
}
$key = Key::decode($project, $team, $user, $key);
return Key::decode($project, $key);
}, ['request', 'project']);
$userHeader = $request->getHeader('x-appwrite-user');
$organizationHeader = $request->getHeader('x-appwrite-organization');
$projectHeader = $request->getHeader('x-appwrite-project');
App::setResource('executor', fn () => new Executor());
if (!empty($key->getProjectId())) {
if (empty($projectHeader) || $projectHeader !== $key->getProjectId()) {
throw new Exception(Exception::PROJECT_ID_MISSING);
}
}
if (!empty($key->getUserId())) {
if (empty($userHeader) || $userHeader !== $key->getUserId()) {
throw new Exception(Exception::USER_ID_MISSING);
}
}
if (!empty($key->getTeamId())) {
if (empty($organizationHeader) || $organizationHeader !== $key->getTeamId()) {
throw new Exception(Exception::ORGANIZATION_ID_MISSING);
}
}
return $key;
}, ['request', 'project', 'team', 'user']);
Http::setResource('executor', fn () => new Executor());
Http::setResource('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
App::setResource('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
$tokenJWT = $request->getParam('token');
if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication
@@ -1454,11 +1358,11 @@ Http::setResource('resourceToken', function ($project, $dbForProject, $request,
return new Document([]);
}, ['project', 'dbForProject', 'request', 'authorization']);
Http::setResource('transactionState', function (Database $dbForProject, Authorization $authorization) {
App::setResource('transactionState', function (Database $dbForProject, Authorization $authorization) {
return new TransactionState($dbForProject, $authorization);
}, ['dbForProject', 'authorization']);
Http::setResource('executionsRetentionCount', function (Document $project, array $plan) {
App::setResource('executionsRetentionCount', function (Document $project, array $plan) {
if ($project->getId() === 'console' || empty($plan)) {
return 0;
}
+39 -117
View File
@@ -16,6 +16,7 @@ use Swoole\Table;
use Swoole\Timer;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\App;
use Utopia\Auth\Hashes\Sha;
use Utopia\Auth\Proofs\Token;
use Utopia\Auth\Store;
@@ -33,7 +34,6 @@ use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\DSN\DSN;
use Utopia\Http;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
use Utopia\Registry\Registry;
@@ -431,24 +431,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
]
];
$subscribers = $realtime->getSubscribers($event);
$groups = [];
foreach ($subscribers as $id => $matched) {
$key = implode(',', array_keys($matched));
$groups[$key]['ids'][] = $id;
$groups[$key]['subscriptions'] = array_keys($matched);
}
foreach ($groups as $group) {
$data = $event['data'];
$data['subscriptions'] = $group['subscriptions'];
$server->send($group['ids'], json_encode([
'type' => 'event',
'data' => $data
]));
}
$server->send($realtime->getSubscribers($event), json_encode([
'type' => 'event',
'data' => $event['data']
]));
}
});
@@ -487,63 +473,33 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$user = $database->getDocument('users', $userId);
$roles = $user->getRoles($database->getAuthorization());
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
$meta = $realtime->getSubscriptionMetadata($connection);
$channels = $realtime->connections[$connection]['channels'];
$queries = $realtime->connections[$connection]['queries'] ?? [];
$realtime->unsubscribe($connection);
foreach ($meta as $subscriptionId => $subscription) {
$queries = Query::parseQueries($subscription['queries'] ?? []);
$realtime->subscribe(
$projectId,
$connection,
$subscriptionId,
$roles,
$subscription['channels'] ?? [],
$queries
);
}
// Restore authorization after subscribe
if ($authorization !== null) {
$realtime->connections[$connection]['authorization'] = $authorization;
}
$realtime->subscribe($projectId, $connection, $roles, $channels, $queries);
}
}
$receivers = $realtime->getSubscribers($event);
if (Http::isDevelopment() && !empty($receivers)) {
if (App::isDevelopment() && !empty($receivers)) {
Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers));
Console::log("[Debug][Worker {$workerId}] Connection IDs: " . json_encode(array_keys($receivers)));
Console::log("[Debug][Worker {$workerId}] Matched: " . json_encode(array_values($receivers)));
Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers));
Console::log("[Debug][Worker {$workerId}] Event: " . $payload);
}
// Group connections by matched subscription IDs for batch sending
$groups = [];
foreach ($receivers as $id => $matched) {
$key = implode(',', array_keys($matched));
$groups[$key]['ids'][] = $id;
$groups[$key]['subscriptions'] = array_keys($matched);
}
$total = 0;
foreach ($groups as $group) {
$data = $event['data'];
$data['subscriptions'] = $group['subscriptions'];
$server->send($group['ids'], json_encode([
$server->send(
$receivers,
json_encode([
'type' => 'event',
'data' => $data
]));
$total += count($group['ids']);
}
'data' => $event['data']
])
);
if ($total > 0) {
$register->get('telemetry.messageSentCounter')->add($total);
$stats->incr($event['project'], 'messages', $total);
if (($num = count($receivers)) > 0) {
$register->get('telemetry.messageSentCounter')->add($num);
$stats->incr($event['project'], 'messages', $num);
}
});
} catch (Throwable $th) {
@@ -560,15 +516,15 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
});
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) {
$app = new Http('UTC');
$app = new App('UTC');
$request = new Request($request);
$response = new Response(new SwooleResponse());
Console::info("Connection open (user: {$connection})");
Http::setResource('pools', fn () => $register->get('pools'));
Http::setResource('request', fn () => $request);
Http::setResource('response', fn () => $response);
App::setResource('pools', fn () => $register->get('pools'));
App::setResource('request', fn () => $request);
App::setResource('response', fn () => $response);
try {
/** @var Document $project */
@@ -624,6 +580,11 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$roles = $user->getRoles($authorization);
$channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId());
try {
$queries = Realtime::convertQueries($request->getQuery('queries', []));
} catch (QueryException $e) {
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $e->getMessage());
}
/**
* Channels Check
@@ -632,32 +593,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing channels');
}
$names = array_keys($channels);
try {
$subscriptions = Realtime::constructSubscriptions(
$names,
fn ($channel) => $request->getQuery($channel, null)
);
} catch (QueryException $e) {
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $e->getMessage());
}
$mapping = [];
foreach ($subscriptions as $index => $subscription) {
$subscriptionId = ID::unique();
$realtime->subscribe(
$project->getId(),
$connection,
$subscriptionId,
$roles,
$subscription['channels'],
$subscription['queries']
);
$mapping[$index] = $subscriptionId;
}
$realtime->subscribe($project->getId(), $connection, $roles, $channels, $queries);
$realtime->connections[$connection]['authorization'] = $authorization;
@@ -666,8 +602,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$server->send([$connection], json_encode([
'type' => 'connected',
'data' => [
'channels' => $names,
'subscriptions' => $mapping,
'channels' => array_keys($channels),
'queries' => $queries,
'user' => $user
]
]));
@@ -694,7 +630,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$message = $th->getMessage();
// sanitize 0 && 5xx errors
if (($code === 0 || $code >= 500) && !Http::isDevelopment()) {
if (($code === 0 || $code >= 500) && !App::isDevelopment()) {
$message = 'Error: Server Error';
}
@@ -709,7 +645,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$server->send([$connection], json_encode($response));
$server->close($connection, $code);
if (Http::isDevelopment()) {
if (App::isDevelopment()) {
Console::error('[Error] Connection Error');
Console::error('[Error] Code: ' . $response['data']['code']);
Console::error('[Error] Message: ' . $response['data']['message']);
@@ -797,29 +733,15 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
}
$roles = $user->getRoles($database->getAuthorization());
$channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId());
// Preserve authorization before subscribe overwrites the connection array
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
$projectId = $realtime->connections[$connection]['projectId'] ?? null;
$meta = $realtime->getSubscriptionMetadata($connection);
$realtime->unsubscribe($connection);
if (!empty($projectId)) {
foreach ($meta as $subscriptionId => $subscription) {
$queries = Query::parseQueries($subscription['queries'] ?? []);
$realtime->subscribe(
$projectId,
$connection,
$subscriptionId,
$roles,
$subscription['channels'] ?? [],
$queries
);
}
}
$queries = $realtime->connections[$connection]['queries'];
$realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels, $queries);
// Restore authorization after subscribe
if ($authorization !== null) {
$realtime->connections[$connection]['authorization'] = $authorization;
}
@@ -848,7 +770,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$message = $th->getMessage();
// sanitize 0 && 5xx errors
if (($code === 0 || $code >= 500) && !Http::isDevelopment()) {
if (($code === 0 || $code >= 500) && !App::isDevelopment()) {
$message = 'Error: Server Error';
}
+1 -1
View File
@@ -14,7 +14,7 @@ $image = $this->getParam('image', '');
$enableAssistant = $this->getParam('enableAssistant', false);
?>services:
traefik:
image: traefik:3.6
image: traefik:2.11
container_name: appwrite-traefik
<<: *x-logging
command:
-11
View File
@@ -9,7 +9,6 @@ use Appwrite\Event\Certificate;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Execution;
use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
@@ -45,19 +44,13 @@ use Utopia\Queue\Message;
use Utopia\Queue\Publisher;
use Utopia\Queue\Server;
use Utopia\Registry\Registry;
use Utopia\Span\Exporter;
use Utopia\Span\Span;
use Utopia\Span\Storage;
use Utopia\Storage\Device\Telemetry as TelemetryDevice;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
Runtime::enableCoroutine();
Span::setStorage(new Storage\Coroutine());
Span::addExporter(new Exporter\Stdout());
global $register;
Server::setResource('register', fn () => $register);
Server::setResource('authorization', function () {
@@ -359,10 +352,6 @@ Server::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
}, ['publisher']);
Server::setResource('queueForExecutions', function (Publisher $publisher) {
return new Execution($publisher);
}, ['publisher']);
Server::setResource('queueForRealtime', function () {
return new Realtime();
}, []);
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
exec php /usr/src/code/app/worker.php executions "$@"
+18 -18
View File
@@ -38,51 +38,51 @@
"ext-yaml": "*",
"ext-dom": "*",
"ext-redis": "*",
"ext-swoole": "6.*",
"ext-swoole": "*",
"ext-pdo": "*",
"ext-openssl": "*",
"ext-zlib": "*",
"ext-sockets": "*",
"appwrite/php-runtimes": "0.19.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "1.2.*",
"utopia-php/abuse": "1.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "2.2.*",
"utopia-php/audit": "1.*",
"utopia-php/auth": "0.5.*",
"utopia-php/cache": "1.0.*",
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "1.*",
"utopia-php/database": "5.*",
"utopia-php/config": "1.*.*",
"utopia-php/database": "4.*",
"utopia-php/detector": "0.2.*",
"utopia-php/domains": "1.*",
"utopia-php/domains": "0.9.*",
"utopia-php/emails": "0.6.*",
"utopia-php/dns": "1.5.*",
"utopia-php/dns": "1.4.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.5.*",
"utopia-php/fetch": "0.4.*",
"utopia-php/image": "0.8.*",
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.20.*",
"utopia-php/migration": "1.5.*",
"utopia-php/migration": "1.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "1.*",
"utopia-php/pools": "0.8.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.15.*",
"utopia-php/queue": "0.11.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.18.*",
"utopia-php/swoole": "1.*",
"utopia-php/system": "0.10.*",
"utopia-php/telemetry": "0.2.*",
"utopia-php/vcs": "1.*",
"utopia-php/swoole": "0.8.*",
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/vcs": "0.13.*",
"utopia-php/websocket": "0.3.*",
"matomo/device-detector": "6.4.*",
"dragonmantank/cron-expression": "3.4.*",
"phpmailer/phpmailer": "6.9.*",
"chillerlan/php-qrcode": "4.4.*",
"adhocore/jwt": "1.1.*",
"spomky-labs/otphp": "^11.4.2",
"spomky-labs/otphp": "10.0.*",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.24.*",
"enshrined/svg-sanitize": "0.22.*"
@@ -91,7 +91,7 @@
"ext-fileinfo": "*",
"appwrite/sdk-generator": "*",
"phpunit/phpunit": "9.*",
"swoole/ide-helper": "6.*",
"swoole/ide-helper": "5.1.2",
"phpstan/phpstan": "1.8.*",
"textalk/websocket": "1.5.*",
"laravel/pint": "1.*",
Generated
+515 -458
View File
File diff suppressed because it is too large Load Diff
+4 -71
View File
@@ -12,7 +12,7 @@ x-logging: &x-logging
services:
traefik:
image: traefik:3.6
image: traefik:2.11
<<: *x-logging
container_name: appwrite-traefik
command:
@@ -98,7 +98,6 @@ services:
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
- ./dev:/usr/src/code/dev
# - ./vendor/utopia-php/framework:/usr/src/code/vendor/utopia-php/framework
depends_on:
- mariadb
- redis
@@ -112,9 +111,7 @@ services:
- _APP_ENV
- _APP_EDITION
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_LOCALE
- _APP_COMPRESSION_ENABLED
- _APP_COMPRESSION_MIN_SIZE_BYTES
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
@@ -133,7 +130,6 @@ services:
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_CONSOLE_DOMAIN
- _APP_CONSOLE_TRUSTED_PROJECTS
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
@@ -229,7 +225,6 @@ services:
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
- _APP_CUSTOM_DOMAIN_DENY_LIST
- _APP_TRUSTED_HEADERS
- _APP_MIGRATION_HOST
extra_hosts:
- "host.docker.internal:host-gateway"
@@ -304,7 +299,6 @@ services:
- _APP_LOGGING_CONFIG
- _APP_LOGGING_CONFIG_REALTIME
- _APP_DATABASE_SHARED_TABLES
- _APP_POOL_ADAPTER=swoole
appwrite-worker-audits:
entrypoint: worker-audits
@@ -322,7 +316,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -354,7 +347,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_EMAIL_SECURITY
- _APP_DB_HOST
@@ -392,7 +384,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -450,7 +441,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -485,7 +475,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
@@ -512,7 +501,6 @@ services:
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_DOMAIN
- _APP_CONSOLE_DOMAIN
- _APP_CONSOLE_TRUSTED_PROJECTS
- _APP_STORAGE_DEVICE
- _APP_STORAGE_S3_ACCESS_KEY
- _APP_STORAGE_S3_SECRET
@@ -560,7 +548,6 @@ services:
# Basic
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_LOGGING_CONFIG
# Database
- _APP_OPENSSL_KEY_V1
@@ -618,7 +605,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
@@ -641,37 +627,6 @@ services:
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-executions:
entrypoint: worker-executions
<<: *x-logging
container_name: appwrite-worker-executions
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
redis:
condition: service_started
mariadb:
condition: service_started
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-functions:
entrypoint: worker-functions
<<: *x-logging
@@ -683,16 +638,12 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
redis:
condition: service_started
mariadb:
condition: service_started
openruntimes-executor:
condition: service_healthy
- redis
- mariadb
- openruntimes-executor
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
@@ -736,7 +687,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
@@ -771,7 +721,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -829,7 +778,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
@@ -852,7 +800,6 @@ services:
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_DATABASE_SHARED_TABLES
- _APP_OPTIONS_FORCE_HTTPS
- _APP_MIGRATION_HOST
appwrite-task-maintenance:
entrypoint: maintenance
@@ -870,7 +817,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
@@ -915,7 +861,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
@@ -954,7 +899,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
@@ -986,7 +930,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
@@ -1018,7 +961,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
@@ -1050,7 +992,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -1079,7 +1020,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -1107,7 +1047,6 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -1186,12 +1125,6 @@ services:
- OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
healthcheck:
test: ["CMD-SHELL", "curl -fsS -H \"Authorization: Bearer $$OPR_EXECUTOR_SECRET\" http://localhost/v1/health >/dev/null"]
interval: 5s
timeout: 3s
retries: 20
start_period: 5s
mariadb:
image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.createAnonymousSession(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.createEmailPasswordSession(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -23,4 +22,3 @@ account.createEmailToken(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.createEmailVerification(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -9,16 +8,11 @@ Client client = new Client(context)
Account account = new Account(client);
account.createJWT(
0, // duration (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
account.createJWT(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
```
Log.d("Appwrite", result.toString());
}));
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -24,4 +23,3 @@ account.createMagicURLToken(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.createMFAAuthenticator(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.createMFAChallenge(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.createMFARecoveryCodes(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -25,4 +24,3 @@ account.createOAuth2Session(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -25,4 +24,3 @@ account.createOAuth2Token(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.createPhoneToken(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.createPhoneVerification(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -23,4 +22,3 @@ account.createPushTarget(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.createRecovery(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.createSession(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.createVerification(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -24,4 +23,3 @@ account.create(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.deleteIdentity(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.deleteMFAAuthenticator(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.deletePushTarget(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.deleteSession(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.deleteSessions(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.getMFARecoveryCodes(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.getPrefs(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.getSession(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.get(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.listIdentities(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.listLogs(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.listMFAFactors(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.listSessions(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.updateEmailVerification(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.updateEmail(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.updateMagicURLSession(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -23,4 +22,3 @@ account.updateMFAAuthenticator(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.updateMFAChallenge(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -17,4 +16,3 @@ account.updateMFARecoveryCodes(new CoroutineCallback<>((result, error) -> {
Log.d("Appwrite", result.toString());
}));
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.updateMFA(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -21,4 +20,3 @@ account.updateName(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.updatePassword(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.updatePhoneSession(
})
);
```
@@ -1,4 +1,3 @@
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
@@ -22,4 +21,3 @@ account.updatePhoneVerification(
})
);
```

Some files were not shown because too many files have changed in this diff Show More