mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
297
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfc5caf419 | ||
|
|
a13375682b | ||
|
|
ac0bfd6cb8 | ||
|
|
549570a29e | ||
|
|
3459133fd4 | ||
|
|
1d91ecd9fb | ||
|
|
b62a1d8b5c | ||
|
|
4ebc67c235 | ||
|
|
ed80450bd5 | ||
|
|
104368966d | ||
|
|
07be361c7c | ||
|
|
bc508353db | ||
|
|
da1285bb22 | ||
|
|
b06b33016c | ||
|
|
a0cf7315fc | ||
|
|
fad29ee3cd | ||
|
|
dd76d52c00 | ||
|
|
c4cc8595d6 | ||
|
|
5878a82b8a | ||
|
|
0353b826ac | ||
|
|
68ffa5258c | ||
|
|
91a1e4cfd0 | ||
|
|
7aceae05d5 | ||
|
|
0c114b8d66 | ||
|
|
8fe1ec48d0 | ||
|
|
379b85bb7b | ||
|
|
97038c9d05 | ||
|
|
5cc6845a44 | ||
|
|
0386fde126 | ||
|
|
866bd5ebc8 | ||
|
|
d0b1f5f529 | ||
|
|
98dbb03155 | ||
|
|
088359c257 | ||
|
|
e114d49789 | ||
|
|
1df5b71e32 | ||
|
|
a166ae5a03 | ||
|
|
c5b2b67361 | ||
|
|
4c62ecbc95 | ||
|
|
1a4adc50b2 | ||
|
|
aaafdaae52 | ||
|
|
adc0d3dbd0 | ||
|
|
27b8b23c07 | ||
|
|
6aa054bc7c | ||
|
|
64dbb28612 | ||
|
|
0b5d67a77a | ||
|
|
e204419b8b | ||
|
|
851fb58645 | ||
|
|
128478ed00 | ||
|
|
6f0c4d6c4e | ||
|
|
c4366c9de1 | ||
|
|
d517ca8444 | ||
|
|
67ae0b088a | ||
|
|
adc5426ce9 | ||
|
|
c09f7d5cf9 | ||
|
|
95abe61bab | ||
|
|
2fdfbf6e61 | ||
|
|
c2585e16b1 | ||
|
|
fd466dfcea | ||
|
|
2bd308c236 | ||
|
|
885a48b598 | ||
|
|
63652cc155 | ||
|
|
06c876906c | ||
|
|
ef8d7a45f9 | ||
|
|
04dd0f807d | ||
|
|
5dd3f9c574 | ||
|
|
cbc8b2ca0d | ||
|
|
f6b04ae63e | ||
|
|
4fd0af509e | ||
|
|
d539186808 | ||
|
|
1ed3cf0e59 | ||
|
|
11b8742a73 | ||
|
|
7e9d74e141 | ||
|
|
aa98fbb2dc | ||
|
|
8f1b3a8e30 | ||
|
|
3d8cee4676 | ||
|
|
7efbae4310 | ||
|
|
1c9fd39fd6 | ||
|
|
8c854b7d07 | ||
|
|
c713357131 | ||
|
|
fe9ecb6fcb | ||
|
|
ab1541808b | ||
|
|
623a8a747d | ||
|
|
21237f8e02 | ||
|
|
f0ed75f585 | ||
|
|
34ce5099e2 | ||
|
|
10171c80da | ||
|
|
c6e4afdb49 | ||
|
|
448e60479d | ||
|
|
8fe7de7ff9 | ||
|
|
8e70a5239d | ||
|
|
28beb27759 | ||
|
|
b1f5b6946a | ||
|
|
9ac594c537 | ||
|
|
6547637c96 | ||
|
|
13a7ead598 | ||
|
|
d40e793137 | ||
|
|
149fee503f | ||
|
|
5a1c257a37 | ||
|
|
94ba8ee5f2 | ||
|
|
e5fa23ef6c | ||
|
|
053665eef9 | ||
|
|
66afd1064d | ||
|
|
e2758691af | ||
|
|
9a8a42f479 | ||
|
|
3347573f17 | ||
|
|
cceb108fd3 | ||
|
|
b905686c0e | ||
|
|
ad43be5dab | ||
|
|
ceb11e039f | ||
|
|
63e06750cf | ||
|
|
a4f7cf9f73 | ||
|
|
ba10bed271 | ||
|
|
e2979410da | ||
|
|
9b68b54743 | ||
|
|
0fbdbb386f | ||
|
|
50f26e8702 | ||
|
|
a2463f8cc3 | ||
|
|
0cdabbf959 | ||
|
|
4b752c15e8 | ||
|
|
fcce6f8d34 | ||
|
|
2443266ac5 | ||
|
|
4616a2e95d | ||
|
|
9a0e1c1840 | ||
|
|
bdbe94b56f | ||
|
|
0974e1335f | ||
|
|
61f8c99da7 | ||
|
|
150d09f656 | ||
|
|
bfce804f79 | ||
|
|
a479ce63fc | ||
|
|
8765d5650a | ||
|
|
e3ee724178 | ||
|
|
ce2f2d0ebc | ||
|
|
9f7e3146a4 | ||
|
|
e6039cf65e | ||
|
|
7b3bceeb7d | ||
|
|
90ef76ad82 | ||
|
|
5a8a900b18 | ||
|
|
c2645be4e1 | ||
|
|
20221cd6b6 | ||
|
|
275ff3dbf2 | ||
|
|
b6fbddad81 | ||
|
|
39c5b6c4d0 | ||
|
|
f9274dad3f | ||
|
|
3568af54b3 | ||
|
|
71b6ac0364 | ||
|
|
919b01a1b1 | ||
|
|
3d69998f9c | ||
|
|
d2000ee9d8 | ||
|
|
0718f90405 | ||
|
|
73f5b4227d | ||
|
|
52766cfdba | ||
|
|
ba542554ce | ||
|
|
deb5039352 | ||
|
|
793057c823 | ||
|
|
4a86b2d5b1 | ||
|
|
d36e42377b | ||
|
|
d9522ac6b8 | ||
|
|
db1b87ecbb | ||
|
|
99d16dfc60 | ||
|
|
725870b1b1 | ||
|
|
38ef0ad399 | ||
|
|
c961316f46 | ||
|
|
2349401a86 | ||
|
|
6274d5a3e4 | ||
|
|
83b9815644 | ||
|
|
afb0fcbc6f | ||
|
|
1da4736d5d | ||
|
|
5722241ff8 | ||
|
|
37910a4c5c | ||
|
|
0c7551b0c1 | ||
|
|
653ac184e1 | ||
|
|
b20c493fa6 | ||
|
|
b91268b3cd | ||
|
|
831a261c04 | ||
|
|
c784cf950b | ||
|
|
64b4ea9551 | ||
|
|
47b653fd64 | ||
|
|
c8e6fcc090 | ||
|
|
b3df65cf33 | ||
|
|
3ef6154ebe | ||
|
|
a96ba5499e | ||
|
|
599aa1dcca | ||
|
|
62e5f54fcb | ||
|
|
ae2ffd259c | ||
|
|
3d471bae56 | ||
|
|
d0f2293755 | ||
|
|
d6bd26da43 | ||
|
|
16d6c9e5e3 | ||
|
|
9ef6fa9b4c | ||
|
|
9fe74bdd22 | ||
|
|
fa2b46c7e7 | ||
|
|
13f9900802 | ||
|
|
3c93004cde | ||
|
|
631fd2ea4c | ||
|
|
26d3cef528 | ||
|
|
fb7691be3b | ||
|
|
eb7be569eb | ||
|
|
88cbc36a02 | ||
|
|
6a60b7d25b | ||
|
|
343d443903 | ||
|
|
12c27edaf9 | ||
|
|
0f9b13e251 | ||
|
|
965c4738e7 | ||
|
|
4679eff4b8 | ||
|
|
3cfb5d695f | ||
|
|
501cba216b | ||
|
|
7ef5a7edaf | ||
|
|
8adeb40c1d | ||
|
|
ae105165ab | ||
|
|
5dfee10369 | ||
|
|
44b5ed90d5 | ||
|
|
e70d5c46b6 | ||
|
|
c9d66c52b4 | ||
|
|
4d22f4252f | ||
|
|
9130a7102e | ||
|
|
2a7d89ea7b | ||
|
|
f3fd2fd191 | ||
|
|
a970969c4d | ||
|
|
f0e4c9184d | ||
|
|
decad9ff9d | ||
|
|
e264ef53f4 | ||
|
|
31ffc9ea92 | ||
|
|
81480f4492 | ||
|
|
4b15fec1d7 | ||
|
|
918ad4a545 | ||
|
|
dff7aa6ce0 | ||
|
|
5ef717d0fd | ||
|
|
3739ee26d8 | ||
|
|
437d914e90 | ||
|
|
897f512b47 | ||
|
|
df1f14c853 | ||
|
|
68cce71527 | ||
|
|
2ae474971d | ||
|
|
617c1aaee3 | ||
|
|
dc43b5977f | ||
|
|
e1fbaf14eb | ||
|
|
d721425cfe | ||
|
|
02dd275be4 | ||
|
|
50cd6a737d | ||
|
|
a575c07468 | ||
|
|
071ec671e0 | ||
|
|
3391563829 | ||
|
|
5709265f6c | ||
|
|
89d5a28c28 | ||
|
|
f613a06d19 | ||
|
|
30f4b846d2 | ||
|
|
a3742bc65e | ||
|
|
cd1b2b6c05 | ||
|
|
c13aed651a | ||
|
|
dbccb16784 | ||
|
|
c0be333c0f | ||
|
|
d96c7eb3e9 | ||
|
|
4fca374fe9 | ||
|
|
9b82dd0411 | ||
|
|
0904881de8 | ||
|
|
d38399dcc0 | ||
|
|
68b0fe8c70 | ||
|
|
7c2a1ade1f | ||
|
|
5c2828bc78 | ||
|
|
111b2abeeb | ||
|
|
9503507011 | ||
|
|
a6d1f46133 | ||
|
|
145b884628 | ||
|
|
0e22b06f0a | ||
|
|
9e79d883a0 | ||
|
|
02e8a7b429 | ||
|
|
ca861119f9 | ||
|
|
0bfbae4cdd | ||
|
|
61de3b9e08 | ||
|
|
7b1e163c77 | ||
|
|
3f710ddc70 | ||
|
|
1546add5d5 | ||
|
|
734c44c1da | ||
|
|
68524067cf | ||
|
|
cc6319a2c7 | ||
|
|
4aaaa460b2 | ||
|
|
dee3d375db | ||
|
|
462634d311 | ||
|
|
e769cf4e48 | ||
|
|
fbaffa980a | ||
|
|
26f61c1855 | ||
|
|
dae62d863a | ||
|
|
681fa198c9 | ||
|
|
e7b8e6c90d | ||
|
|
d7cc3895cb | ||
|
|
a0b21a0bd4 | ||
|
|
65e99ff0de | ||
|
|
ecd6880fe0 | ||
|
|
e20adbba61 | ||
|
|
30d58e6371 | ||
|
|
6ed2000478 | ||
|
|
25403ea35e | ||
|
|
815c75fa9a | ||
|
|
8b3b07a710 | ||
|
|
33f88fa5e0 | ||
|
|
8b663f041a | ||
|
|
52d656194c |
@@ -103,6 +103,7 @@ _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
|
||||
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
|
||||
_APP_USAGE_STATS=enabled
|
||||
_APP_LOGGING_CONFIG=
|
||||
_APP_LOGGING_CONFIG_REALTIME=
|
||||
_APP_GRAPHQL_MAX_BATCH_SIZE=10
|
||||
_APP_GRAPHQL_MAX_COMPLEXITY=250
|
||||
_APP_GRAPHQL_MAX_DEPTH=4
|
||||
|
||||
+2
-2
@@ -24,9 +24,9 @@ ENV _APP_VERSION=$VERSION \
|
||||
_APP_HOME=https://appwrite.io
|
||||
|
||||
RUN \
|
||||
if [ "$DEBUG" == "true" ]; then \
|
||||
if [ "$DEBUG" == "true" ]; then \
|
||||
apk add boost boost-dev; \
|
||||
fi
|
||||
fi
|
||||
|
||||
WORKDIR /usr/src/code
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -9,6 +9,7 @@ use Appwrite\Event\StatsResources;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Executor\Executor;
|
||||
use Swoole\Runtime;
|
||||
use Swoole\Timer;
|
||||
@@ -76,6 +77,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
|
||||
->setNamespace('_console')
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', 'console');
|
||||
$dbForPlatform->setDocumentType('users', User::class);
|
||||
|
||||
// Ensure tables exist
|
||||
$collections = Config::getParam('collections', [])['console'];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
|
||||
@@ -173,7 +173,7 @@ return [
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => Auth::DEFAULT_ALGO,
|
||||
'default' => (new Argon2())->getName(),
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
@@ -184,7 +184,7 @@ return [
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'default' => (new Argon2())->getOptions(),
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
@@ -1582,6 +1582,17 @@ return [
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('transformations'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
'default' => true,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('search'),
|
||||
'type' => Database::VAR_STRING,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* Initializes console project document.
|
||||
*/
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Network\Platform;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\System\System;
|
||||
@@ -38,7 +37,7 @@ $console = [
|
||||
'mockNumbers' => [],
|
||||
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
|
||||
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
|
||||
'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled',
|
||||
'invalidateSessions' => true
|
||||
],
|
||||
|
||||
@@ -522,6 +522,11 @@ return [
|
||||
'description' => 'The requested file is not publicly readable.',
|
||||
'code' => 403,
|
||||
],
|
||||
Exception::STORAGE_BUCKET_TRANSFORMATIONS_DISABLED => [
|
||||
'name' => Exception::STORAGE_BUCKET_TRANSFORMATIONS_DISABLED,
|
||||
'description' => 'Image transformations are disabled for the requested bucket.',
|
||||
'code' => 403,
|
||||
],
|
||||
|
||||
/** Tokens */
|
||||
Exception::TOKEN_NOT_FOUND => [
|
||||
|
||||
+16
-23
@@ -8,20 +8,13 @@ use Utopia\Config\Config;
|
||||
|
||||
$templateRuntimes = Config::getParam('template-runtimes');
|
||||
|
||||
function getVersions(array $versions, string $prefix)
|
||||
{
|
||||
return array_map(function ($version) use ($prefix) {
|
||||
return $prefix . '-' . $version;
|
||||
}, $versions);
|
||||
}
|
||||
|
||||
return [
|
||||
'analog' => [
|
||||
'key' => 'analog',
|
||||
'name' => 'Analog',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/analog/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/analog/env.sh',
|
||||
'adapters' => [
|
||||
@@ -47,7 +40,7 @@ return [
|
||||
'name' => 'Angular',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/angular/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/angular/env.sh',
|
||||
'adapters' => [
|
||||
@@ -73,7 +66,7 @@ return [
|
||||
'name' => 'Next.js',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/next-js/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/next-js/env.sh',
|
||||
'adapters' => [
|
||||
@@ -98,7 +91,7 @@ return [
|
||||
'name' => 'React',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
@@ -115,7 +108,7 @@ return [
|
||||
'name' => 'Nuxt',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/nuxt/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/nuxt/env.sh',
|
||||
'adapters' => [
|
||||
@@ -140,7 +133,7 @@ return [
|
||||
'name' => 'Vue.js',
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
@@ -157,7 +150,7 @@ return [
|
||||
'name' => 'SvelteKit',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/sveltekit/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/sveltekit/env.sh',
|
||||
'adapters' => [
|
||||
@@ -182,7 +175,7 @@ return [
|
||||
'name' => 'Astro',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/astro/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/astro/env.sh',
|
||||
'adapters' => [
|
||||
@@ -207,7 +200,7 @@ return [
|
||||
'name' => 'TanStack Start',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/tanstack-start/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/tanstack-start/env.sh',
|
||||
'adapters' => [
|
||||
@@ -215,7 +208,7 @@ return [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'outputDirectory' => './.output',
|
||||
'startCommand' => 'bash helpers/tanstack-start/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
@@ -232,7 +225,7 @@ return [
|
||||
'name' => 'Remix',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'bundleCommand' => 'bash /usr/local/server/helpers/remix/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/remix/env.sh',
|
||||
'adapters' => [
|
||||
@@ -257,7 +250,7 @@ return [
|
||||
'name' => 'Lynx',
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
@@ -274,7 +267,7 @@ return [
|
||||
'name' => 'Flutter',
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'flutter-3.35',
|
||||
'runtimes' => getVersions($templateRuntimes['FLUTTER']['versions'], 'flutter'),
|
||||
'runtimes' => $templateRuntimes['FLUTTER'],
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
@@ -291,7 +284,7 @@ return [
|
||||
'name' => 'React Native',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
@@ -308,7 +301,7 @@ return [
|
||||
'name' => 'Vite',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
@@ -324,7 +317,7 @@ return [
|
||||
'name' => 'Other',
|
||||
'screenshotSleep' => 3000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'runtimes' => $templateRuntimes['NODE'],
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
|
||||
+15
-15
@@ -11,7 +11,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '21.4.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' => '20.3.1',
|
||||
'version' => '20.3.2',
|
||||
'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' => '13.4.0',
|
||||
'version' => '13.5.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
@@ -116,7 +116,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '11.3.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,
|
||||
@@ -139,7 +139,7 @@ return [
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.18.0',
|
||||
'version' => '0.19.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -226,7 +226,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '11.1.1',
|
||||
'version' => '12.0.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -262,7 +262,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '20.3.0',
|
||||
'version' => '21.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -281,7 +281,7 @@ return [
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '18.0.1',
|
||||
'version' => '19.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -300,7 +300,7 @@ return [
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '13.6.1',
|
||||
'version' => '14.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -319,7 +319,7 @@ return [
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '19.3.0',
|
||||
'version' => '20.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -338,7 +338,7 @@ return [
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => 'v0.14.0',
|
||||
'version' => 'v0.15.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
@@ -357,7 +357,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.22.0',
|
||||
'version' => '0.23.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -376,7 +376,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '19.4.0',
|
||||
'version' => '20.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -395,7 +395,7 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '12.3.0',
|
||||
'version' => '13.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
@@ -418,7 +418,7 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '13.3.0',
|
||||
'version' => '14.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
|
||||
$member = [
|
||||
'global',
|
||||
@@ -92,7 +92,7 @@ $admins = [
|
||||
];
|
||||
|
||||
return [
|
||||
Auth::USER_ROLE_GUESTS => [
|
||||
User::ROLE_GUESTS => [
|
||||
'label' => 'Guests',
|
||||
'scopes' => [
|
||||
'global',
|
||||
@@ -112,23 +112,23 @@ return [
|
||||
'execution.write',
|
||||
],
|
||||
],
|
||||
Auth::USER_ROLE_USERS => [
|
||||
User::ROLE_USERS => [
|
||||
'label' => 'Users',
|
||||
'scopes' => \array_merge($member),
|
||||
],
|
||||
Auth::USER_ROLE_ADMIN => [
|
||||
User::ROLE_ADMIN => [
|
||||
'label' => 'Admin',
|
||||
'scopes' => \array_merge($admins),
|
||||
],
|
||||
Auth::USER_ROLE_DEVELOPER => [
|
||||
User::ROLE_DEVELOPER => [
|
||||
'label' => 'Developer',
|
||||
'scopes' => \array_merge($admins),
|
||||
],
|
||||
Auth::USER_ROLE_OWNER => [
|
||||
User::ROLE_OWNER => [
|
||||
'label' => 'Owner',
|
||||
'scopes' => \array_merge($member, $admins),
|
||||
],
|
||||
Auth::USER_ROLE_APPS => [
|
||||
User::ROLE_APPS => [
|
||||
'label' => 'Applications',
|
||||
'scopes' => ['global', 'health.read', 'graphql'],
|
||||
],
|
||||
|
||||
@@ -47,10 +47,10 @@ return [ // List of publicly visible scopes
|
||||
'description' => 'Access to create, update, and delete your project\'s database table\'s columns',
|
||||
],
|
||||
'indexes.read' => [
|
||||
'description' => 'Access to read your project\'s database collection\'s indexes',
|
||||
'description' => 'Access to read your project\'s database table\'s indexes',
|
||||
],
|
||||
'indexes.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s database collection\'s indexes',
|
||||
'description' => 'Access to create, update, and delete your project\'s database table\'s indexes',
|
||||
],
|
||||
'documents.read' => [
|
||||
'description' => 'Access to read your project\'s database documents',
|
||||
|
||||
@@ -258,7 +258,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listIdentities",
|
||||
"group": "identities",
|
||||
"weight": 58,
|
||||
"weight": 48,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-identities.md",
|
||||
@@ -328,7 +328,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteIdentity",
|
||||
"group": "identities",
|
||||
"weight": 59,
|
||||
"weight": 49,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-identity.md",
|
||||
@@ -517,7 +517,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 45,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -587,7 +587,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 47,
|
||||
"weight": 308,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -707,7 +707,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 48,
|
||||
"weight": 309,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -843,7 +843,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 52,
|
||||
"weight": 310,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -939,7 +939,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/account\/mfa\/challenge": {
|
||||
"\/account\/mfa\/challenges": {
|
||||
"post": {
|
||||
"summary": "Create MFA challenge",
|
||||
"operationId": "accountCreateMfaChallenge",
|
||||
@@ -963,7 +963,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 53,
|
||||
"weight": 314,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1091,7 +1091,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 54,
|
||||
"weight": 315,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1225,7 +1225,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 46,
|
||||
"weight": 307,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1322,7 +1322,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 51,
|
||||
"weight": 313,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1417,7 +1417,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 49,
|
||||
"weight": 311,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1512,7 +1512,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 50,
|
||||
"weight": 312,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -2918,7 +2918,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createPushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 55,
|
||||
"weight": 45,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-push-target.md",
|
||||
@@ -2997,7 +2997,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 56,
|
||||
"weight": 46,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-push-target.md",
|
||||
@@ -3068,7 +3068,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deletePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 57,
|
||||
"weight": 47,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-push-target.md",
|
||||
@@ -3879,7 +3879,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 61,
|
||||
"weight": 51,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4005,7 +4005,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 60,
|
||||
"weight": 50,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4137,7 +4137,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 64,
|
||||
"weight": 54,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4195,7 +4195,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 62,
|
||||
"weight": 52,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -4683,7 +4683,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 63,
|
||||
"weight": 53,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -4765,7 +4765,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 66,
|
||||
"weight": 56,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -4857,7 +4857,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 65,
|
||||
"weight": 55,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -4949,7 +4949,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 67,
|
||||
"weight": 57,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -4993,7 +4993,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"x-example": "{}",
|
||||
"x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}",
|
||||
"default": {}
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5005,7 +5005,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1920",
|
||||
"default": 1280
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5017,7 +5017,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1080",
|
||||
"default": 720
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5029,7 +5029,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0.1,
|
||||
"x-example": "2",
|
||||
"default": 1
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5040,7 +5040,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "light",
|
||||
"x-example": "dark",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark"
|
||||
@@ -5057,7 +5057,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<USER_AGENT>",
|
||||
"x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15",
|
||||
"default": ""
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5068,7 +5068,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5079,7 +5079,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<LOCALE>",
|
||||
"x-example": "en-US",
|
||||
"default": ""
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5090,7 +5090,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "africa\/abidjan",
|
||||
"x-example": "america\/new_york",
|
||||
"enum": [
|
||||
"africa\/abidjan",
|
||||
"africa\/accra",
|
||||
@@ -5525,7 +5525,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -90,
|
||||
"x-example": "37.7749",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5537,7 +5537,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -180,
|
||||
"x-example": "-122.4194",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5549,7 +5549,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0,
|
||||
"x-example": "100",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5560,7 +5560,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5572,8 +5572,33 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"geolocation",
|
||||
"camera",
|
||||
"microphone",
|
||||
"notifications",
|
||||
"midi",
|
||||
"push",
|
||||
"clipboard-read",
|
||||
"clipboard-write",
|
||||
"payment-handler",
|
||||
"usb",
|
||||
"bluetooth",
|
||||
"accelerometer",
|
||||
"gyroscope",
|
||||
"magnetometer",
|
||||
"ambient-light-sensor",
|
||||
"background-sync",
|
||||
"persistent-storage",
|
||||
"screen-wake-lock",
|
||||
"web-share",
|
||||
"xr-spatial-tracking"
|
||||
],
|
||||
"x-enum-name": "BrowserPermission",
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"x-example": "[\"geolocation\",\"notifications\"]",
|
||||
"default": []
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5585,7 +5610,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "3",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5597,7 +5622,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "800",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5609,7 +5634,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "600",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5621,7 +5646,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": -1,
|
||||
"x-example": "85",
|
||||
"default": -1
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5632,7 +5657,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "jpg",
|
||||
"x-example": "jpeg",
|
||||
"enum": [
|
||||
"jpg",
|
||||
"jpeg",
|
||||
@@ -7370,7 +7395,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 251,
|
||||
"weight": 241,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7422,7 +7447,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 250,
|
||||
"weight": 240,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -7474,7 +7499,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": null,
|
||||
"weight": 71,
|
||||
"weight": 61,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/get.md",
|
||||
@@ -7526,7 +7551,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCodes",
|
||||
"group": null,
|
||||
"weight": 72,
|
||||
"weight": 62,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-codes.md",
|
||||
@@ -7578,7 +7603,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listContinents",
|
||||
"group": null,
|
||||
"weight": 76,
|
||||
"weight": 66,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-continents.md",
|
||||
@@ -7630,7 +7655,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountries",
|
||||
"group": null,
|
||||
"weight": 73,
|
||||
"weight": 63,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries.md",
|
||||
@@ -7682,7 +7707,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesEU",
|
||||
"group": null,
|
||||
"weight": 74,
|
||||
"weight": 64,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-eu.md",
|
||||
@@ -7734,7 +7759,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesPhones",
|
||||
"group": null,
|
||||
"weight": 75,
|
||||
"weight": 65,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-phones.md",
|
||||
@@ -7786,7 +7811,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCurrencies",
|
||||
"group": null,
|
||||
"weight": 77,
|
||||
"weight": 67,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-currencies.md",
|
||||
@@ -7838,7 +7863,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listLanguages",
|
||||
"group": null,
|
||||
"weight": 78,
|
||||
"weight": 68,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-languages.md",
|
||||
@@ -7890,7 +7915,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 300,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -7973,7 +7998,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 304,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8048,7 +8073,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 161,
|
||||
"weight": 151,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8145,7 +8170,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 160,
|
||||
"weight": 150,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8244,7 +8269,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 162,
|
||||
"weight": 152,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8316,7 +8341,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 167,
|
||||
"weight": 157,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8407,7 +8432,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 168,
|
||||
"weight": 158,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8474,7 +8499,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 164,
|
||||
"weight": 154,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8552,7 +8577,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 163,
|
||||
"weight": 153,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -8780,7 +8805,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 165,
|
||||
"weight": 155,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -10281,7 +10306,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"group": "teams",
|
||||
"weight": 172,
|
||||
"weight": 162,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list.md",
|
||||
@@ -10368,7 +10393,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"group": "teams",
|
||||
"weight": 171,
|
||||
"weight": 161,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create.md",
|
||||
@@ -10453,7 +10478,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": "teams",
|
||||
"weight": 173,
|
||||
"weight": 163,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get.md",
|
||||
@@ -10515,7 +10540,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"group": "teams",
|
||||
"weight": 175,
|
||||
"weight": 165,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-name.md",
|
||||
@@ -10589,7 +10614,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"group": "teams",
|
||||
"weight": 177,
|
||||
"weight": 167,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete.md",
|
||||
@@ -10653,7 +10678,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"group": "memberships",
|
||||
"weight": 179,
|
||||
"weight": 169,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list-memberships.md",
|
||||
@@ -10750,7 +10775,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"group": "memberships",
|
||||
"weight": 178,
|
||||
"weight": 168,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create-membership.md",
|
||||
@@ -10814,7 +10839,14 @@
|
||||
"description": "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 100 roles are allowed, each 32 characters long.",
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
@@ -10861,7 +10893,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"group": "memberships",
|
||||
"weight": 180,
|
||||
"weight": 170,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-membership.md",
|
||||
@@ -10933,7 +10965,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"group": "memberships",
|
||||
"weight": 181,
|
||||
"weight": 171,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership.md",
|
||||
@@ -10992,7 +11024,14 @@
|
||||
"description": "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 100 roles are allowed, each 32 characters long.",
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11020,7 +11059,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"group": "memberships",
|
||||
"weight": 183,
|
||||
"weight": 173,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete-membership.md",
|
||||
@@ -11094,7 +11133,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"group": "memberships",
|
||||
"weight": 182,
|
||||
"weight": 172,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership-status.md",
|
||||
@@ -11192,7 +11231,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"group": "teams",
|
||||
"weight": 174,
|
||||
"weight": 164,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-prefs.md",
|
||||
@@ -11253,7 +11292,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"group": "teams",
|
||||
"weight": 176,
|
||||
"weight": 166,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-prefs.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -258,7 +258,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listIdentities",
|
||||
"group": "identities",
|
||||
"weight": 58,
|
||||
"weight": 48,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-identities.md",
|
||||
@@ -328,7 +328,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteIdentity",
|
||||
"group": "identities",
|
||||
"weight": 59,
|
||||
"weight": 49,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-identity.md",
|
||||
@@ -517,7 +517,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 45,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -587,7 +587,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 47,
|
||||
"weight": 308,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -707,7 +707,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 48,
|
||||
"weight": 309,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -843,7 +843,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 52,
|
||||
"weight": 310,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -939,7 +939,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/account\/mfa\/challenge": {
|
||||
"\/account\/mfa\/challenges": {
|
||||
"post": {
|
||||
"summary": "Create MFA challenge",
|
||||
"operationId": "accountCreateMfaChallenge",
|
||||
@@ -963,7 +963,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 53,
|
||||
"weight": 314,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1091,7 +1091,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 54,
|
||||
"weight": 315,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1225,7 +1225,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 46,
|
||||
"weight": 307,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1322,7 +1322,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 51,
|
||||
"weight": 313,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1417,7 +1417,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 49,
|
||||
"weight": 311,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1512,7 +1512,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 50,
|
||||
"weight": 312,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -2918,7 +2918,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createPushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 55,
|
||||
"weight": 45,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-push-target.md",
|
||||
@@ -2997,7 +2997,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 56,
|
||||
"weight": 46,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-push-target.md",
|
||||
@@ -3068,7 +3068,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deletePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 57,
|
||||
"weight": 47,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-push-target.md",
|
||||
@@ -3879,7 +3879,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 61,
|
||||
"weight": 51,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4005,7 +4005,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 60,
|
||||
"weight": 50,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4137,7 +4137,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 64,
|
||||
"weight": 54,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4195,7 +4195,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 62,
|
||||
"weight": 52,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -4683,7 +4683,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 63,
|
||||
"weight": 53,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -4765,7 +4765,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 66,
|
||||
"weight": 56,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -4857,7 +4857,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 65,
|
||||
"weight": 55,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -4949,7 +4949,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 67,
|
||||
"weight": 57,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -4993,7 +4993,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"x-example": "{}",
|
||||
"x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}",
|
||||
"default": {}
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5005,7 +5005,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1920",
|
||||
"default": 1280
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5017,7 +5017,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1080",
|
||||
"default": 720
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5029,7 +5029,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0.1,
|
||||
"x-example": "2",
|
||||
"default": 1
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5040,7 +5040,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "light",
|
||||
"x-example": "dark",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark"
|
||||
@@ -5057,7 +5057,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<USER_AGENT>",
|
||||
"x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15",
|
||||
"default": ""
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5068,7 +5068,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5079,7 +5079,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<LOCALE>",
|
||||
"x-example": "en-US",
|
||||
"default": ""
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5090,7 +5090,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "africa\/abidjan",
|
||||
"x-example": "america\/new_york",
|
||||
"enum": [
|
||||
"africa\/abidjan",
|
||||
"africa\/accra",
|
||||
@@ -5525,7 +5525,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -90,
|
||||
"x-example": "37.7749",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5537,7 +5537,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -180,
|
||||
"x-example": "-122.4194",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5549,7 +5549,7 @@
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0,
|
||||
"x-example": "100",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5560,7 +5560,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5572,8 +5572,33 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"geolocation",
|
||||
"camera",
|
||||
"microphone",
|
||||
"notifications",
|
||||
"midi",
|
||||
"push",
|
||||
"clipboard-read",
|
||||
"clipboard-write",
|
||||
"payment-handler",
|
||||
"usb",
|
||||
"bluetooth",
|
||||
"accelerometer",
|
||||
"gyroscope",
|
||||
"magnetometer",
|
||||
"ambient-light-sensor",
|
||||
"background-sync",
|
||||
"persistent-storage",
|
||||
"screen-wake-lock",
|
||||
"web-share",
|
||||
"xr-spatial-tracking"
|
||||
],
|
||||
"x-enum-name": "BrowserPermission",
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"x-example": "[\"geolocation\",\"notifications\"]",
|
||||
"default": []
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5585,7 +5610,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "3",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5597,7 +5622,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "800",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5609,7 +5634,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "600",
|
||||
"default": 0
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5621,7 +5646,7 @@
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": -1,
|
||||
"x-example": "85",
|
||||
"default": -1
|
||||
},
|
||||
"in": "query"
|
||||
@@ -5632,7 +5657,7 @@
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "jpg",
|
||||
"x-example": "jpeg",
|
||||
"enum": [
|
||||
"jpg",
|
||||
"jpeg",
|
||||
@@ -7370,7 +7395,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 251,
|
||||
"weight": 241,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7422,7 +7447,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 250,
|
||||
"weight": 240,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -7474,7 +7499,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": null,
|
||||
"weight": 71,
|
||||
"weight": 61,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/get.md",
|
||||
@@ -7526,7 +7551,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCodes",
|
||||
"group": null,
|
||||
"weight": 72,
|
||||
"weight": 62,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-codes.md",
|
||||
@@ -7578,7 +7603,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listContinents",
|
||||
"group": null,
|
||||
"weight": 76,
|
||||
"weight": 66,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-continents.md",
|
||||
@@ -7630,7 +7655,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountries",
|
||||
"group": null,
|
||||
"weight": 73,
|
||||
"weight": 63,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries.md",
|
||||
@@ -7682,7 +7707,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesEU",
|
||||
"group": null,
|
||||
"weight": 74,
|
||||
"weight": 64,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-eu.md",
|
||||
@@ -7734,7 +7759,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesPhones",
|
||||
"group": null,
|
||||
"weight": 75,
|
||||
"weight": 65,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-phones.md",
|
||||
@@ -7786,7 +7811,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCurrencies",
|
||||
"group": null,
|
||||
"weight": 77,
|
||||
"weight": 67,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-currencies.md",
|
||||
@@ -7838,7 +7863,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listLanguages",
|
||||
"group": null,
|
||||
"weight": 78,
|
||||
"weight": 68,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-languages.md",
|
||||
@@ -7890,7 +7915,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 300,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -7973,7 +7998,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 304,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8048,7 +8073,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 161,
|
||||
"weight": 151,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8145,7 +8170,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 160,
|
||||
"weight": 150,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8244,7 +8269,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 162,
|
||||
"weight": 152,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8316,7 +8341,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 167,
|
||||
"weight": 157,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8407,7 +8432,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 168,
|
||||
"weight": 158,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8474,7 +8499,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 164,
|
||||
"weight": 154,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8552,7 +8577,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 163,
|
||||
"weight": 153,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -8780,7 +8805,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 165,
|
||||
"weight": 155,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -10281,7 +10306,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"group": "teams",
|
||||
"weight": 172,
|
||||
"weight": 162,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list.md",
|
||||
@@ -10368,7 +10393,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"group": "teams",
|
||||
"weight": 171,
|
||||
"weight": 161,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create.md",
|
||||
@@ -10453,7 +10478,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": "teams",
|
||||
"weight": 173,
|
||||
"weight": 163,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get.md",
|
||||
@@ -10515,7 +10540,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"group": "teams",
|
||||
"weight": 175,
|
||||
"weight": 165,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-name.md",
|
||||
@@ -10589,7 +10614,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"group": "teams",
|
||||
"weight": 177,
|
||||
"weight": 167,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete.md",
|
||||
@@ -10653,7 +10678,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"group": "memberships",
|
||||
"weight": 179,
|
||||
"weight": 169,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list-memberships.md",
|
||||
@@ -10750,7 +10775,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"group": "memberships",
|
||||
"weight": 178,
|
||||
"weight": 168,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create-membership.md",
|
||||
@@ -10814,7 +10839,14 @@
|
||||
"description": "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 100 roles are allowed, each 32 characters long.",
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
@@ -10861,7 +10893,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"group": "memberships",
|
||||
"weight": 180,
|
||||
"weight": 170,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-membership.md",
|
||||
@@ -10933,7 +10965,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"group": "memberships",
|
||||
"weight": 181,
|
||||
"weight": 171,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership.md",
|
||||
@@ -10992,7 +11024,14 @@
|
||||
"description": "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 100 roles are allowed, each 32 characters long.",
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11020,7 +11059,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"group": "memberships",
|
||||
"weight": 183,
|
||||
"weight": 173,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete-membership.md",
|
||||
@@ -11094,7 +11133,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"group": "memberships",
|
||||
"weight": 182,
|
||||
"weight": 172,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership-status.md",
|
||||
@@ -11192,7 +11231,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"group": "teams",
|
||||
"weight": 174,
|
||||
"weight": 164,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-prefs.md",
|
||||
@@ -11253,7 +11292,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"group": "teams",
|
||||
"weight": 176,
|
||||
"weight": 166,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-prefs.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -314,7 +314,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listIdentities",
|
||||
"group": "identities",
|
||||
"weight": 58,
|
||||
"weight": 48,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-identities.md",
|
||||
@@ -385,7 +385,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteIdentity",
|
||||
"group": "identities",
|
||||
"weight": 59,
|
||||
"weight": 49,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-identity.md",
|
||||
@@ -573,7 +573,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 45,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -646,7 +646,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 47,
|
||||
"weight": 308,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -766,7 +766,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 48,
|
||||
"weight": 309,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -903,7 +903,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 52,
|
||||
"weight": 310,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -997,7 +997,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/account\/mfa\/challenge": {
|
||||
"\/account\/mfa\/challenges": {
|
||||
"post": {
|
||||
"summary": "Create MFA challenge",
|
||||
"operationId": "accountCreateMfaChallenge",
|
||||
@@ -1023,7 +1023,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 53,
|
||||
"weight": 314,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1154,7 +1154,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 54,
|
||||
"weight": 315,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1290,7 +1290,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 46,
|
||||
"weight": 307,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1387,7 +1387,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 51,
|
||||
"weight": 313,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1484,7 +1484,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 49,
|
||||
"weight": 311,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1581,7 +1581,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 50,
|
||||
"weight": 312,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -3033,7 +3033,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createPushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 55,
|
||||
"weight": 45,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-push-target.md",
|
||||
@@ -3117,7 +3117,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 56,
|
||||
"weight": 46,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-push-target.md",
|
||||
@@ -3189,7 +3189,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deletePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 57,
|
||||
"weight": 47,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-push-target.md",
|
||||
@@ -4030,7 +4030,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 61,
|
||||
"weight": 51,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4154,7 +4154,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 60,
|
||||
"weight": 50,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4284,7 +4284,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 64,
|
||||
"weight": 54,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4346,7 +4346,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 62,
|
||||
"weight": 52,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -4832,7 +4832,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 63,
|
||||
"weight": 53,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -4914,7 +4914,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 66,
|
||||
"weight": 56,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -5004,7 +5004,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 65,
|
||||
"weight": 55,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -5094,7 +5094,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 67,
|
||||
"weight": 57,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -5135,7 +5135,7 @@
|
||||
"required": false,
|
||||
"type": "object",
|
||||
"default": [],
|
||||
"x-example": "{}",
|
||||
"x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
@@ -5144,7 +5144,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1920",
|
||||
"default": 1280,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5154,7 +5154,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1080",
|
||||
"default": 720,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5164,7 +5164,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0.1,
|
||||
"x-example": "2",
|
||||
"default": 1,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5173,7 +5173,7 @@
|
||||
"description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "light",
|
||||
"x-example": "dark",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark"
|
||||
@@ -5188,7 +5188,7 @@
|
||||
"description": "Custom user agent string. Defaults to browser default.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "<USER_AGENT>",
|
||||
"x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15",
|
||||
"default": "",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5197,7 +5197,7 @@
|
||||
"description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.",
|
||||
"required": false,
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5206,7 +5206,7 @@
|
||||
"description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "<LOCALE>",
|
||||
"x-example": "en-US",
|
||||
"default": "",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5215,7 +5215,7 @@
|
||||
"description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "africa\/abidjan",
|
||||
"x-example": "america\/new_york",
|
||||
"enum": [
|
||||
"africa\/abidjan",
|
||||
"africa\/accra",
|
||||
@@ -5648,7 +5648,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -90,
|
||||
"x-example": "37.7749",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5658,7 +5658,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -180,
|
||||
"x-example": "-122.4194",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5668,7 +5668,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0,
|
||||
"x-example": "100",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5677,7 +5677,7 @@
|
||||
"description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.",
|
||||
"required": false,
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5688,8 +5688,33 @@
|
||||
"type": "array",
|
||||
"collectionFormat": "multi",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"geolocation",
|
||||
"camera",
|
||||
"microphone",
|
||||
"notifications",
|
||||
"midi",
|
||||
"push",
|
||||
"clipboard-read",
|
||||
"clipboard-write",
|
||||
"payment-handler",
|
||||
"usb",
|
||||
"bluetooth",
|
||||
"accelerometer",
|
||||
"gyroscope",
|
||||
"magnetometer",
|
||||
"ambient-light-sensor",
|
||||
"background-sync",
|
||||
"persistent-storage",
|
||||
"screen-wake-lock",
|
||||
"web-share",
|
||||
"xr-spatial-tracking"
|
||||
],
|
||||
"x-enum-name": "BrowserPermission",
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"x-example": "[\"geolocation\",\"notifications\"]",
|
||||
"default": [],
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5699,7 +5724,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "3",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5709,7 +5734,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "800",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5719,7 +5744,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "600",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5729,7 +5754,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": -1,
|
||||
"x-example": "85",
|
||||
"default": -1,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5738,7 +5763,7 @@
|
||||
"description": "Output format type (jpeg, jpg, png, gif and webp).",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "jpg",
|
||||
"x-example": "jpeg",
|
||||
"enum": [
|
||||
"jpg",
|
||||
"jpeg",
|
||||
@@ -7420,7 +7445,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 251,
|
||||
"weight": 241,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7493,7 +7518,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 250,
|
||||
"weight": 240,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -7564,7 +7589,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": null,
|
||||
"weight": 71,
|
||||
"weight": 61,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/get.md",
|
||||
@@ -7615,7 +7640,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCodes",
|
||||
"group": null,
|
||||
"weight": 72,
|
||||
"weight": 62,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-codes.md",
|
||||
@@ -7666,7 +7691,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listContinents",
|
||||
"group": null,
|
||||
"weight": 76,
|
||||
"weight": 66,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-continents.md",
|
||||
@@ -7717,7 +7742,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountries",
|
||||
"group": null,
|
||||
"weight": 73,
|
||||
"weight": 63,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries.md",
|
||||
@@ -7768,7 +7793,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesEU",
|
||||
"group": null,
|
||||
"weight": 74,
|
||||
"weight": 64,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-eu.md",
|
||||
@@ -7819,7 +7844,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesPhones",
|
||||
"group": null,
|
||||
"weight": 75,
|
||||
"weight": 65,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-phones.md",
|
||||
@@ -7870,7 +7895,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCurrencies",
|
||||
"group": null,
|
||||
"weight": 77,
|
||||
"weight": 67,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-currencies.md",
|
||||
@@ -7921,7 +7946,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listLanguages",
|
||||
"group": null,
|
||||
"weight": 78,
|
||||
"weight": 68,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-languages.md",
|
||||
@@ -7974,7 +7999,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 300,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -8058,7 +8083,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 304,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8128,7 +8153,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 161,
|
||||
"weight": 151,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8219,7 +8244,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 160,
|
||||
"weight": 150,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8308,7 +8333,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 162,
|
||||
"weight": 152,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8377,7 +8402,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 167,
|
||||
"weight": 157,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8467,7 +8492,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 168,
|
||||
"weight": 158,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8536,7 +8561,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 164,
|
||||
"weight": 154,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8614,7 +8639,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 163,
|
||||
"weight": 153,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -8820,7 +8845,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 165,
|
||||
"weight": 155,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -10265,7 +10290,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"group": "teams",
|
||||
"weight": 172,
|
||||
"weight": 162,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list.md",
|
||||
@@ -10348,7 +10373,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"group": "teams",
|
||||
"weight": 171,
|
||||
"weight": 161,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create.md",
|
||||
@@ -10437,7 +10462,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": "teams",
|
||||
"weight": 173,
|
||||
"weight": 163,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get.md",
|
||||
@@ -10498,7 +10523,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"group": "teams",
|
||||
"weight": 175,
|
||||
"weight": 165,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-name.md",
|
||||
@@ -10572,7 +10597,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"group": "teams",
|
||||
"weight": 177,
|
||||
"weight": 167,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete.md",
|
||||
@@ -10633,7 +10658,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"group": "memberships",
|
||||
"weight": 179,
|
||||
"weight": 169,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list-memberships.md",
|
||||
@@ -10724,7 +10749,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"group": "memberships",
|
||||
"weight": 178,
|
||||
"weight": 168,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create-membership.md",
|
||||
@@ -10788,7 +10813,14 @@
|
||||
"default": null,
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
@@ -10836,7 +10868,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"group": "memberships",
|
||||
"weight": 180,
|
||||
"weight": 170,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-membership.md",
|
||||
@@ -10905,7 +10937,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"group": "memberships",
|
||||
"weight": 181,
|
||||
"weight": 171,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership.md",
|
||||
@@ -10959,7 +10991,14 @@
|
||||
"default": null,
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10990,7 +11029,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"group": "memberships",
|
||||
"weight": 183,
|
||||
"weight": 173,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete-membership.md",
|
||||
@@ -11061,7 +11100,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"group": "memberships",
|
||||
"weight": 182,
|
||||
"weight": 172,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership-status.md",
|
||||
@@ -11155,7 +11194,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"group": "teams",
|
||||
"weight": 174,
|
||||
"weight": 164,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-prefs.md",
|
||||
@@ -11216,7 +11255,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"group": "teams",
|
||||
"weight": 176,
|
||||
"weight": 166,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-prefs.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -314,7 +314,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listIdentities",
|
||||
"group": "identities",
|
||||
"weight": 58,
|
||||
"weight": 48,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-identities.md",
|
||||
@@ -385,7 +385,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteIdentity",
|
||||
"group": "identities",
|
||||
"weight": 59,
|
||||
"weight": 49,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-identity.md",
|
||||
@@ -573,7 +573,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 45,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -646,7 +646,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 47,
|
||||
"weight": 308,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -766,7 +766,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 48,
|
||||
"weight": 309,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -903,7 +903,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 52,
|
||||
"weight": 310,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -997,7 +997,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/account\/mfa\/challenge": {
|
||||
"\/account\/mfa\/challenges": {
|
||||
"post": {
|
||||
"summary": "Create MFA challenge",
|
||||
"operationId": "accountCreateMfaChallenge",
|
||||
@@ -1023,7 +1023,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 53,
|
||||
"weight": 314,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1154,7 +1154,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 54,
|
||||
"weight": 315,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1290,7 +1290,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 46,
|
||||
"weight": 307,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1387,7 +1387,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 51,
|
||||
"weight": 313,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1484,7 +1484,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 49,
|
||||
"weight": 311,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1581,7 +1581,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 50,
|
||||
"weight": 312,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -3033,7 +3033,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createPushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 55,
|
||||
"weight": 45,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-push-target.md",
|
||||
@@ -3117,7 +3117,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 56,
|
||||
"weight": 46,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-push-target.md",
|
||||
@@ -3189,7 +3189,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deletePushTarget",
|
||||
"group": "pushTargets",
|
||||
"weight": 57,
|
||||
"weight": 47,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-push-target.md",
|
||||
@@ -4030,7 +4030,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 61,
|
||||
"weight": 51,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4154,7 +4154,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 60,
|
||||
"weight": 50,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4284,7 +4284,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 64,
|
||||
"weight": 54,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4346,7 +4346,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 62,
|
||||
"weight": 52,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -4832,7 +4832,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 63,
|
||||
"weight": 53,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -4914,7 +4914,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 66,
|
||||
"weight": 56,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -5004,7 +5004,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 65,
|
||||
"weight": 55,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -5094,7 +5094,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 67,
|
||||
"weight": 57,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -5135,7 +5135,7 @@
|
||||
"required": false,
|
||||
"type": "object",
|
||||
"default": [],
|
||||
"x-example": "{}",
|
||||
"x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
@@ -5144,7 +5144,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1920",
|
||||
"default": 1280,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5154,7 +5154,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 1,
|
||||
"x-example": "1080",
|
||||
"default": 720,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5164,7 +5164,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0.1,
|
||||
"x-example": "2",
|
||||
"default": 1,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5173,7 +5173,7 @@
|
||||
"description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "light",
|
||||
"x-example": "dark",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark"
|
||||
@@ -5188,7 +5188,7 @@
|
||||
"description": "Custom user agent string. Defaults to browser default.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "<USER_AGENT>",
|
||||
"x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15",
|
||||
"default": "",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5197,7 +5197,7 @@
|
||||
"description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.",
|
||||
"required": false,
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5206,7 +5206,7 @@
|
||||
"description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "<LOCALE>",
|
||||
"x-example": "en-US",
|
||||
"default": "",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5215,7 +5215,7 @@
|
||||
"description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "africa\/abidjan",
|
||||
"x-example": "america\/new_york",
|
||||
"enum": [
|
||||
"africa\/abidjan",
|
||||
"africa\/accra",
|
||||
@@ -5648,7 +5648,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -90,
|
||||
"x-example": "37.7749",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5658,7 +5658,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": -180,
|
||||
"x-example": "-122.4194",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5668,7 +5668,7 @@
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"x-example": 0,
|
||||
"x-example": "100",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5677,7 +5677,7 @@
|
||||
"description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.",
|
||||
"required": false,
|
||||
"type": "boolean",
|
||||
"x-example": false,
|
||||
"x-example": "true",
|
||||
"default": false,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5688,8 +5688,33 @@
|
||||
"type": "array",
|
||||
"collectionFormat": "multi",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"geolocation",
|
||||
"camera",
|
||||
"microphone",
|
||||
"notifications",
|
||||
"midi",
|
||||
"push",
|
||||
"clipboard-read",
|
||||
"clipboard-write",
|
||||
"payment-handler",
|
||||
"usb",
|
||||
"bluetooth",
|
||||
"accelerometer",
|
||||
"gyroscope",
|
||||
"magnetometer",
|
||||
"ambient-light-sensor",
|
||||
"background-sync",
|
||||
"persistent-storage",
|
||||
"screen-wake-lock",
|
||||
"web-share",
|
||||
"xr-spatial-tracking"
|
||||
],
|
||||
"x-enum-name": "BrowserPermission",
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"x-example": "[\"geolocation\",\"notifications\"]",
|
||||
"default": [],
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5699,7 +5724,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "3",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5709,7 +5734,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "800",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5719,7 +5744,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": 0,
|
||||
"x-example": "600",
|
||||
"default": 0,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5729,7 +5754,7 @@
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"x-example": -1,
|
||||
"x-example": "85",
|
||||
"default": -1,
|
||||
"in": "query"
|
||||
},
|
||||
@@ -5738,7 +5763,7 @@
|
||||
"description": "Output format type (jpeg, jpg, png, gif and webp).",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "jpg",
|
||||
"x-example": "jpeg",
|
||||
"enum": [
|
||||
"jpg",
|
||||
"jpeg",
|
||||
@@ -7420,7 +7445,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 251,
|
||||
"weight": 241,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7493,7 +7518,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 250,
|
||||
"weight": 240,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -7564,7 +7589,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": null,
|
||||
"weight": 71,
|
||||
"weight": 61,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/get.md",
|
||||
@@ -7615,7 +7640,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCodes",
|
||||
"group": null,
|
||||
"weight": 72,
|
||||
"weight": 62,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-codes.md",
|
||||
@@ -7666,7 +7691,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listContinents",
|
||||
"group": null,
|
||||
"weight": 76,
|
||||
"weight": 66,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-continents.md",
|
||||
@@ -7717,7 +7742,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountries",
|
||||
"group": null,
|
||||
"weight": 73,
|
||||
"weight": 63,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries.md",
|
||||
@@ -7768,7 +7793,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesEU",
|
||||
"group": null,
|
||||
"weight": 74,
|
||||
"weight": 64,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-eu.md",
|
||||
@@ -7819,7 +7844,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesPhones",
|
||||
"group": null,
|
||||
"weight": 75,
|
||||
"weight": 65,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-countries-phones.md",
|
||||
@@ -7870,7 +7895,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCurrencies",
|
||||
"group": null,
|
||||
"weight": 77,
|
||||
"weight": 67,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-currencies.md",
|
||||
@@ -7921,7 +7946,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listLanguages",
|
||||
"group": null,
|
||||
"weight": 78,
|
||||
"weight": 68,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "locale\/list-languages.md",
|
||||
@@ -7974,7 +7999,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 300,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -8058,7 +8083,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 304,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8128,7 +8153,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 161,
|
||||
"weight": 151,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8219,7 +8244,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 160,
|
||||
"weight": 150,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8308,7 +8333,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 162,
|
||||
"weight": 152,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8377,7 +8402,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 167,
|
||||
"weight": 157,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8467,7 +8492,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 168,
|
||||
"weight": 158,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8536,7 +8561,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 164,
|
||||
"weight": 154,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8614,7 +8639,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 163,
|
||||
"weight": 153,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -8820,7 +8845,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 165,
|
||||
"weight": 155,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -10265,7 +10290,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"group": "teams",
|
||||
"weight": 172,
|
||||
"weight": 162,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list.md",
|
||||
@@ -10348,7 +10373,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"group": "teams",
|
||||
"weight": 171,
|
||||
"weight": 161,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create.md",
|
||||
@@ -10437,7 +10462,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": "teams",
|
||||
"weight": 173,
|
||||
"weight": 163,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get.md",
|
||||
@@ -10498,7 +10523,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"group": "teams",
|
||||
"weight": 175,
|
||||
"weight": 165,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-name.md",
|
||||
@@ -10572,7 +10597,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"group": "teams",
|
||||
"weight": 177,
|
||||
"weight": 167,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete.md",
|
||||
@@ -10633,7 +10658,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"group": "memberships",
|
||||
"weight": 179,
|
||||
"weight": 169,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/list-memberships.md",
|
||||
@@ -10724,7 +10749,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"group": "memberships",
|
||||
"weight": 178,
|
||||
"weight": 168,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/create-membership.md",
|
||||
@@ -10788,7 +10813,14 @@
|
||||
"default": null,
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
@@ -10836,7 +10868,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"group": "memberships",
|
||||
"weight": 180,
|
||||
"weight": 170,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-membership.md",
|
||||
@@ -10905,7 +10937,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"group": "memberships",
|
||||
"weight": 181,
|
||||
"weight": 171,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership.md",
|
||||
@@ -10959,7 +10991,14 @@
|
||||
"default": null,
|
||||
"x-example": null,
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"developer",
|
||||
"owner"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10990,7 +11029,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"group": "memberships",
|
||||
"weight": 183,
|
||||
"weight": 173,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/delete-membership.md",
|
||||
@@ -11061,7 +11100,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"group": "memberships",
|
||||
"weight": 182,
|
||||
"weight": 172,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-membership-status.md",
|
||||
@@ -11155,7 +11194,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"group": "teams",
|
||||
"weight": 174,
|
||||
"weight": 164,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/get-prefs.md",
|
||||
@@ -11216,7 +11255,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"group": "teams",
|
||||
"weight": 176,
|
||||
"weight": 166,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "teams\/update-prefs.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,43 +1,12 @@
|
||||
<?php
|
||||
|
||||
// TODO: Remove, replace with runtimes.php directly
|
||||
// Used in function templates and site frameworks
|
||||
use Utopia\Config\Config;
|
||||
|
||||
return [
|
||||
'NODE' => [
|
||||
'name' => 'node',
|
||||
'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
|
||||
],
|
||||
'PYTHON' => [
|
||||
'name' => 'python',
|
||||
'versions' => ['3.12', '3.11', '3.10', '3.9', '3.8']
|
||||
],
|
||||
'DART' => [
|
||||
'name' => 'dart',
|
||||
'versions' => ['3.9', '3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
'versions' => ['1.23']
|
||||
],
|
||||
'PHP' => [
|
||||
'name' => 'php',
|
||||
'versions' => ['8.3', '8.2', '8.1', '8.0']
|
||||
],
|
||||
'DENO' => [
|
||||
'name' => 'deno',
|
||||
'versions' => ['2.0', '1.46', '1.40', '1.35', '1.24', '1.21']
|
||||
],
|
||||
'BUN' => [
|
||||
'name' => 'bun',
|
||||
'versions' => ['1.1', '1.0']
|
||||
],
|
||||
'RUBY' => [
|
||||
'name' => 'ruby',
|
||||
'versions' => ['3.3', '3.2', '3.1', '3.0']
|
||||
],
|
||||
'FLUTTER' => [
|
||||
'name' => 'flutter',
|
||||
'versions' => ['3.35', '3.32', '3.24']
|
||||
],
|
||||
];
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
|
||||
$mappedRuntimes = \array_reduce($runtimes, function ($acc, $runtime) {
|
||||
$acc[strtoupper($runtime['key'])][] = $runtime['key'] . '-' . $runtime['version'];
|
||||
return $acc;
|
||||
}, []);
|
||||
|
||||
return $mappedRuntimes;
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\System\System;
|
||||
|
||||
$templateRuntimes = Config::getParam('template-runtimes');
|
||||
$allowList = \array_map('trim', \explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
|
||||
|
||||
function getRuntimes($runtime, $commands, $entrypoint, $providerRootDirectory, $versionsDenyList = [])
|
||||
function getRuntimes($runtimes, $commands, $entrypoint, $providerRootDirectory, $allowList)
|
||||
{
|
||||
return array_map(function ($version) use ($runtime, $commands, $entrypoint, $providerRootDirectory) {
|
||||
return array_map(function ($runtime) use ($commands, $entrypoint, $providerRootDirectory) {
|
||||
return [
|
||||
'name' => $runtime['name'] . '-' . $version,
|
||||
'name' => $runtime,
|
||||
'commands' => $commands,
|
||||
'entrypoint' => $entrypoint,
|
||||
'providerRootDirectory' => $providerRootDirectory
|
||||
];
|
||||
}, array_filter($runtime['versions'], function ($version) use ($versionsDenyList) {
|
||||
return !in_array($version, $versionsDenyList);
|
||||
}, array_filter($runtimes, function ($runtime) use ($allowList) {
|
||||
return in_array($runtime, $allowList);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -32,24 +34,26 @@ return [
|
||||
'timeout' => 15,
|
||||
'useCases' => ['starter'],
|
||||
'runtimes' => [
|
||||
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/starter'),
|
||||
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/starter', $allowList),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/starter'
|
||||
'python/starter',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
|
||||
...getRuntimes($templateRuntimes['GO'], '', 'main.go', 'go/starter'),
|
||||
...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter', $allowList),
|
||||
...getRuntimes($templateRuntimes['GO'], '', 'main.go', 'go/starter', $allowList),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/starter'
|
||||
'php/starter',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes($templateRuntimes['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter'),
|
||||
...getRuntimes($templateRuntimes['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
|
||||
...getRuntimes($templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'),
|
||||
...getRuntimes($templateRuntimes['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter', $allowList),
|
||||
...getRuntimes($templateRuntimes['BUN'], 'bun install', 'src/main.ts', 'bun/starter', $allowList),
|
||||
...getRuntimes($templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter', $allowList),
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/starter">file</a>.',
|
||||
'vcsProvider' => 'github',
|
||||
@@ -75,7 +79,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-upstash-vector'
|
||||
'node/query-upstash-vector',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/query-upstash-vector">file</a>.',
|
||||
@@ -120,7 +125,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-redis-labs'
|
||||
'node/query-redis-labs',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/query-redis-labs">file</a>.',
|
||||
@@ -164,7 +170,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-neo4j-auradb'
|
||||
'node/query-neo4j-auradb',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/query-neo4j-auradb">file</a>.',
|
||||
@@ -217,7 +224,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-mongo-atlas'
|
||||
'node/query-mongo-atlas',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/query-mongo-atlas">file</a>.',
|
||||
@@ -255,7 +263,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-neon-postgres'
|
||||
'node/query-neon-postgres',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/query-neon-postgres">file</a>.',
|
||||
@@ -323,25 +332,29 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/prompt-chatgpt'
|
||||
'node/prompt-chatgpt',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/prompt_chatgpt'
|
||||
'python/prompt_chatgpt',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/prompt-chatgpt'
|
||||
'php/prompt-chatgpt',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/prompt_chatgpt'
|
||||
'dart/prompt_chatgpt',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/prompt-chatgpt">file</a>.',
|
||||
@@ -385,19 +398,22 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/discord-command-bot'
|
||||
'node/discord-command-bot',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt && python src/setup.py',
|
||||
'src/main.py',
|
||||
'python/discord_command_bot'
|
||||
'python/discord_command_bot',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['GO'],
|
||||
'',
|
||||
'main.go',
|
||||
'go/discord-command-bot'
|
||||
'go/discord-command-bot',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/discord-command-bot">file</a>.',
|
||||
@@ -449,7 +465,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/analyze-with-perspectiveapi'
|
||||
'node/analyze-with-perspectiveapi',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/analyze-with-perspectiveapi">file</a>.',
|
||||
@@ -486,19 +503,22 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/censor-with-redact'
|
||||
'node/censor-with-redact',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/censor_with_redact'
|
||||
'python/censor_with_redact',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/censor_with_redact'
|
||||
'dart/censor_with_redact',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/censor-with-redact">file</a>.',
|
||||
@@ -530,7 +550,7 @@ return [
|
||||
'timeout' => 15,
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
|
||||
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf', $allowList)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/generate-pdf">file</a>.',
|
||||
'vcsProvider' => 'github',
|
||||
@@ -557,7 +577,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/github-issue-bot'
|
||||
'node/github-issue-bot',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/github-issue-bot">file</a>.',
|
||||
@@ -601,7 +622,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/url-shortener'
|
||||
'node/url-shortener',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/url-shortener">file</a>.',
|
||||
@@ -653,19 +675,22 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/sync-with-algolia'
|
||||
'node/sync-with-algolia',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/sync_with_algolia'
|
||||
'python/sync_with_algolia',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/sync-with-algolia'
|
||||
'php/sync-with-algolia',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/sync-with-algolia">file</a>.',
|
||||
@@ -735,31 +760,36 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/sync-with-meilisearch'
|
||||
'node/sync-with-meilisearch',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/sync-with-meilisearch'
|
||||
'python/sync-with-meilisearch',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/sync-with-meilisearch'
|
||||
'php/sync-with-meilisearch',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['BUN'],
|
||||
'bun install',
|
||||
'src/main.ts',
|
||||
'bun/sync-with-meilisearch'
|
||||
'bun/sync-with-meilisearch',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['RUBY'],
|
||||
'bundle install',
|
||||
'lib/main.rb',
|
||||
'ruby/sync-with-meilisearch'
|
||||
'ruby/sync-with-meilisearch',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/sync-with-meilisearch">file</a>.',
|
||||
@@ -829,37 +859,43 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/whatsapp-with-vonage'
|
||||
'node/whatsapp-with-vonage',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/whatsapp_with_vonage'
|
||||
'python/whatsapp_with_vonage',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/whatsapp-with-vonage'
|
||||
'dart/whatsapp-with-vonage',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/whatsapp-with-vonage'
|
||||
'php/whatsapp-with-vonage',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['BUN'],
|
||||
'bun install',
|
||||
'src/main.ts',
|
||||
'bun/whatsapp-with-vonage'
|
||||
'bun/whatsapp-with-vonage',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['RUBY'],
|
||||
'bundle install',
|
||||
'lib/main.rb',
|
||||
'ruby/whatsapp-with-vonage'
|
||||
'ruby/whatsapp-with-vonage',
|
||||
$allowList
|
||||
),
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/whatsapp-with-vonage">file</a>.',
|
||||
@@ -916,7 +952,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/push-notification-with-fcm'
|
||||
'node/push-notification-with-fcm',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/push-notification-with-fcm">file</a>.',
|
||||
@@ -973,19 +1010,22 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/email-contact-form'
|
||||
'node/email-contact-form',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/email_contact_form'
|
||||
'python/email_contact_form',
|
||||
$allowList
|
||||
),
|
||||
...getRuntimes(
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/email-contact-form'
|
||||
'php/email-contact-form',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/email-contact-form">file</a>.',
|
||||
@@ -1057,7 +1097,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/subscriptions-with-stripe'
|
||||
'node/subscriptions-with-stripe',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/subscriptions-with-stripe">file</a>.',
|
||||
@@ -1099,7 +1140,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/payments-with-stripe'
|
||||
'node/payments-with-stripe',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/payments-with-stripe">file</a>.',
|
||||
@@ -1157,7 +1199,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/text-generation-with-huggingface'
|
||||
'node/text-generation-with-huggingface',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/text-generation-with-huggingface">file</a>.',
|
||||
@@ -1192,7 +1235,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/language-translation-with-huggingface'
|
||||
'node/language-translation-with-huggingface',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/language-translation-with-huggingface">file</a>.',
|
||||
@@ -1227,7 +1271,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/image-classification-with-huggingface'
|
||||
'node/image-classification-with-huggingface',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/image-classification-with-huggingface">file</a>.',
|
||||
@@ -1286,7 +1331,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/object-detection-with-huggingface'
|
||||
'node/object-detection-with-huggingface',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/object-detection-with-huggingface">file</a>.',
|
||||
@@ -1345,7 +1391,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/speech-recognition-with-huggingface'
|
||||
'node/speech-recognition-with-huggingface',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/speech-recognition-with-huggingface">file</a>.',
|
||||
@@ -1407,7 +1454,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/text-to-speech-with-huggingface'
|
||||
'node/text-to-speech-with-huggingface',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/text-to-speech-with-huggingface">file</a>.',
|
||||
@@ -1466,7 +1514,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-replicate'
|
||||
'node/generate-with-replicate',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/generate-with-replicate">file</a>.',
|
||||
@@ -1502,7 +1551,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-together-ai'
|
||||
'node/generate-with-together-ai',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/generate-with-together-ai">file</a>.',
|
||||
@@ -1545,7 +1595,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/chat-with-perplexity-ai'
|
||||
'node/chat-with-perplexity-ai',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/chat-with-perplexity-ai">file</a>.',
|
||||
@@ -1587,7 +1638,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-replicate'
|
||||
'node/generate-with-replicate',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/generate-with-replicate">file</a>.',
|
||||
@@ -1623,7 +1675,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/sync-with-pinecone'
|
||||
'node/sync-with-pinecone',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/sync-with-pinecone">file</a>.',
|
||||
@@ -1687,7 +1740,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/rag-with-langchain'
|
||||
'node/rag-with-langchain',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/rag-with-langchain">file</a>.',
|
||||
@@ -1751,7 +1805,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/speak-with-elevenlabs'
|
||||
'node/speak-with-elevenlabs',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/speak-with-elevenlabs">file</a>.',
|
||||
@@ -1807,7 +1862,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/speak-with-lmnt'
|
||||
'node/speak-with-lmnt',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/speak-with-lmnt">file</a>.',
|
||||
@@ -1849,7 +1905,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/chat-with-anyscale'
|
||||
'node/chat-with-anyscale',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/chat-with-anyscale">file</a>.',
|
||||
@@ -1891,7 +1948,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/music-generation-with-huggingface'
|
||||
'node/music-generation-with-huggingface',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/music-generation-with-huggingface">file</a>.',
|
||||
@@ -1934,7 +1992,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-fal-ai'
|
||||
'node/generate-with-fal-ai',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/generate-with-fal-ai">file</a>.',
|
||||
@@ -1970,7 +2029,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/subscriptions-with-lemon-squeezy'
|
||||
'node/subscriptions-with-lemon-squeezy',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/subscriptions-with-lemon-squeezy">file</a>.',
|
||||
@@ -2026,7 +2086,8 @@ return [
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/payments-with-lemon-squeezy'
|
||||
'node/payments-with-lemon-squeezy',
|
||||
$allowList
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/payments-with-lemon-squeezy">file</a>.',
|
||||
@@ -2094,7 +2155,7 @@ return [
|
||||
'timeout' => 15,
|
||||
'useCases' => ['auth'],
|
||||
'runtimes' => [
|
||||
...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/sign_in_with_apple')
|
||||
...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/sign_in_with_apple', $allowList)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions, check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/dart/sign_in_with_apple">file</a>.',
|
||||
'vcsProvider' => 'github',
|
||||
|
||||
+286
-1132
File diff suppressed because it is too large
Load Diff
@@ -96,8 +96,8 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
|
||||
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||
|
||||
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
||||
|
||||
$oAuthProviders = Config::getParam('oAuthProviders');
|
||||
$className = $oAuthProviders[$provider]['class'];
|
||||
if (!\class_exists($className)) {
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
@@ -662,26 +662,26 @@ App::get('/v1/avatars/screenshots')
|
||||
],
|
||||
contentType: ContentType::IMAGE_PNG
|
||||
))
|
||||
->param('url', '', new URL(['http', 'https']), 'Website URL which you want to capture.')
|
||||
->param('headers', [], new Assoc(), 'HTTP headers to send with the browser request. Defaults to empty.', true)
|
||||
->param('viewportWidth', 1280, new Range(1, 1920), 'Browser viewport width. Pass an integer between 1 to 1920. Defaults to 1280.', true)
|
||||
->param('viewportHeight', 720, new Range(1, 1080), 'Browser viewport height. Pass an integer between 1 to 1080. Defaults to 720.', true)
|
||||
->param('scale', 1, new Range(0.1, 3, Range::TYPE_FLOAT), 'Browser scale factor. Pass a number between 0.1 to 3. Defaults to 1.', true)
|
||||
->param('theme', 'light', new WhiteList(['light', 'dark']), 'Browser theme. Pass "light" or "dark". Defaults to "light".', true)
|
||||
->param('userAgent', '', new Text(512), 'Custom user agent string. Defaults to browser default.', true)
|
||||
->param('fullpage', false, new Boolean(true), 'Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.', true)
|
||||
->param('locale', '', new Text(10), 'Browser locale (e.g., "en-US", "fr-FR"). Defaults to browser default.', true)
|
||||
->param('timezone', '', new WhiteList(timezone_identifiers_list()), 'IANA timezone identifier (e.g., "America/New_York", "Europe/London"). Defaults to browser default.', true)
|
||||
->param('latitude', 0, new Range(-90, 90, Range::TYPE_FLOAT), 'Geolocation latitude. Pass a number between -90 to 90. Defaults to 0.', true)
|
||||
->param('longitude', 0, new Range(-180, 180, Range::TYPE_FLOAT), 'Geolocation longitude. Pass a number between -180 to 180. Defaults to 0.', true)
|
||||
->param('accuracy', 0, new Range(0, 100000, Range::TYPE_FLOAT), 'Geolocation accuracy in meters. Pass a number between 0 to 100000. Defaults to 0.', true)
|
||||
->param('touch', false, new Boolean(true), 'Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.', true)
|
||||
->param('permissions', [], new ArrayList(new WhiteList(['geolocation', 'camera', 'microphone', 'notifications', 'midi', 'push', 'clipboard-read', 'clipboard-write', 'payment-handler', 'usb', 'bluetooth', 'accelerometer', 'gyroscope', 'magnetometer', 'ambient-light-sensor', 'background-sync', 'persistent-storage', 'screen-wake-lock', 'web-share', 'xr-spatial-tracking'])), 'Browser permissions to grant. Pass an array of permission names like ["geolocation", "camera", "microphone"]. Defaults to empty.', true)
|
||||
->param('sleep', 0, new Range(0, 10), 'Wait time in seconds before taking the screenshot. Pass an integer between 0 to 10. Defaults to 0.', true)
|
||||
->param('width', 0, new Range(0, 2000), 'Output image width. Pass 0 to use original width, or an integer between 1 to 2000. Defaults to 0 (original width).', true)
|
||||
->param('height', 0, new Range(0, 2000), 'Output image height. Pass 0 to use original height, or an integer between 1 to 2000. Defaults to 0 (original height).', true)
|
||||
->param('quality', -1, new Range(-1, 100), 'Screenshot quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true)
|
||||
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
|
||||
->param('url', '', new URL(['http', 'https']), 'Website URL which you want to capture.', example: 'https://example.com')
|
||||
->param('headers', [], new Assoc(), 'HTTP headers to send with the browser request. Defaults to empty.', true, example: '{"Authorization":"Bearer token123","X-Custom-Header":"value"}')
|
||||
->param('viewportWidth', 1280, new Range(1, 1920), 'Browser viewport width. Pass an integer between 1 to 1920. Defaults to 1280.', true, example: '1920')
|
||||
->param('viewportHeight', 720, new Range(1, 1080), 'Browser viewport height. Pass an integer between 1 to 1080. Defaults to 720.', true, example: '1080')
|
||||
->param('scale', 1, new Range(0.1, 3, Range::TYPE_FLOAT), 'Browser scale factor. Pass a number between 0.1 to 3. Defaults to 1.', true, example: '2')
|
||||
->param('theme', 'light', new WhiteList(['light', 'dark']), 'Browser theme. Pass "light" or "dark". Defaults to "light".', true, example: 'dark')
|
||||
->param('userAgent', '', new Text(512), 'Custom user agent string. Defaults to browser default.', true, example: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15')
|
||||
->param('fullpage', false, new Boolean(true), 'Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.', true, example: 'true')
|
||||
->param('locale', '', new Text(10), 'Browser locale (e.g., "en-US", "fr-FR"). Defaults to browser default.', true, example: 'en-US')
|
||||
->param('timezone', '', new WhiteList(timezone_identifiers_list()), 'IANA timezone identifier (e.g., "America/New_York", "Europe/London"). Defaults to browser default.', true, example: 'america/new_york')
|
||||
->param('latitude', 0, new Range(-90, 90, Range::TYPE_FLOAT), 'Geolocation latitude. Pass a number between -90 to 90. Defaults to 0.', true, example: '37.7749')
|
||||
->param('longitude', 0, new Range(-180, 180, Range::TYPE_FLOAT), 'Geolocation longitude. Pass a number between -180 to 180. Defaults to 0.', true, example: '-122.4194')
|
||||
->param('accuracy', 0, new Range(0, 100000, Range::TYPE_FLOAT), 'Geolocation accuracy in meters. Pass a number between 0 to 100000. Defaults to 0.', true, example: '100')
|
||||
->param('touch', false, new Boolean(true), 'Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.', true, example: 'true')
|
||||
->param('permissions', [], new ArrayList(new WhiteList(['geolocation', 'camera', 'microphone', 'notifications', 'midi', 'push', 'clipboard-read', 'clipboard-write', 'payment-handler', 'usb', 'bluetooth', 'accelerometer', 'gyroscope', 'magnetometer', 'ambient-light-sensor', 'background-sync', 'persistent-storage', 'screen-wake-lock', 'web-share', 'xr-spatial-tracking'])), 'Browser permissions to grant. Pass an array of permission names like ["geolocation", "camera", "microphone"]. Defaults to empty.', true, example: '["geolocation","notifications"]')
|
||||
->param('sleep', 0, new Range(0, 10), 'Wait time in seconds before taking the screenshot. Pass an integer between 0 to 10. Defaults to 0.', true, example: '3')
|
||||
->param('width', 0, new Range(0, 2000), 'Output image width. Pass 0 to use original width, or an integer between 1 to 2000. Defaults to 0 (original width).', true, example: '800')
|
||||
->param('height', 0, new Range(0, 2000), 'Output image height. Pass 0 to use original height, or an integer between 1 to 2000. Defaults to 0 (original height).', true, example: '600')
|
||||
->param('quality', -1, new Range(-1, 100), 'Screenshot quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true, example: '85')
|
||||
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true, example: 'jpeg')
|
||||
->inject('response')
|
||||
->inject('queueForStatsUsage')
|
||||
->action(function (string $url, array $headers, int $viewportWidth, int $viewportHeight, float $scale, string $theme, string $userAgent, bool $fullpage, string $locale, string $timezone, float $latitude, float $longitude, float $accuracy, bool $touch, array $permissions, int $sleep, int $width, int $height, int $quality, string $output, Response $response, StatsUsage $queueForStatsUsage) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\GraphQL\Promises\Adapter;
|
||||
@@ -9,6 +8,7 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\MethodType;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use GraphQL\Error\DebugFlag;
|
||||
@@ -32,7 +32,7 @@ App::init()
|
||||
if (
|
||||
array_key_exists('graphql', $project->getAttribute('apis', []))
|
||||
&& !$project->getAttribute('apis', [])['graphql']
|
||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
||||
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
|
||||
) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\MockNumber;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Mail;
|
||||
@@ -119,7 +118,7 @@ App::post('/v1/projects')
|
||||
'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT,
|
||||
'passwordHistory' => 0,
|
||||
'passwordDictionary' => false,
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'personalDataCheck' => false,
|
||||
'mockNumbers' => [],
|
||||
'sessionAlerts' => false,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\ClamAV\Network;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -13,6 +12,7 @@ use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\MethodType;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Buckets;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Files;
|
||||
@@ -86,10 +86,11 @@ App::post('/v1/storage/buckets')
|
||||
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD], true), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
|
||||
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
|
||||
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
|
||||
->param('transformations', true, new Boolean(true), 'Are image transformations enabled?', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, ?string $compression, ?bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, ?string $compression, ?bool $encryption, bool $antivirus, bool $transformations, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId;
|
||||
|
||||
@@ -142,6 +143,7 @@ App::post('/v1/storage/buckets')
|
||||
'compression' => $compression,
|
||||
'encryption' => $encryption,
|
||||
'antivirus' => $antivirus,
|
||||
'transformations' => $transformations,
|
||||
'search' => implode(' ', [$bucketId, $name]),
|
||||
]));
|
||||
|
||||
@@ -299,10 +301,11 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD], true), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
|
||||
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
|
||||
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
|
||||
->param('transformations', true, new Boolean(true), 'Are image transformations enabled?', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, ?string $compression, ?bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, ?string $compression, ?bool $encryption, bool $antivirus, bool $transformations, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
||||
|
||||
if ($bucket->isEmpty()) {
|
||||
@@ -316,6 +319,7 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
$encryption ??= $bucket->getAttribute('encryption', true);
|
||||
$antivirus ??= $bucket->getAttribute('antivirus', true);
|
||||
$compression ??= $bucket->getAttribute('compression', Compression::NONE);
|
||||
$transformations ??= $bucket->getAttribute('transformations', true);
|
||||
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions);
|
||||
@@ -329,7 +333,8 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
->setAttribute('enabled', $enabled)
|
||||
->setAttribute('encryption', $encryption)
|
||||
->setAttribute('compression', $compression)
|
||||
->setAttribute('antivirus', $antivirus));
|
||||
->setAttribute('antivirus', $antivirus)
|
||||
->setAttribute('transformations', $transformations));
|
||||
|
||||
$dbForProject->updateCollection('bucket_' . $bucket->getSequence(), $permissions, $fileSecurity);
|
||||
|
||||
@@ -432,8 +437,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -465,7 +470,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
$roles = Authorization::getRoles();
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
if (!User::isApp($roles) && !User::isPrivileged($roles)) {
|
||||
foreach (Database::PERMISSIONS as $type) {
|
||||
foreach ($permissions as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
@@ -676,7 +681,13 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
'metadata' => $metadata,
|
||||
]);
|
||||
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc);
|
||||
try {
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc);
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::STORAGE_FILE_ALREADY_EXISTS);
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
} else {
|
||||
$file = $file
|
||||
->setAttribute('$permissions', $permissions)
|
||||
@@ -726,6 +737,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
|
||||
try {
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc);
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::STORAGE_FILE_ALREADY_EXISTS);
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
@@ -794,8 +807,8 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
->action(function (string $bucketId, array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject, string $mode) {
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -895,8 +908,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) {
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -977,13 +990,17 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$bucket->getAttribute('transformations', true) && !$isAPIKey && !$isPrivilegedUser) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_TRANSFORMATIONS_DISABLED);
|
||||
}
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
@@ -1118,7 +1135,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
|
||||
|
||||
//Do not update transformedAt if it's a console user
|
||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
if (!User::isPrivileged(Authorization::getRoles())) {
|
||||
$transformedAt = $file->getAttribute('transformedAt', '');
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
|
||||
$file->setAttribute('transformedAt', DateTime::now());
|
||||
@@ -1169,8 +1186,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -1330,8 +1347,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -1500,10 +1517,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
}
|
||||
|
||||
$isInternal = $decoded['internal'] ?? false;
|
||||
$disposition = $decoded['disposition'] ?? 'inline';
|
||||
$dbForProject = $isInternal ? $dbForPlatform : $dbForProject;
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
@@ -1556,7 +1574,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
->setContentType($contentType)
|
||||
->addHeader('Content-Security-Policy', 'script-src none;')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Content-Disposition', $disposition . '; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage());
|
||||
|
||||
@@ -1659,8 +1677,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -1689,7 +1707,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
$roles = Authorization::getRoles();
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) {
|
||||
if (!User::isApp($roles) && !User::isPrivileged($roles) && !\is_null($permissions)) {
|
||||
foreach (Database::PERMISSIONS as $type) {
|
||||
foreach ($permissions as $permission) {
|
||||
$permission = Permission::parse($permission);
|
||||
@@ -1773,8 +1791,8 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) {
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAPIKey = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\MFA\Type\TOTP;
|
||||
use Appwrite\Auth\Validator\Phone;
|
||||
use Appwrite\Detector\Detector;
|
||||
@@ -18,6 +17,7 @@ use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Teams;
|
||||
@@ -28,6 +28,9 @@ use MaxMind\Db\Reader;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Proofs\Password;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -87,8 +90,8 @@ App::post('/v1/teams')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
$isAppUser = User::isApp(Authorization::getRoles());
|
||||
|
||||
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
|
||||
|
||||
@@ -176,6 +179,7 @@ App::get('/v1/teams')
|
||||
->inject('dbForProject')
|
||||
->action(function (array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject) {
|
||||
|
||||
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
@@ -474,10 +478,9 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
|
||||
->param('roles', [], function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
;
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
$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);
|
||||
}
|
||||
@@ -496,9 +499,11 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->inject('timelimit')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('plan')
|
||||
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
->inject('proofForPassword')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword, Token $proofForToken) {
|
||||
$isAppUser = User::isApp(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
$url = htmlentities($url);
|
||||
if (empty($url)) {
|
||||
@@ -568,6 +573,8 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
|
||||
try {
|
||||
$userId = ID::unique();
|
||||
$hash = $proofForPassword->hash($proofForPassword->generate());
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
@@ -588,9 +595,9 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
// TODO: Set password empty?
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'password' => $hash,
|
||||
'hash' => $proofForPassword->getHash()->getName(),
|
||||
'hashOptions' => $proofForPassword->getHash()->getOptions(),
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
@@ -630,7 +637,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
Query::equal('teamInternalId', [$team->getSequence()]),
|
||||
]);
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
$secret = $proofForToken->generate();
|
||||
if ($membership->isEmpty()) {
|
||||
$membershipId = ID::unique();
|
||||
$membership = new Document([
|
||||
@@ -650,7 +657,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
'invited' => DateTime::now(),
|
||||
'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null,
|
||||
'confirm' => ($isPrivilegedUser || $isAppUser),
|
||||
'secret' => Auth::hash($secret),
|
||||
'secret' => $proofForToken->hash($secret),
|
||||
'search' => implode(' ', [$membershipId, $invitee->getId()])
|
||||
]);
|
||||
|
||||
@@ -661,9 +668,8 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
if ($isPrivilegedUser || $isAppUser) {
|
||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||
}
|
||||
|
||||
} elseif ($membership->getAttribute('confirm') === false) {
|
||||
$membership->setAttribute('secret', Auth::hash($secret));
|
||||
$membership->setAttribute('secret', $proofForToken->hash($secret));
|
||||
$membership->setAttribute('invited', DateTime::now());
|
||||
|
||||
if ($isPrivilegedUser || $isAppUser) {
|
||||
@@ -766,7 +772,6 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->setName($invitee->getAttribute('name', ''))
|
||||
->setVariables($emailVariables)
|
||||
->trigger();
|
||||
|
||||
} elseif (!empty($phone)) {
|
||||
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
@@ -930,8 +935,8 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
];
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
$isPrivilegedUser = User::isPrivileged($roles);
|
||||
$isAppUser = User::isApp($roles);
|
||||
|
||||
$membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) {
|
||||
return $privacy || $isPrivilegedUser || $isAppUser;
|
||||
@@ -1021,8 +1026,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
];
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
$isPrivilegedUser = User::isPrivileged($roles);
|
||||
$isAppUser = User::isApp($roles);
|
||||
|
||||
$membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) {
|
||||
return $privacy || $isPrivilegedUser || $isAppUser;
|
||||
@@ -1087,8 +1092,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->param('roles', [], function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
$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);
|
||||
}
|
||||
@@ -1117,8 +1122,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
$isAppUser = User::isApp(Authorization::getRoles());
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
@@ -1203,7 +1208,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
->inject('project')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents) {
|
||||
->inject('store')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
@@ -1222,7 +1229,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
|
||||
}
|
||||
|
||||
if (Auth::hash($secret) !== $membership->getAttribute('secret')) {
|
||||
if (!$proofForToken->verify($secret, $membership->getAttribute('secret'))) {
|
||||
throw new Exception(Exception::TEAM_INVALID_SECRET);
|
||||
}
|
||||
|
||||
@@ -1256,9 +1263,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::addSeconds(new \DateTime(), $authDuration);
|
||||
$secret = Auth::tokenGenerator();
|
||||
$secret = $proofForToken->generate();
|
||||
$session = new Document(array_merge([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
@@ -1268,9 +1275,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => $user->getAttribute('email'),
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
'factors' => ['email'],
|
||||
@@ -1282,14 +1289,19 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
|
||||
Authorization::setRole(Role::user($userId)->toString());
|
||||
|
||||
$encoded = $store
|
||||
->setProperty('id', $user->getId())
|
||||
->setProperty('secret', $secret)
|
||||
->encode();
|
||||
|
||||
if (!Config::getParam('domainVerification')) {
|
||||
$response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]));
|
||||
$response->addHeader('X-Fallback-Cookies', \json_encode([$store->getKey() => $encoded]));
|
||||
}
|
||||
|
||||
$response
|
||||
->addCookie(
|
||||
name: Auth::$cookieName . '_legacy',
|
||||
value: Auth::encodeSession($user->getId(), $secret),
|
||||
name: $store->getKey() . '_legacy',
|
||||
value: $encoded,
|
||||
expire: (new \DateTime($expire))->getTimestamp(),
|
||||
path: '/',
|
||||
domain: Config::getParam('cookieDomain'),
|
||||
@@ -1297,8 +1309,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
httponly: true
|
||||
)
|
||||
->addCookie(
|
||||
name: Auth::$cookieName,
|
||||
value: Auth::encodeSession($user->getId(), $secret),
|
||||
name: $store->getKey(),
|
||||
value: $encoded,
|
||||
expire: (new \DateTime($expire))->getTimestamp(),
|
||||
path: '/',
|
||||
domain: Config::getParam('cookieDomain'),
|
||||
|
||||
+100
-76
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\MFA\Type;
|
||||
use Appwrite\Auth\MFA\Type\TOTP;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
@@ -32,6 +31,18 @@ use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Hash;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
use Utopia\Auth\Hashes\Bcrypt;
|
||||
use Utopia\Auth\Hashes\MD5;
|
||||
use Utopia\Auth\Hashes\PHPass;
|
||||
use Utopia\Auth\Hashes\Plaintext;
|
||||
use Utopia\Auth\Hashes\Scrypt;
|
||||
use Utopia\Auth\Hashes\ScryptModified;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Password as ProofsPassword;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -62,10 +73,9 @@ use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
/** TODO: Remove function when we move to using utopia/platform */
|
||||
function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Hooks $hooks): Document
|
||||
function createUser(Hash $hash, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Hooks $hooks): Document
|
||||
{
|
||||
$plaintextPassword = $password;
|
||||
$hashOptionsObject = (\is_string($hashOptions)) ? \json_decode($hashOptions, true) : $hashOptions; // Cast to JSON array
|
||||
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||
|
||||
if (!empty($email)) {
|
||||
@@ -104,8 +114,24 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
$hashedPassword = null;
|
||||
|
||||
$isHashed = !$hash instanceof Plaintext;
|
||||
|
||||
$defaultHash = new ProofsPassword();
|
||||
if (!empty($password)) {
|
||||
if (!$isHashed) { // Password was never hashed, hash it with the default hash
|
||||
$hashedPassword = $defaultHash->hash($password);
|
||||
$hash = $defaultHash->getHash();
|
||||
} else {
|
||||
$hashedPassword = $password;
|
||||
}
|
||||
} else {
|
||||
// when password is not provided, plaintext was set as the default hash causing the issue
|
||||
$hash = $defaultHash->getHash();
|
||||
$isHashed = !$hash instanceof Plaintext;
|
||||
}
|
||||
|
||||
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
|
||||
$user = new Document([
|
||||
'$id' => $userId,
|
||||
'$permissions' => [
|
||||
@@ -119,11 +145,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
'phoneVerification' => false,
|
||||
'status' => true,
|
||||
'labels' => [],
|
||||
'password' => $password,
|
||||
'passwordHistory' => is_null($password) || $passwordHistory === 0 ? [] : [$password],
|
||||
'passwordUpdate' => (!empty($password)) ? DateTime::now() : null,
|
||||
'hash' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO : $hash,
|
||||
'hashOptions' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO_OPTIONS : $hashOptionsObject + ['type' => $hash],
|
||||
'password' => $hashedPassword,
|
||||
'passwordHistory' => is_null($hashedPassword) || $passwordHistory === 0 ? [] : [$hashedPassword],
|
||||
'passwordUpdate' => (!empty($hashedPassword)) ? DateTime::now() : null,
|
||||
'hash' => $hash->getName(),
|
||||
'hashOptions' => $hash->getOptions(),
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
@@ -139,7 +165,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
'emailIsFree' => $emailCanonical?->isFree(),
|
||||
]);
|
||||
|
||||
if ($hash === 'plaintext') {
|
||||
if (!$isHashed && !empty($password)) {
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $plaintextPassword, &$user, true]);
|
||||
}
|
||||
|
||||
@@ -230,7 +256,9 @@ App::post('/v1/users')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $hooks);
|
||||
$plaintext = new Plaintext();
|
||||
|
||||
$user = createUser($plaintext, $userId, $email, $password, $phone, $name, $project, $dbForProject, $hooks);
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
@@ -264,7 +292,10 @@ App::post('/v1/users/bcrypt')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('bcrypt', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$bcrypt = new Bcrypt();
|
||||
$bcrypt->setCost(8); // Default cost
|
||||
|
||||
$user = createUser($bcrypt, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -299,7 +330,9 @@ App::post('/v1/users/md5')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('md5', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$md5 = new MD5();
|
||||
|
||||
$user = createUser($md5, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -334,7 +367,9 @@ App::post('/v1/users/argon2')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('argon2', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$argon2 = new Argon2();
|
||||
|
||||
$user = createUser($argon2, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -370,13 +405,12 @@ App::post('/v1/users/sha')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$options = '{}';
|
||||
|
||||
$sha = new Sha();
|
||||
if (!empty($passwordVersion)) {
|
||||
$options = '{"version":"' . $passwordVersion . '"}';
|
||||
$sha->setVersion($passwordVersion);
|
||||
}
|
||||
|
||||
$user = createUser('sha', $options, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$user = createUser($sha, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -411,7 +445,9 @@ App::post('/v1/users/phpass')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('phpass', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$phpass = new PHPass();
|
||||
|
||||
$user = createUser($phpass, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -451,15 +487,15 @@ App::post('/v1/users/scrypt')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $passwordSalt, int $passwordCpu, int $passwordMemory, int $passwordParallel, int $passwordLength, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$options = [
|
||||
'salt' => $passwordSalt,
|
||||
'costCpu' => $passwordCpu,
|
||||
'costMemory' => $passwordMemory,
|
||||
'costParallel' => $passwordParallel,
|
||||
'length' => $passwordLength
|
||||
];
|
||||
$scrypt = new Scrypt();
|
||||
$scrypt
|
||||
->setSalt($passwordSalt)
|
||||
->setCpuCost($passwordCpu)
|
||||
->setMemoryCost($passwordMemory)
|
||||
->setParallelCost($passwordParallel)
|
||||
->setLength($passwordLength);
|
||||
|
||||
$user = createUser('scrypt', \json_encode($options), $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$user = createUser($scrypt, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -497,7 +533,13 @@ App::post('/v1/users/scrypt-modified')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $passwordSalt, string $passwordSaltSeparator, string $passwordSignerKey, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$scryptModified = new ScryptModified();
|
||||
$scryptModified
|
||||
->setSalt($passwordSalt)
|
||||
->setSaltSeparator($passwordSaltSeparator)
|
||||
->setSignerKey($passwordSignerKey);
|
||||
|
||||
$user = createUser($scryptModified, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -809,16 +851,12 @@ App::get('/v1/users/:userId/sessions')
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
||||
foreach ($sessions as $key => $session) {
|
||||
/** @var Document $session */
|
||||
|
||||
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$session->setAttribute('countryName', $countryName);
|
||||
$session->setAttribute('current', false);
|
||||
|
||||
$sessions[$key] = $session;
|
||||
}
|
||||
|
||||
@@ -858,28 +896,22 @@ App::get('/v1/users/:userId/memberships')
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
// Set internal queries
|
||||
$queries[] = Query::equal('userInternalId', [$user->getSequence()]);
|
||||
|
||||
$memberships = array_map(function ($membership) use ($dbForProject, $user) {
|
||||
$team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId'));
|
||||
|
||||
$membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email'));
|
||||
|
||||
return $membership;
|
||||
}, $dbForProject->find('memberships', $queries));
|
||||
|
||||
@@ -920,35 +952,26 @@ App::get('/v1/users/:userId/logs')
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
// Temp fix for logs
|
||||
$queries[] = Query::or([
|
||||
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
|
||||
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
|
||||
]);
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
|
||||
$logs = $audit->getLogsByUser($user->getSequence(), $queries);
|
||||
|
||||
$output = [];
|
||||
|
||||
foreach ($logs as $i => &$log) {
|
||||
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
|
||||
|
||||
$detector = new Detector($log['userAgent']);
|
||||
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
|
||||
$os = $detector->getOS();
|
||||
$client = $detector->getClient();
|
||||
$device = $detector->getDevice();
|
||||
|
||||
$output[$i] = new Document([
|
||||
'event' => $log['event'],
|
||||
'userId' => ID::custom($log['data']['userId']),
|
||||
@@ -969,9 +992,7 @@ App::get('/v1/users/:userId/logs')
|
||||
'deviceBrand' => $device['deviceBrand'],
|
||||
'deviceModel' => $device['deviceModel']
|
||||
]);
|
||||
|
||||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
@@ -1015,15 +1036,12 @@ App::get('/v1/users/:userId/targets')
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('userId', [$userId]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
@@ -1031,20 +1049,16 @@ App::get('/v1/users/:userId/targets')
|
||||
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());
|
||||
}
|
||||
|
||||
$targetId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('targets', $targetId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Target '{$targetId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
try {
|
||||
@@ -1092,7 +1106,6 @@ App::get('/v1/users/identities')
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
@@ -1102,19 +1115,15 @@ App::get('/v1/users/identities')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$identityId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('identities', $identityId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "User '{$identityId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
@@ -1354,12 +1363,17 @@ App::patch('/v1/users/:userId/password')
|
||||
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]);
|
||||
|
||||
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||
// Create Argon2 hasher with default settings
|
||||
$hasher = new Argon2();
|
||||
|
||||
$newPassword = $hasher->hash($password);
|
||||
|
||||
$hash = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||
$history = $user->getAttribute('passwordHistory', []);
|
||||
|
||||
if ($historyLimit > 0) {
|
||||
$validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||
$validator = new PasswordHistory($history, $hash);
|
||||
if (!$validator->isValid($password)) {
|
||||
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED);
|
||||
}
|
||||
@@ -1372,8 +1386,8 @@ App::patch('/v1/users/:userId/password')
|
||||
->setAttribute('password', $newPassword)
|
||||
->setAttribute('passwordHistory', $history)
|
||||
->setAttribute('passwordUpdate', DateTime::now())
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
|
||||
->setAttribute('hash', $hasher->getName())
|
||||
->setAttribute('hashOptions', $hasher->getOptions());
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
@@ -2197,17 +2211,19 @@ App::post('/v1/users/:userId/sessions')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) {
|
||||
->inject('store')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
|
||||
$secret = $proofForToken->generate();
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
$session = new Document(array_merge(
|
||||
@@ -2215,8 +2231,8 @@ App::post('/v1/users/:userId/sessions')
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'provider' => Auth::SESSION_PROVIDER_SERVER,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'provider' => SESSION_PROVIDER_SERVER,
|
||||
'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'factors' => ['server'],
|
||||
'ip' => $request->getIP(),
|
||||
@@ -2240,8 +2256,13 @@ App::post('/v1/users/:userId/sessions')
|
||||
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
$encoded = $store
|
||||
->setProperty('id', $user->getId())
|
||||
->setProperty('secret', $secret)
|
||||
->encode();
|
||||
|
||||
$session
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
|
||||
->setAttribute('secret', $encoded)
|
||||
->setAttribute('countryName', $countryName);
|
||||
|
||||
$queueForEvents
|
||||
@@ -2276,7 +2297,7 @@ App::post('/v1/users/:userId/tokens')
|
||||
))
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('length', 6, new Range(4, 128), 'Token length in characters. The default length is 6 characters', true)
|
||||
->param('expire', Auth::TOKEN_EXPIRATION_GENERIC, new Range(60, Auth::TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
|
||||
->param('expire', TOKEN_EXPIRATION_GENERIC, new Range(60, TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -2288,15 +2309,17 @@ App::post('/v1/users/:userId/tokens')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator($length);
|
||||
$proofForToken = new Token($length);
|
||||
$proofForToken->setHash(new Sha());
|
||||
$secret = $proofForToken->generate();
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $expire));
|
||||
|
||||
$token = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'type' => Auth::TOKEN_TYPE_GENERIC,
|
||||
'secret' => Auth::hash($secret),
|
||||
'type' => TOKEN_TYPE_GENERIC,
|
||||
'secret' => $proofForToken->hash($secret),
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'ip' => $request->getIP()
|
||||
@@ -2609,7 +2632,8 @@ App::post('/v1/users/:userId/jwts')
|
||||
$session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document();
|
||||
} else {
|
||||
// Find by ID
|
||||
foreach ($sessions as $loopSession) { /** @var Utopia\Database\Document $loopSession */
|
||||
foreach ($sessions as $loopSession) {
|
||||
/** @var Utopia\Database\Document $loopSession */
|
||||
if ($loopSession->getId() == $sessionId) {
|
||||
$session = $loopSession;
|
||||
break;
|
||||
|
||||
+101
-6
@@ -14,9 +14,12 @@ use Appwrite\Utopia\Database\Validator\Queries\Installations;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Vcs\Comment;
|
||||
use Swoole\Coroutine\WaitGroup;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Adapters\Dotenv as ConfigDotenv;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Config\Exceptions\Parse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
@@ -28,7 +31,10 @@ use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Detector\Detection\Framework\Analog;
|
||||
use Utopia\Detector\Detection\Framework\Angular;
|
||||
use Utopia\Detector\Detection\Framework\Astro;
|
||||
@@ -963,6 +969,46 @@ App::post('/v1/vcs/github/installations/:installationId/detections')
|
||||
throw new Exception(Exception::FUNCTION_RUNTIME_NOT_DETECTED);
|
||||
}
|
||||
}
|
||||
|
||||
$wg = new WaitGroup();
|
||||
$envs = [];
|
||||
foreach ($files as $file) {
|
||||
if (!(\str_starts_with($file, '.env'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wg->add();
|
||||
go(function () use ($github, $owner, $repositoryName, $providerRootDirectory, $file, $wg, &$envs) {
|
||||
try {
|
||||
$contentResponse = $github->getRepositoryContent($owner, $repositoryName, \rtrim($providerRootDirectory, '/') . '/' . $file);
|
||||
$envFile = $contentResponse['content'] ?? '';
|
||||
|
||||
$configAdapter = new ConfigDotenv();
|
||||
try {
|
||||
$envObject = $configAdapter->parse($envFile);
|
||||
foreach ($envObject as $envName => $envValue) {
|
||||
$envs[$envName] = $envValue;
|
||||
}
|
||||
} catch (Parse $err) {
|
||||
// Silence error, so rest of endpoint can return
|
||||
}
|
||||
} finally {
|
||||
$wg->done();
|
||||
}
|
||||
});
|
||||
}
|
||||
$wg->wait();
|
||||
|
||||
$variables = [];
|
||||
foreach ($envs as $key => $value) {
|
||||
$variables[] = [
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
$output->setAttribute('variables', $variables);
|
||||
|
||||
$response->dynamic($output, $type === 'framework' ? Response::MODEL_DETECTION_FRAMEWORK : Response::MODEL_DETECTION_RUNTIME);
|
||||
});
|
||||
|
||||
@@ -990,10 +1036,11 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $installationId, string $type, string $search, GitHub $github, Response $response, Database $dbForPlatform) {
|
||||
->action(function (string $installationId, string $type, string $search, array $queries, GitHub $github, Response $response, Database $dbForPlatform) {
|
||||
if (empty($search)) {
|
||||
$search = "";
|
||||
}
|
||||
@@ -1009,11 +1056,20 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$page = 1;
|
||||
$perPage = 4;
|
||||
$queries = Query::parseQueries($queries);
|
||||
$limitQuery = current(array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_LIMIT));
|
||||
$offsetQuery = current(array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_OFFSET));
|
||||
|
||||
$limit = !empty($limitQuery) ? $limitQuery->getValue() : 4;
|
||||
$offset = !empty($offsetQuery) ? $offsetQuery->getValue() : 0;
|
||||
|
||||
if ($offset % $limit !== 0) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'offset must be a multiple of the limit');
|
||||
}
|
||||
|
||||
$page = ($offset / $limit) + 1;
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
$repos = $github->searchRepositories($owner, $page, $perPage, $search);
|
||||
['items' => $repos, 'total' => $total] = $github->searchRepositories($owner, $page, $limit, $search);
|
||||
|
||||
$repos = \array_map(function ($repo) use ($installation) {
|
||||
$repo['id'] = \strval($repo['id'] ?? '');
|
||||
@@ -1137,6 +1193,44 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
$repo['runtime'] = $runtimeWithVersion ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
$wg = new WaitGroup();
|
||||
$envs = [];
|
||||
foreach ($files as $file) {
|
||||
if (!(\str_starts_with($file, '.env'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wg->add();
|
||||
go(function () use ($github, $repo, $file, $wg, &$envs) {
|
||||
try {
|
||||
$contentResponse = $github->getRepositoryContent($repo['organization'], $repo['name'], $file);
|
||||
$envFile = $contentResponse['content'] ?? '';
|
||||
|
||||
$configAdapter = new ConfigDotenv();
|
||||
try {
|
||||
$envObject = $configAdapter->parse($envFile);
|
||||
foreach ($envObject as $envName => $envValue) {
|
||||
$envs[$envName] = $envValue;
|
||||
}
|
||||
} catch (Parse) {
|
||||
// Silence error, so rest of endpoint can return
|
||||
}
|
||||
} finally {
|
||||
$wg->done();
|
||||
}
|
||||
});
|
||||
}
|
||||
$wg->wait();
|
||||
|
||||
$repo['variables'] = [];
|
||||
foreach ($envs as $key => $value) {
|
||||
$repo['variables'][] = [
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
return $repo;
|
||||
};
|
||||
}, $repos));
|
||||
@@ -1147,7 +1241,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
|
||||
$response->dynamic(new Document([
|
||||
$type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos,
|
||||
'total' => \count($repos),
|
||||
'total' => $total,
|
||||
]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST);
|
||||
});
|
||||
|
||||
@@ -1702,7 +1796,8 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$repository = Authorization::skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [
|
||||
$repository = Authorization::skip(fn () => $dbForPlatform->findOne('repositories', [
|
||||
Query::equal('$id', [$repositoryId]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()])
|
||||
]));
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ require_once __DIR__ . '/../init.php';
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Key;
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -17,6 +16,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Transformation\Adapter\Preview;
|
||||
use Appwrite\Transformation\Transformation;
|
||||
use Appwrite\Utopia\Database\Documents\User as DBUser;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
|
||||
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
|
||||
@@ -223,7 +223,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
*/
|
||||
$requirePreview = \is_null($apiKey) || !$apiKey->isPreviewAuthDisabled();
|
||||
if ($isPreview && $requirePreview) {
|
||||
$cookie = $request->getCookie(Auth::$cookieNamePreview, '');
|
||||
$cookie = $request->getCookie(COOKIE_NAME_PREVIEW, '');
|
||||
$authorized = false;
|
||||
|
||||
// Security checks to mark authorized true
|
||||
@@ -1260,7 +1260,7 @@ App::error()
|
||||
* If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
|
||||
*/
|
||||
if (!$publish && $project->getId() !== 'console') {
|
||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
if (!DBUser::isPrivileged(Authorization::getRoles())) {
|
||||
$fileSize = 0;
|
||||
$file = $request->getFiles('file');
|
||||
if (!empty($file)) {
|
||||
@@ -1617,7 +1617,7 @@ App::get('/_appwrite/authorize')
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieNamePreview, $jwt, (new \DateTime($expire))->getTimestamp(), '/', $host, ('https' === $protocol), true, null)
|
||||
->addCookie(COOKIE_NAME_PREVIEW, $jwt, (new \DateTime($expire))->getTimestamp(), '/', $host, ('https' === $protocol), true, null)
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($protocol . '://' . $host . $path);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Key;
|
||||
use Appwrite\Auth\MFA\Type\TOTP;
|
||||
use Appwrite\Event\Audit;
|
||||
@@ -16,13 +15,13 @@ use Appwrite\Event\Webhook;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\SDK\Method;
|
||||
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\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -34,7 +33,7 @@ use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
|
||||
$parseLabel = function (string $label, array $responsePayload, array $requestParams, User $user) {
|
||||
preg_match_all('/{(.*?)}/', $label, $matches);
|
||||
foreach ($matches[1] ?? [] as $pos => $match) {
|
||||
$find = $matches[0][$pos];
|
||||
@@ -232,44 +231,97 @@ App::init()
|
||||
->inject('mode')
|
||||
->inject('team')
|
||||
->inject('apiKey')
|
||||
->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) {
|
||||
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, User $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey) {
|
||||
$route = $utopia->getRoute();
|
||||
if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted' && str_starts_with($route->getPath(), '/v1/backups')) {
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Database Backups are available on Appwrite Cloud');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user authentication and session validation.
|
||||
*
|
||||
* This function follows a series of steps to determine the appropriate user session
|
||||
* based on cookies, headers, and JWT tokens.
|
||||
*
|
||||
* Process:
|
||||
*
|
||||
* Project & Role Validation:
|
||||
* 1. Check if the project is empty. If so, throw an exception.
|
||||
* 2. Get the roles configuration.
|
||||
* 3. Determine the role for the user based on the user document.
|
||||
* 4. Get the scopes for the role.
|
||||
*
|
||||
* API Key Authentication:
|
||||
* 5. If there is an API key:
|
||||
* - Verify no user session exists simultaneously
|
||||
* - Check if key is expired
|
||||
* - Set role and scopes from API key
|
||||
* - Handle special app role case
|
||||
* - For standard keys, update last accessed time
|
||||
*
|
||||
* User Activity:
|
||||
* 6. If the project is not the console and user is not admin:
|
||||
* - Update user's last activity timestamp
|
||||
*
|
||||
* Access Control:
|
||||
* 7. Get the method from the route
|
||||
* 8. Validate namespace permissions
|
||||
* 9. Validate scope permissions
|
||||
* 10. Check if user is blocked
|
||||
*
|
||||
* Security Checks:
|
||||
* 11. Verify password status (check if reset required)
|
||||
* 12. Validate MFA requirements:
|
||||
* - Check if MFA is enabled
|
||||
* - Verify email status
|
||||
* - Verify phone status
|
||||
* - Verify authenticator status
|
||||
* 13. Handle Multi-Factor Authentication:
|
||||
* - Check remaining required factors
|
||||
* - Validate factor completion
|
||||
* - Throw exception if factors incomplete
|
||||
*/
|
||||
|
||||
// Step 1: Check if project is empty
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Step 2: Get roles configuration
|
||||
$roles = Config::getParam('roles', []);
|
||||
|
||||
// Step 3: Determine role for user
|
||||
// TODO get scopes from the identity instead of the user roles config. The identity will containn the scopes the user authorized for the access token.
|
||||
|
||||
$role = $user->isEmpty()
|
||||
? Role::guests()->toString()
|
||||
: Role::users()->toString();
|
||||
|
||||
// Step 4: Get scopes for the role
|
||||
$scopes = $roles[$role]['scopes'];
|
||||
|
||||
// API Key authentication
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Set role and scopes from API key
|
||||
$role = $apiKey->getRole();
|
||||
$scopes = $apiKey->getScopes();
|
||||
|
||||
|
||||
if ($apiKey->getRole() === Auth::USER_ROLE_APPS) {
|
||||
// Handle special app role case
|
||||
if ($apiKey->getRole() === User::ROLE_APPS) {
|
||||
// Disable authorization checks for API keys
|
||||
Authorization::setDefaultStatus(false);
|
||||
|
||||
$user = new Document([
|
||||
$user = new User([
|
||||
'$id' => '',
|
||||
'status' => true,
|
||||
'type' => Auth::ACTIVITY_TYPE_APP,
|
||||
'type' => ACTIVITY_TYPE_APP,
|
||||
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
|
||||
'password' => '',
|
||||
'name' => $apiKey->getName(),
|
||||
@@ -278,6 +330,7 @@ App::init()
|
||||
$queueForAudits->setUser($user);
|
||||
}
|
||||
|
||||
// For standard keys, update last accessed time
|
||||
if ($apiKey->getType() === API_KEY_STANDARD) {
|
||||
$dbKey = $project->find(
|
||||
key: 'secret',
|
||||
@@ -343,11 +396,11 @@ App::init()
|
||||
$scopes = \array_unique($scopes);
|
||||
|
||||
Authorization::setRole($role);
|
||||
foreach (Auth::getRoles($user) as $authRole) {
|
||||
foreach ($user->getRoles() as $authRole) {
|
||||
Authorization::setRole($authRole);
|
||||
}
|
||||
|
||||
// Update project last activity
|
||||
// Step 6: Update project and user last activity
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$accessedAt = $project->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
||||
@@ -356,7 +409,6 @@ App::init()
|
||||
}
|
||||
}
|
||||
|
||||
// Update user last activity
|
||||
if (!empty($user->getId())) {
|
||||
$accessedAt = $user->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) {
|
||||
@@ -370,6 +422,7 @@ App::init()
|
||||
}
|
||||
}
|
||||
|
||||
// Steps 7-9: Access Control - Method, Namespace and Scope Validation
|
||||
/**
|
||||
* @var ?Method $method
|
||||
*/
|
||||
@@ -387,27 +440,29 @@ App::init()
|
||||
if (
|
||||
array_key_exists($namespace, $project->getAttribute('services', []))
|
||||
&& !$project->getAttribute('services', [])[$namespace]
|
||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
||||
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
|
||||
) {
|
||||
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
// Do now allow access if scope is not allowed
|
||||
// Step 9: Validate scope permissions
|
||||
$allowed = (array)$route->getLabel('scope', 'none');
|
||||
if (empty(\array_intersect($allowed, $scopes))) {
|
||||
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scopes (' . \json_encode($allowed) . ')');
|
||||
}
|
||||
|
||||
// Do not allow access to blocked accounts
|
||||
// Step 10: Check if user is blocked
|
||||
if (false === $user->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception(Exception::USER_BLOCKED);
|
||||
}
|
||||
|
||||
// Step 11: Verify password status
|
||||
if ($user->getAttribute('reset')) {
|
||||
throw new Exception(Exception::USER_PASSWORD_RESET_REQUIRED);
|
||||
}
|
||||
|
||||
// Step 12: Validate MFA requirements
|
||||
$mfaEnabled = $user->getAttribute('mfa', false);
|
||||
$hasVerifiedEmail = $user->getAttribute('emailVerification', false);
|
||||
$hasVerifiedPhone = $user->getAttribute('phoneVerification', false);
|
||||
@@ -415,6 +470,7 @@ App::init()
|
||||
$hasMoreFactors = $hasVerifiedEmail || $hasVerifiedPhone || $hasVerifiedAuthenticator;
|
||||
$minimumFactors = ($mfaEnabled && $hasMoreFactors) ? 2 : 1;
|
||||
|
||||
// Step 13: Handle Multi-Factor Authentication
|
||||
if (!in_array('mfa', $route->getGroups())) {
|
||||
if ($session && \count($session->getAttribute('factors', [])) < $minimumFactors) {
|
||||
throw new Exception(Exception::USER_MORE_FACTORS_REQUIRED);
|
||||
@@ -454,7 +510,7 @@ App::init()
|
||||
if (
|
||||
array_key_exists('rest', $project->getAttribute('apis', []))
|
||||
&& !$project->getAttribute('apis', [])['rest']
|
||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
||||
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
|
||||
) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
||||
}
|
||||
@@ -485,8 +541,8 @@ App::init()
|
||||
$closestLimit = null;
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
$isPrivilegedUser = User::isPrivileged($roles);
|
||||
$isAppUser = User::isApp($roles);
|
||||
|
||||
foreach ($timeLimitArray as $timeLimit) {
|
||||
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||
@@ -541,7 +597,7 @@ App::init()
|
||||
if (!$user->isEmpty()) {
|
||||
$userClone = clone $user;
|
||||
// $user doesn't support `type` and can cause unintended effects.
|
||||
$userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
$userClone->setAttribute('type', ACTIVITY_TYPE_USER);
|
||||
$queueForAudits->setUser($userClone);
|
||||
}
|
||||
|
||||
@@ -584,7 +640,7 @@ App::init()
|
||||
if ($useCache) {
|
||||
$route = $utopia->match($request);
|
||||
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
|
||||
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
$key = $request->cacheIdentifier();
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
@@ -607,12 +663,16 @@ App::init()
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$bucket->getAttribute('transformations', true) && !$isAppUser && !$isPrivilegedUser) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_TRANSFORMATIONS_DISABLED);
|
||||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
@@ -637,7 +697,7 @@ App::init()
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
//Do not update transformedAt if it's a console user
|
||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
if (!User::isPrivileged(Authorization::getRoles())) {
|
||||
$transformedAt = $file->getAttribute('transformedAt', '');
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
|
||||
$file->setAttribute('transformedAt', DateTime::now());
|
||||
@@ -737,7 +797,7 @@ App::shutdown()
|
||||
->inject('queueForWebhooks')
|
||||
->inject('queueForRealtime')
|
||||
->inject('dbForProject')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) 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) use ($parseLabel) {
|
||||
|
||||
$responsePayload = $response->getPayload();
|
||||
|
||||
@@ -785,7 +845,7 @@ App::shutdown()
|
||||
if (!$user->isEmpty()) {
|
||||
$userClone = clone $user;
|
||||
// $user doesn't support `type` and can cause unintended effects.
|
||||
$userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
$userClone->setAttribute('type', ACTIVITY_TYPE_USER);
|
||||
$queueForAudits->setUser($userClone);
|
||||
} elseif ($queueForAudits->getUser() === null || $queueForAudits->getUser()->isEmpty()) {
|
||||
/**
|
||||
@@ -796,10 +856,10 @@ App::shutdown()
|
||||
*
|
||||
* Therefore, we consider this an anonymous request and create a relevant user.
|
||||
*/
|
||||
$user = new Document([
|
||||
$user = new User([
|
||||
'$id' => '',
|
||||
'status' => true,
|
||||
'type' => Auth::ACTIVITY_TYPE_GUEST,
|
||||
'type' => ACTIVITY_TYPE_GUEST,
|
||||
'email' => 'guest.' . $project->getId() . '@service.' . $request->getHostname(),
|
||||
'password' => '',
|
||||
'name' => 'Guest',
|
||||
@@ -889,7 +949,7 @@ App::shutdown()
|
||||
}
|
||||
|
||||
if ($project->getId() !== 'console') {
|
||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
if (!User::isPrivileged(Authorization::getRoles())) {
|
||||
$fileSize = 0;
|
||||
$file = $request->getFiles('file');
|
||||
if (!empty($file)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Request;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
@@ -20,7 +20,7 @@ App::init()
|
||||
$lastUpdate = $session->getAttribute('mfaUpdatedAt');
|
||||
if (!empty($lastUpdate)) {
|
||||
$now = DateTime::now();
|
||||
$maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), Auth::MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge
|
||||
$maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge
|
||||
|
||||
$isSessionFresh = DateTime::formatTz($maxAllowedDate) >= DateTime::formatTz($now);
|
||||
}
|
||||
@@ -49,8 +49,8 @@ App::init()
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
|
||||
$isAppUser = User::isApp(Authorization::getRoles());
|
||||
|
||||
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||
return;
|
||||
|
||||
+39
-36
@@ -1,42 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Utopia\Config\Adapters\PHP;
|
||||
use Utopia\Config\Config;
|
||||
|
||||
require_once __DIR__ . '/../config/storage/resource_limits.php';
|
||||
|
||||
Config::load('template-runtimes', __DIR__ . '/../config/template-runtimes.php');
|
||||
Config::load('events', __DIR__ . '/../config/events.php');
|
||||
Config::load('auth', __DIR__ . '/../config/auth.php');
|
||||
Config::load('apis', __DIR__ . '/../config/apis.php'); // List of APIs
|
||||
Config::load('errors', __DIR__ . '/../config/errors.php');
|
||||
Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php');
|
||||
Config::load('platforms', __DIR__ . '/../config/platforms.php');
|
||||
Config::load('console', __DIR__ . '/../config/console.php');
|
||||
Config::load('collections', __DIR__ . '/../config/collections.php');
|
||||
Config::load('frameworks', __DIR__ . '/../config/frameworks.php');
|
||||
Config::load('runtimes', __DIR__ . '/../config/runtimes.php');
|
||||
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php');
|
||||
Config::load('usage', __DIR__ . '/../config/usage.php');
|
||||
Config::load('roles', __DIR__ . '/../config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/../config/scopes.php'); // User roles and scopes
|
||||
Config::load('services', __DIR__ . '/../config/services.php'); // List of services
|
||||
Config::load('variables', __DIR__ . '/../config/variables.php'); // List of env variables
|
||||
Config::load('regions', __DIR__ . '/../config/regions.php'); // List of available regions
|
||||
Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php');
|
||||
Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php');
|
||||
Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php');
|
||||
Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php');
|
||||
Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php');
|
||||
Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php');
|
||||
Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php');
|
||||
Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php');
|
||||
Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php');
|
||||
Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php');
|
||||
Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php');
|
||||
Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php');
|
||||
Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php');
|
||||
Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php');
|
||||
Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php');
|
||||
Config::load('specifications', __DIR__ . '/../config/specifications.php');
|
||||
Config::load('templates-function', __DIR__ . '/../config/templates/function.php');
|
||||
Config::load('templates-site', __DIR__ . '/../config/templates/site.php');
|
||||
$configAdapter = new PHP();
|
||||
|
||||
Config::load('runtimes', __DIR__ . '/../config/runtimes.php', $configAdapter);
|
||||
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php', $configAdapter);
|
||||
Config::load('template-runtimes', __DIR__ . '/../config/template-runtimes.php', $configAdapter);
|
||||
Config::load('events', __DIR__ . '/../config/events.php', $configAdapter);
|
||||
Config::load('auth', __DIR__ . '/../config/auth.php', $configAdapter);
|
||||
Config::load('apis', __DIR__ . '/../config/apis.php', $configAdapter); // List of APIs
|
||||
Config::load('errors', __DIR__ . '/../config/errors.php', $configAdapter);
|
||||
Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php', $configAdapter);
|
||||
Config::load('platforms', __DIR__ . '/../config/platforms.php', $configAdapter);
|
||||
Config::load('console', __DIR__ . '/../config/console.php', $configAdapter);
|
||||
Config::load('collections', __DIR__ . '/../config/collections.php', $configAdapter);
|
||||
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('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
|
||||
Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php', $configAdapter);
|
||||
Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php', $configAdapter);
|
||||
Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php', $configAdapter);
|
||||
Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php', $configAdapter);
|
||||
Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php', $configAdapter);
|
||||
Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php', $configAdapter);
|
||||
Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php', $configAdapter);
|
||||
Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php', $configAdapter);
|
||||
Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php', $configAdapter);
|
||||
Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php', $configAdapter);
|
||||
Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php', $configAdapter);
|
||||
Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php', $configAdapter);
|
||||
Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php', $configAdapter);
|
||||
Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php', $configAdapter);
|
||||
Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php', $configAdapter);
|
||||
Config::load('specifications', __DIR__ . '/../config/specifications.php', $configAdapter);
|
||||
Config::load('templates-function', __DIR__ . '/../config/templates/function.php', $configAdapter);
|
||||
Config::load('templates-site', __DIR__ . '/../config/templates/site.php', $configAdapter);
|
||||
|
||||
@@ -93,6 +93,69 @@ const APP_VCS_GITHUB_EMAIL = 'team@appwrite.io';
|
||||
const APP_VCS_GITHUB_URL = 'https://github.com/TeamAppwrite';
|
||||
const APP_BRANDED_EMAIL_BASE_TEMPLATE = 'email-base-styled';
|
||||
|
||||
/**
|
||||
* JWT for Resource Tokens.
|
||||
*/
|
||||
const RESOURCE_TOKEN_ALGORITHM = 'HS256';
|
||||
const RESOURCE_TOKEN_MAX_AGE = 86400 * 365 * 10; /* 10 years */
|
||||
const RESOURCE_TOKEN_LEEWAY = 10; // 10 seconds
|
||||
|
||||
/**
|
||||
* Token Expiration times.
|
||||
*/
|
||||
const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */
|
||||
const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */
|
||||
|
||||
/**
|
||||
* Token Lengths.
|
||||
*/
|
||||
const TOKEN_LENGTH_MAGIC_URL = 64;
|
||||
const TOKEN_LENGTH_VERIFICATION = 256;
|
||||
const TOKEN_LENGTH_RECOVERY = 256;
|
||||
const TOKEN_LENGTH_OAUTH2 = 64;
|
||||
const TOKEN_LENGTH_SESSION = 256;
|
||||
|
||||
/**
|
||||
* Token Types.
|
||||
*/
|
||||
const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
const TOKEN_TYPE_VERIFICATION = 2;
|
||||
const TOKEN_TYPE_RECOVERY = 3;
|
||||
const TOKEN_TYPE_INVITE = 4;
|
||||
const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
const TOKEN_TYPE_PHONE = 6;
|
||||
const TOKEN_TYPE_OAUTH2 = 7;
|
||||
const TOKEN_TYPE_GENERIC = 8;
|
||||
const TOKEN_TYPE_EMAIL = 9; // OTP
|
||||
|
||||
/**
|
||||
* Session Providers.
|
||||
*/
|
||||
const SESSION_PROVIDER_EMAIL = 'email';
|
||||
const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
const SESSION_PROVIDER_PHONE = 'phone';
|
||||
const SESSION_PROVIDER_OAUTH2 = 'oauth2';
|
||||
const SESSION_PROVIDER_TOKEN = 'token';
|
||||
const SESSION_PROVIDER_SERVER = 'server';
|
||||
|
||||
/**
|
||||
* Activity associated with user or the app.
|
||||
*/
|
||||
const ACTIVITY_TYPE_APP = 'app';
|
||||
const ACTIVITY_TYPE_USER = 'user';
|
||||
const ACTIVITY_TYPE_GUEST = 'guest';
|
||||
|
||||
/**
|
||||
* MFA
|
||||
*/
|
||||
const MFA_RECENT_DURATION = 1800; // 30 mins
|
||||
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||
@@ -297,3 +360,6 @@ const TOKENS_RESOURCE_TYPE_DATABASES = 'databases';
|
||||
const SCHEDULE_RESOURCE_TYPE_EXECUTION = 'execution';
|
||||
const SCHEDULE_RESOURCE_TYPE_FUNCTION = 'function';
|
||||
const SCHEDULE_RESOURCE_TYPE_MESSAGE = 'message';
|
||||
|
||||
/** Preview cookie */
|
||||
const COOKIE_NAME_PREVIEW = 'a_jwt_console';
|
||||
|
||||
+47
-1
@@ -39,6 +39,7 @@ if (!App::isProduction()) {
|
||||
PublicDomain::allow(['request-catcher-sms']);
|
||||
PublicDomain::allow(['request-catcher-webhook']);
|
||||
}
|
||||
|
||||
$register->set('logger', function () {
|
||||
// Register error logger
|
||||
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||
@@ -97,6 +98,51 @@ $register->set('logger', function () {
|
||||
return new Logger($adapter);
|
||||
});
|
||||
|
||||
$register->set('realtimeLogger', function () {
|
||||
// Register error logger for realtime, falls back to default logging config
|
||||
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG_REALTIME', '')
|
||||
?: System::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
|
||||
if (empty($providerConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$loggingProvider = new DSN($providerConfig);
|
||||
$providerName = $loggingProvider->getScheme();
|
||||
$providerConfig = match ($providerName) {
|
||||
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()],
|
||||
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
|
||||
default => ['key' => $loggingProvider->getHost()],
|
||||
};
|
||||
|
||||
if (empty($providerName) || empty($providerConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Logger::hasProvider($providerName)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
|
||||
}
|
||||
|
||||
try {
|
||||
$adapter = match ($providerName) {
|
||||
'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']),
|
||||
'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']),
|
||||
'raygun' => new Raygun($providerConfig['key']),
|
||||
'appsignal' => new AppSignal($providerConfig['key']),
|
||||
default => null
|
||||
};
|
||||
} catch (Throwable $th) {
|
||||
$adapter = null;
|
||||
}
|
||||
|
||||
if ($adapter === null) {
|
||||
Console::error("Logging provider not supported. Logging is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
return new Logger($adapter);
|
||||
});
|
||||
|
||||
$register->set('pools', function () {
|
||||
$group = new Group();
|
||||
|
||||
@@ -326,7 +372,7 @@ $register->set('smtp', function () {
|
||||
return $mail;
|
||||
});
|
||||
$register->set('geodb', function () {
|
||||
return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb');
|
||||
return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2025-12.mmdb');
|
||||
});
|
||||
$register->set('passwordsDictionary', function () {
|
||||
$content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords');
|
||||
|
||||
+99
-48
@@ -2,7 +2,6 @@
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Key;
|
||||
use Appwrite\Databases\TransactionState;
|
||||
use Appwrite\Event\Audit;
|
||||
@@ -23,10 +22,18 @@ use Appwrite\Extend\Exception;
|
||||
use Appwrite\GraphQL\Schema;
|
||||
use Appwrite\Network\Platform;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Executor\Executor;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Code;
|
||||
use Utopia\Auth\Proofs\Password;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -226,76 +233,91 @@ App::setResource('platforms', function (Request $request, Document $console, Doc
|
||||
];
|
||||
}, ['request', 'console', 'project', 'dbForPlatform']);
|
||||
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
/** @var string $mode */
|
||||
App::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken) {
|
||||
/**
|
||||
* Handles user authentication and session validation.
|
||||
*
|
||||
* This function follows a series of steps to determine the appropriate user session
|
||||
* based on cookies, headers, and JWT tokens.
|
||||
*
|
||||
* Process:
|
||||
* 1. Checks the cookie based on mode:
|
||||
* - If in admin mode, uses console project id for key.
|
||||
* - Otherwise, sets the key using the project ID
|
||||
* 2. If no cookie is found, attempts to retrieve the fallback header `x-fallback-cookies`.
|
||||
* - If this method is used, returns the header: `X-Debug-Fallback: true`.
|
||||
* 3. Fetches the user document from the appropriate database based on the mode.
|
||||
* 4. If the user document is empty or the session key cannot be verified, sets an empty user document.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
Authorization::setDefaultStatus(true);
|
||||
|
||||
Auth::setCookieName('a_session_' . $project->getId());
|
||||
$store->setKey('a_session_' . $project->getId());
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
Auth::setCookieName('a_session_' . $console->getId());
|
||||
$store->setKey('a_session_' . $console->getId());
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession(
|
||||
$store->decode(
|
||||
$request->getCookie(
|
||||
Auth::$cookieName, // Get sessions
|
||||
$request->getCookie(Auth::$cookieName . '_legacy', '')
|
||||
$store->getKey(), // Get sessions
|
||||
$request->getCookie($store->getKey() . '_legacy', '')
|
||||
)
|
||||
);
|
||||
|
||||
// Get session from header for SSR clients
|
||||
if (empty($session['id']) && empty($session['secret'])) {
|
||||
if (empty($store->getProperty('id', '')) && empty($store->getProperty('secret', ''))) {
|
||||
$sessionHeader = $request->getHeader('x-appwrite-session', '');
|
||||
|
||||
if (!empty($sessionHeader)) {
|
||||
$session = Auth::decodeSession($sessionHeader);
|
||||
$store->decode($sessionHeader);
|
||||
}
|
||||
}
|
||||
|
||||
// Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies
|
||||
if ($response) {
|
||||
if ($response) { // if in http context - add debug header
|
||||
$response->addHeader('X-Debug-Fallback', 'false');
|
||||
}
|
||||
|
||||
if (empty($session['id']) && empty($session['secret'])) {
|
||||
if (empty($store->getProperty('id', '')) && empty($store->getProperty('secret', ''))) {
|
||||
if ($response) {
|
||||
$response->addHeader('X-Debug-Fallback', 'true');
|
||||
}
|
||||
$fallback = $request->getHeader('x-fallback-cookies', '');
|
||||
$fallback = \json_decode($fallback, true);
|
||||
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
|
||||
$store->decode(((is_array($fallback) && isset($fallback[$store->getKey()])) ? $fallback[$store->getKey()] : ''));
|
||||
}
|
||||
|
||||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
|
||||
$user = new Document([]);
|
||||
|
||||
if (!empty(Auth::$unique)) {
|
||||
if ($mode === APP_MODE_ADMIN) {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
} elseif (!$project->isEmpty()) {
|
||||
if ($project->getId() === 'console') {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
$user = null;
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
/** @var User $user */
|
||||
$user = $dbForPlatform->getDocument('users', $store->getProperty('id', ''));
|
||||
} else {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new User([]);
|
||||
} else {
|
||||
if (!empty($store->getProperty('id', ''))) {
|
||||
if ($project->getId() === 'console') {
|
||||
/** @var User $user */
|
||||
$user = $dbForPlatform->getDocument('users', $store->getProperty('id', ''));
|
||||
} else {
|
||||
/** @var User $user */
|
||||
$user = $dbForProject->getDocument('users', $store->getProperty('id', ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!$user ||
|
||||
$user->isEmpty() // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
|
||||
|| !$user->sessionVerify($store->getProperty('secret', ''), $proofForToken)
|
||||
) { // Validate user has valid login token
|
||||
$user = new Document([]);
|
||||
$user = new User([]);
|
||||
}
|
||||
|
||||
// if (APP_MODE_ADMIN === $mode) {
|
||||
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
|
||||
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||
@@ -303,18 +325,14 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
// $user = new Document([]);
|
||||
// }
|
||||
// }
|
||||
|
||||
$authJWT = $request->getHeader('x-appwrite-jwt', '');
|
||||
|
||||
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
|
||||
$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) {
|
||||
@@ -323,20 +341,18 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
}
|
||||
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
if (!empty($jwtSessionId)) {
|
||||
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
|
||||
$user = new Document([]);
|
||||
$user = new User([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->setMetadata('user', $user->getId());
|
||||
$dbForPlatform->setMetadata('user', $user->getId());
|
||||
|
||||
return $user;
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken']);
|
||||
|
||||
App::setResource('project', function ($dbForPlatform, $request, $console) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
@@ -354,31 +370,61 @@ App::setResource('project', function ($dbForPlatform, $request, $console) {
|
||||
return $project;
|
||||
}, ['dbForPlatform', 'request', 'console']);
|
||||
|
||||
App::setResource('session', function (Document $user) {
|
||||
App::setResource('session', function (User $user, Store $store, Token $proofForToken) {
|
||||
if ($user->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
|
||||
$sessionId = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken);
|
||||
|
||||
if (!$sessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($sessions as $session) {/** @var Document $session */
|
||||
foreach ($sessions as $session) {
|
||||
/** @var Document $session */
|
||||
if ($sessionId === $session->getId()) {
|
||||
return $session;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}, ['user']);
|
||||
}, ['user', 'store', 'proofForToken']);
|
||||
|
||||
App::setResource('console', function () {
|
||||
return new Document(Config::getParam('console'));
|
||||
}, []);
|
||||
|
||||
App::setResource('store', function (): Store {
|
||||
return new Store();
|
||||
});
|
||||
|
||||
App::setResource('proofForPassword', function (): Password {
|
||||
$hash = new Argon2();
|
||||
$hash
|
||||
->setMemoryCost(7168)
|
||||
->setTimeCost(5)
|
||||
->setThreads(1);
|
||||
|
||||
$password = new Password();
|
||||
$password
|
||||
->setHash($hash);
|
||||
|
||||
return $password;
|
||||
});
|
||||
|
||||
App::setResource('proofForToken', function (): Token {
|
||||
$token = new Token();
|
||||
$token->setHash(new Sha());
|
||||
return $token;
|
||||
});
|
||||
|
||||
App::setResource('proofForCode', function (): Code {
|
||||
$code = new Code();
|
||||
$code->setHash(new Sha());
|
||||
return $code;
|
||||
});
|
||||
|
||||
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
@@ -399,6 +445,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
|
||||
->setMetadata('project', $project->getId())
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
$database->setDocumentType('users', User::class);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
@@ -428,6 +475,8 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
$database->setDocumentType('users', User::class);
|
||||
|
||||
return $database;
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
@@ -452,6 +501,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||
->setMetadata('project', $project->getId())
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
$database->setDocumentType('users', User::class);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
@@ -953,7 +1003,8 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
|
||||
$tokenJWT = $request->getParam('token');
|
||||
|
||||
if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
|
||||
// Use a large but reasonable maxAge to avoid auto-exp when token has no expiry
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), RESOURCE_TOKEN_ALGORITHM, RESOURCE_TOKEN_MAX_AGE, RESOURCE_TOKEN_LEEWAY); // Instantiate with key, algo, maxAge and leeway.
|
||||
|
||||
try {
|
||||
$payload = $jwt->decode($tokenJWT);
|
||||
|
||||
+69
-48
@@ -1,13 +1,14 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\PubSub\Adapter\Pool as PubSubPool;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
use Swoole\Http\Response as SwooleResponse;
|
||||
use Swoole\Runtime;
|
||||
@@ -16,6 +17,9 @@ 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;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -49,14 +53,14 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
if (!function_exists('getConsoleDB')) {
|
||||
function getConsoleDB(): Database
|
||||
{
|
||||
global $register;
|
||||
$ctx = Coroutine::getContext();
|
||||
|
||||
static $database = null;
|
||||
|
||||
if ($database !== null) {
|
||||
return $database;
|
||||
if (isset($ctx['dbForPlatform'])) {
|
||||
return $ctx['dbForPlatform'];
|
||||
}
|
||||
|
||||
global $register;
|
||||
|
||||
/** @var Group $pools */
|
||||
$pools = $register->get('pools');
|
||||
|
||||
@@ -66,8 +70,8 @@ if (!function_exists('getConsoleDB')) {
|
||||
->setNamespace('_console')
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', '_console');
|
||||
|
||||
return $database;
|
||||
$database->setDocumentType('users', User::class);
|
||||
return $ctx['dbForPlatform'] = $database;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,14 +79,18 @@ if (!function_exists('getConsoleDB')) {
|
||||
if (!function_exists('getProjectDB')) {
|
||||
function getProjectDB(Document $project): Database
|
||||
{
|
||||
global $register;
|
||||
$ctx = Coroutine::getContext();
|
||||
|
||||
static $databases = [];
|
||||
|
||||
if (isset($databases[$project->getSequence()])) {
|
||||
return $databases[$project->getSequence()];
|
||||
if (!isset($ctx['dbForProject'])) {
|
||||
$ctx['dbForProject'] = [];
|
||||
}
|
||||
|
||||
if (isset($ctx['dbForProject'][$project->getSequence()])) {
|
||||
return $ctx['dbForProject'][$project->getSequence()];
|
||||
}
|
||||
|
||||
global $register;
|
||||
|
||||
/** @var Group $pools */
|
||||
$pools = $register->get('pools');
|
||||
|
||||
@@ -118,7 +126,9 @@ if (!function_exists('getProjectDB')) {
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId());
|
||||
|
||||
return $databases[$project->getSequence()] = $database;
|
||||
$database->setDocumentType('users', User::class);
|
||||
|
||||
return $ctx['dbForProject'][$project->getSequence()] = $database;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,14 +136,14 @@ if (!function_exists('getProjectDB')) {
|
||||
if (!function_exists('getCache')) {
|
||||
function getCache(): Cache
|
||||
{
|
||||
global $register;
|
||||
$ctx = Coroutine::getContext();
|
||||
|
||||
static $cache = null;
|
||||
|
||||
if ($cache !== null) {
|
||||
return $cache;
|
||||
if (isset($ctx['cache'])) {
|
||||
return $ctx['cache'];
|
||||
}
|
||||
|
||||
global $register;
|
||||
|
||||
$pools = $register->get('pools'); /** @var Group $pools */
|
||||
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
@@ -143,7 +153,7 @@ if (!function_exists('getCache')) {
|
||||
$adapters[] = new CachePool($pools->get($value));
|
||||
}
|
||||
|
||||
return $cache = new Cache(new Sharding($adapters));
|
||||
return $ctx['cache'] = new Cache(new Sharding($adapters));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,10 +161,10 @@ if (!function_exists('getCache')) {
|
||||
if (!function_exists('getRedis')) {
|
||||
function getRedis(): \Redis
|
||||
{
|
||||
static $redis = null;
|
||||
$ctx = Coroutine::getContext();
|
||||
|
||||
if ($redis !== null) {
|
||||
return $redis;
|
||||
if (isset($ctx['redis'])) {
|
||||
return $ctx['redis'];
|
||||
}
|
||||
|
||||
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
|
||||
@@ -168,46 +178,46 @@ if (!function_exists('getRedis')) {
|
||||
}
|
||||
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
|
||||
|
||||
return $redis;
|
||||
return $ctx['redis'] = $redis;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getTimelimit')) {
|
||||
function getTimelimit(): TimeLimitRedis
|
||||
function getTimelimit(string $key = "", int $limit = 0, int $seconds = 1): TimeLimitRedis
|
||||
{
|
||||
static $timelimit = null;
|
||||
$ctx = Coroutine::getContext();
|
||||
|
||||
if ($timelimit !== null) {
|
||||
return $timelimit;
|
||||
if (isset($ctx['timelimit'])) {
|
||||
return $ctx['timelimit'];
|
||||
}
|
||||
|
||||
return $timelimit = new TimeLimitRedis("", 0, 1, getRedis());
|
||||
return $ctx['timelimit'] = new TimeLimitRedis($key, $limit, $seconds, getRedis());
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getRealtime')) {
|
||||
function getRealtime(): Realtime
|
||||
{
|
||||
static $realtime = null;
|
||||
$ctx = Coroutine::getContext();
|
||||
|
||||
if ($realtime !== null) {
|
||||
return $realtime;
|
||||
if (isset($ctx['realtime'])) {
|
||||
return $ctx['realtime'];
|
||||
}
|
||||
|
||||
return $realtime = new Realtime();
|
||||
return $ctx['realtime'] = new Realtime();
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getTelemetry')) {
|
||||
function getTelemetry(int $workerId): Utopia\Telemetry\Adapter
|
||||
{
|
||||
static $telemetry = null;
|
||||
$ctx = Coroutine::getContext();
|
||||
|
||||
if ($telemetry !== null) {
|
||||
return $telemetry;
|
||||
if (isset($ctx['telemetry'])) {
|
||||
return $ctx['telemetry'];
|
||||
}
|
||||
|
||||
return $telemetry = new NoTelemetry();
|
||||
return $ctx['telemetry'] = new NoTelemetry();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +246,7 @@ $adapter
|
||||
$server = new Server($adapter);
|
||||
|
||||
$logError = function (Throwable $error, string $action) use ($register) {
|
||||
$logger = $register->get('logger');
|
||||
$logger = $register->get('realtimeLogger');
|
||||
|
||||
if ($logger && !$error instanceof Exception) {
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
@@ -457,9 +467,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
$project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
|
||||
$database = getProjectDB($project);
|
||||
|
||||
/** @var Appwrite\Utopia\Database\Documents\User $user */
|
||||
$user = $database->getDocument('users', $userId);
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
$roles = $user->getRoles();
|
||||
$channels = $realtime->connections[$connection]['channels'];
|
||||
|
||||
$realtime->unsubscribe($connection);
|
||||
@@ -526,14 +537,14 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
if (
|
||||
array_key_exists('realtime', $project->getAttribute('apis', []))
|
||||
&& !$project->getAttribute('apis', [])['realtime']
|
||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
||||
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
|
||||
) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
||||
}
|
||||
|
||||
$timelimit = $app->getResource('timelimit');
|
||||
$platforms = $app->getResource('platforms');
|
||||
$user = $app->getResource('user'); /** @var Document $user */
|
||||
$user = $app->getResource('user'); /** @var User $user */
|
||||
|
||||
/*
|
||||
* Abuse Check
|
||||
@@ -563,7 +574,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
|
||||
}
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
$roles = $user->getRoles();
|
||||
|
||||
$channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId());
|
||||
|
||||
@@ -678,21 +689,31 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Payload is not valid.');
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession($message['data']['session']);
|
||||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
$store = new Store();
|
||||
|
||||
$user = $database->getDocument('users', Auth::$unique);
|
||||
$store->decode($message['data']['session']);
|
||||
|
||||
/** @var User $user */
|
||||
$user = $database->getDocument('users', $store->getProperty('id', ''));
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* Moving forward, we should try to use our dependency injection container
|
||||
* to inject the proof for token.
|
||||
* This way we will have one source of truth for the proof for token.
|
||||
*/
|
||||
$proofForToken = new Token();
|
||||
$proofForToken->setHash(new Sha());
|
||||
|
||||
if (
|
||||
empty($user->getId()) // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) // Validate user has valid login token
|
||||
|| !$user->sessionVerify($store->getProperty('secret', ''), $proofForToken) // Validate user has valid login token
|
||||
) {
|
||||
// cookie not valid
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
|
||||
}
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
$roles = $user->getRoles();
|
||||
$channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId());
|
||||
$realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels);
|
||||
|
||||
|
||||
@@ -849,7 +849,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DB_PASS
|
||||
|
||||
appwrite-assistant:
|
||||
image: appwrite/assistant:0.8.3
|
||||
image: appwrite/assistant:0.8.4
|
||||
container_name: appwrite-assistant
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
@@ -857,9 +857,9 @@ $image = $this->getParam('image', '');
|
||||
- appwrite
|
||||
environment:
|
||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||
|
||||
|
||||
appwrite-browser:
|
||||
image: appwrite/browser:0.3.1
|
||||
image: appwrite/browser:0.3.2
|
||||
container_name: appwrite-browser
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
|
||||
+3
-1
@@ -17,6 +17,7 @@ use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Executor\Executor;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
@@ -55,7 +56,7 @@ Server::setResource('dbForPlatform', function (Cache $cache, Registry $register)
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$dbForPlatform = new Database($adapter, $cache);
|
||||
$dbForPlatform->setNamespace('_console');
|
||||
|
||||
$dbForPlatform->setDocumentType('users', User::class);
|
||||
return $dbForPlatform;
|
||||
}, ['cache', 'register']);
|
||||
|
||||
@@ -86,6 +87,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
|
||||
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
$database->setDocumentType('users', User::class);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
|
||||
+5
-4
@@ -48,14 +48,15 @@
|
||||
"utopia-php/abuse": "1.*",
|
||||
"utopia-php/analytics": "0.10.*",
|
||||
"utopia-php/audit": "1.*",
|
||||
"utopia-php/auth": "0.5.*",
|
||||
"utopia-php/cache": "0.13.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/config": "1.*.*",
|
||||
"utopia-php/database": "3.*",
|
||||
"utopia-php/detector": "0.2.*",
|
||||
"utopia-php/domains": "0.9.*",
|
||||
"utopia-php/emails": "0.6.*",
|
||||
"utopia-php/dns": "1.1.*",
|
||||
"utopia-php/dns": "1.4.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.4.*",
|
||||
@@ -63,7 +64,7 @@
|
||||
"utopia-php/locale": "0.8.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.20.*",
|
||||
"utopia-php/migration": "1.*",
|
||||
"utopia-php/migration": "1.3.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.8.*",
|
||||
@@ -74,7 +75,7 @@
|
||||
"utopia-php/swoole": "0.8.*",
|
||||
"utopia-php/system": "0.9.*",
|
||||
"utopia-php/telemetry": "0.1.*",
|
||||
"utopia-php/vcs": "0.12.*",
|
||||
"utopia-php/vcs": "0.13.*",
|
||||
"utopia-php/websocket": "0.3.*",
|
||||
"matomo/device-detector": "6.4.*",
|
||||
"dragonmantank/cron-expression": "3.4.*",
|
||||
|
||||
Generated
+288
-243
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
services:
|
||||
appwrite:
|
||||
# volumes:
|
||||
# - ~/.ssh:/root/.ssh
|
||||
environment:
|
||||
- GH_TOKEN=
|
||||
- GIT_EMAIL=
|
||||
+3
-2
@@ -285,6 +285,7 @@ services:
|
||||
- _APP_DB_PASS
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_LOGGING_CONFIG_REALTIME
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-worker-audits:
|
||||
@@ -950,7 +951,7 @@ services:
|
||||
|
||||
appwrite-assistant:
|
||||
container_name: appwrite-assistant
|
||||
image: appwrite/assistant:0.8.3
|
||||
image: appwrite/assistant:0.8.4
|
||||
networks:
|
||||
- appwrite
|
||||
environment:
|
||||
@@ -958,7 +959,7 @@ services:
|
||||
|
||||
appwrite-browser:
|
||||
container_name: appwrite-browser
|
||||
image: appwrite/browser:0.3.1
|
||||
image: appwrite/browser:0.3.2
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.5.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: <REGION>.cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.5.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.5.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: <REGION>.cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.5.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: <REGION>.cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.5.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.5.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: <REGION>.cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.7.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.7.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
POST /v1/account/mfa/challenge HTTP/1.1
|
||||
POST /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.7.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUT /v1/account/mfa/challenge HTTP/1.1
|
||||
PUT /v1/account/mfa/challenges HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.7.0
|
||||
|
||||
@@ -13,7 +13,7 @@ account.createOAuth2Session(
|
||||
OAuthProvider.AMAZON, // provider
|
||||
"https://example.com", // success (optional)
|
||||
"https://example.com", // failure (optional)
|
||||
listOf(), // scopes (optional)
|
||||
List.of(), // scopes (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -13,7 +13,7 @@ account.createOAuth2Token(
|
||||
OAuthProvider.AMAZON, // provider
|
||||
"https://example.com", // success (optional)
|
||||
"https://example.com", // failure (optional)
|
||||
listOf(), // scopes (optional)
|
||||
List.of(), // scopes (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -9,7 +9,7 @@ Client client = new Client(context)
|
||||
Account account = new Account(client);
|
||||
|
||||
account.listIdentities(
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -9,7 +9,7 @@ Client client = new Client(context)
|
||||
Account account = new Account(client);
|
||||
|
||||
account.listLogs(
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -9,10 +9,10 @@ Client client = new Client(context)
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updatePrefs(
|
||||
mapOf(
|
||||
"language" to "en",
|
||||
"timezone" to "UTC",
|
||||
"darkTheme" to true
|
||||
Map.of(
|
||||
"language", "en",
|
||||
"timezone", "UTC",
|
||||
"darkTheme", true
|
||||
), // prefs
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Avatars;
|
||||
import io.appwrite.enums.Theme;
|
||||
import io.appwrite.enums.Timezone;
|
||||
import io.appwrite.enums.Output;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -10,25 +13,28 @@ Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getScreenshot(
|
||||
"https://example.com", // url
|
||||
mapOf( "a" to "b" ), // headers (optional)
|
||||
1, // viewportWidth (optional)
|
||||
1, // viewportHeight (optional)
|
||||
0.1, // scale (optional)
|
||||
theme.LIGHT, // theme (optional)
|
||||
"<USER_AGENT>", // userAgent (optional)
|
||||
false, // fullpage (optional)
|
||||
"<LOCALE>", // locale (optional)
|
||||
timezone.AFRICA_ABIDJAN, // timezone (optional)
|
||||
-90, // latitude (optional)
|
||||
-180, // longitude (optional)
|
||||
0, // accuracy (optional)
|
||||
false, // touch (optional)
|
||||
listOf(), // permissions (optional)
|
||||
0, // sleep (optional)
|
||||
0, // width (optional)
|
||||
0, // height (optional)
|
||||
-1, // quality (optional)
|
||||
output.JPG, // output (optional)
|
||||
Map.of(
|
||||
"Authorization", "Bearer token123",
|
||||
"X-Custom-Header", "value"
|
||||
), // headers (optional)
|
||||
1920, // viewportWidth (optional)
|
||||
1080, // viewportHeight (optional)
|
||||
2, // scale (optional)
|
||||
Theme.LIGHT, // theme (optional)
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // userAgent (optional)
|
||||
true, // fullpage (optional)
|
||||
"en-US", // locale (optional)
|
||||
Timezone.AFRICA_ABIDJAN, // timezone (optional)
|
||||
37.7749, // latitude (optional)
|
||||
-122.4194, // longitude (optional)
|
||||
100, // accuracy (optional)
|
||||
true, // touch (optional)
|
||||
List.of("geolocation", "notifications"), // permissions (optional)
|
||||
3, // sleep (optional)
|
||||
800, // width (optional)
|
||||
600, // height (optional)
|
||||
85, // quality (optional)
|
||||
Output.JPG, // output (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Databases;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.Databases;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,14 +14,14 @@ databases.createDocument(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
mapOf(
|
||||
"username" to "walter.obrien",
|
||||
"email" to "walter.obrien@example.com",
|
||||
"fullName" to "Walter O'Brien",
|
||||
"age" to 30,
|
||||
"isAdmin" to false
|
||||
Map.of(
|
||||
"username", "walter.obrien",
|
||||
"email", "walter.obrien@example.com",
|
||||
"fullName", "Walter O'Brien",
|
||||
"age", 30,
|
||||
"isAdmin", false
|
||||
), // data
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -10,17 +10,15 @@ Databases databases = new Databases(client);
|
||||
|
||||
databases.createOperations(
|
||||
"<TRANSACTION_ID>", // transactionId
|
||||
listOf(
|
||||
{
|
||||
"action": "create",
|
||||
"databaseId": "<DATABASE_ID>",
|
||||
"collectionId": "<COLLECTION_ID>",
|
||||
"documentId": "<DOCUMENT_ID>",
|
||||
"data": {
|
||||
"name": "Walter O'Brien"
|
||||
}
|
||||
}
|
||||
), // operations (optional)
|
||||
List.of(Map.of(
|
||||
"action", "create",
|
||||
"databaseId", "<DATABASE_ID>",
|
||||
"collectionId", "<COLLECTION_ID>",
|
||||
"documentId", "<DOCUMENT_ID>",
|
||||
"data", Map.of(
|
||||
"name", "Walter O'Brien"
|
||||
)
|
||||
)), // operations (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -12,7 +12,7 @@ databases.getDocument(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -11,7 +11,7 @@ Databases databases = new Databases(client);
|
||||
databases.listDocuments(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -9,7 +9,7 @@ Client client = new Client(context)
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
databases.listTransactions(
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Databases;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.Databases;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,8 +14,8 @@ databases.updateDocument(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
mapOf( "a" to "b" ), // data (optional)
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
Map.of("a", "b"), // data (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Databases;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.Databases;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,8 +14,8 @@ databases.upsertDocument(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
mapOf( "a" to "b" ), // data
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
Map.of("a", "b"), // data
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Functions;
|
||||
import io.appwrite.enums.ExecutionMethod;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,7 +15,7 @@ functions.createExecution(
|
||||
false, // async (optional)
|
||||
"<PATH>", // path (optional)
|
||||
ExecutionMethod.GET, // method (optional)
|
||||
mapOf( "a" to "b" ), // headers (optional)
|
||||
Map.of("a", "b"), // headers (optional)
|
||||
"<SCHEDULED_AT>", // scheduledAt (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -10,7 +10,7 @@ Functions functions = new Functions(client);
|
||||
|
||||
functions.listExecutions(
|
||||
"<FUNCTION_ID>", // functionId
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -9,7 +9,7 @@ Client client = new Client(context)
|
||||
Graphql graphql = new Graphql(client);
|
||||
|
||||
graphql.mutation(
|
||||
mapOf( "a" to "b" ), // query
|
||||
Map.of("a", "b"), // query
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -9,7 +9,7 @@ Client client = new Client(context)
|
||||
Graphql graphql = new Graphql(client);
|
||||
|
||||
graphql.query(
|
||||
mapOf( "a" to "b" ), // query
|
||||
Map.of("a", "b"), // query
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.models.InputFile;
|
||||
import io.appwrite.services.Storage;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.Storage;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -15,7 +15,7 @@ storage.createFile(
|
||||
"<BUCKET_ID>", // bucketId
|
||||
"<FILE_ID>", // fileId
|
||||
InputFile.fromPath("file.png"), // file
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Storage;
|
||||
import io.appwrite.enums.ImageGravity;
|
||||
import io.appwrite.enums.ImageFormat;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
|
||||
@@ -10,7 +10,7 @@ Storage storage = new Storage(client);
|
||||
|
||||
storage.listFiles(
|
||||
"<BUCKET_ID>", // bucketId
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
"<SEARCH>", // search (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Storage;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.Storage;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,7 +14,7 @@ storage.updateFile(
|
||||
"<BUCKET_ID>", // bucketId
|
||||
"<FILE_ID>", // fileId
|
||||
"<NAME>", // name (optional)
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -10,17 +10,15 @@ TablesDB tablesDB = new TablesDB(client);
|
||||
|
||||
tablesDB.createOperations(
|
||||
"<TRANSACTION_ID>", // transactionId
|
||||
listOf(
|
||||
{
|
||||
"action": "create",
|
||||
"databaseId": "<DATABASE_ID>",
|
||||
"tableId": "<TABLE_ID>",
|
||||
"rowId": "<ROW_ID>",
|
||||
"data": {
|
||||
"name": "Walter O'Brien"
|
||||
}
|
||||
}
|
||||
), // operations (optional)
|
||||
List.of(Map.of(
|
||||
"action", "create",
|
||||
"databaseId", "<DATABASE_ID>",
|
||||
"tableId", "<TABLE_ID>",
|
||||
"rowId", "<ROW_ID>",
|
||||
"data", Map.of(
|
||||
"name", "Walter O'Brien"
|
||||
)
|
||||
)), // operations (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.TablesDB;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.TablesDB;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,14 +14,14 @@ tablesDB.createRow(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<TABLE_ID>", // tableId
|
||||
"<ROW_ID>", // rowId
|
||||
mapOf(
|
||||
"username" to "walter.obrien",
|
||||
"email" to "walter.obrien@example.com",
|
||||
"fullName" to "Walter O'Brien",
|
||||
"age" to 30,
|
||||
"isAdmin" to false
|
||||
Map.of(
|
||||
"username", "walter.obrien",
|
||||
"email", "walter.obrien@example.com",
|
||||
"fullName", "Walter O'Brien",
|
||||
"age", 30,
|
||||
"isAdmin", false
|
||||
), // data
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -12,7 +12,7 @@ tablesDB.getRow(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<TABLE_ID>", // tableId
|
||||
"<ROW_ID>", // rowId
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -11,7 +11,7 @@ TablesDB tablesDB = new TablesDB(client);
|
||||
tablesDB.listRows(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<TABLE_ID>", // tableId
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -9,7 +9,7 @@ Client client = new Client(context)
|
||||
TablesDB tablesDB = new TablesDB(client);
|
||||
|
||||
tablesDB.listTransactions(
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.TablesDB;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.TablesDB;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,8 +14,8 @@ tablesDB.updateRow(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<TABLE_ID>", // tableId
|
||||
"<ROW_ID>", // rowId
|
||||
mapOf( "a" to "b" ), // data (optional)
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
Map.of("a", "b"), // data (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.TablesDB;
|
||||
import io.appwrite.Permission;
|
||||
import io.appwrite.Role;
|
||||
import io.appwrite.services.TablesDB;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
@@ -14,8 +14,8 @@ tablesDB.upsertRow(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<TABLE_ID>", // tableId
|
||||
"<ROW_ID>", // rowId
|
||||
mapOf( "a" to "b" ), // data (optional)
|
||||
listOf(Permission.read(Role.any())), // permissions (optional)
|
||||
Map.of("a", "b"), // data (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
|
||||
@@ -10,7 +10,7 @@ Teams teams = new Teams(client);
|
||||
|
||||
teams.createMembership(
|
||||
"<TEAM_ID>", // teamId
|
||||
listOf(), // roles
|
||||
List.of(), // roles
|
||||
"email@example.com", // email (optional)
|
||||
"<USER_ID>", // userId (optional)
|
||||
"+12065550100", // phone (optional)
|
||||
|
||||
@@ -11,7 +11,7 @@ Teams teams = new Teams(client);
|
||||
teams.create(
|
||||
"<TEAM_ID>", // teamId
|
||||
"<NAME>", // name
|
||||
listOf(), // roles (optional)
|
||||
List.of(), // roles (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -10,7 +10,7 @@ Teams teams = new Teams(client);
|
||||
|
||||
teams.listMemberships(
|
||||
"<TEAM_ID>", // teamId
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
"<SEARCH>", // search (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -9,7 +9,7 @@ Client client = new Client(context)
|
||||
Teams teams = new Teams(client);
|
||||
|
||||
teams.list(
|
||||
listOf(), // queries (optional)
|
||||
List.of(), // queries (optional)
|
||||
"<SEARCH>", // search (optional)
|
||||
false, // total (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -11,7 +11,7 @@ Teams teams = new Teams(client);
|
||||
teams.updateMembership(
|
||||
"<TEAM_ID>", // teamId
|
||||
"<MEMBERSHIP_ID>", // membershipId
|
||||
listOf(), // roles
|
||||
List.of(), // roles
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -10,7 +10,7 @@ Teams teams = new Teams(client);
|
||||
|
||||
teams.updatePrefs(
|
||||
"<TEAM_ID>", // teamId
|
||||
mapOf( "a" to "b" ), // prefs
|
||||
Map.of("a", "b"), // prefs
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user