Merge remote-tracking branch 'origin/0.13.x' into feat-permissions-improvements
3
.env
|
|
@ -20,6 +20,7 @@ _APP_DB_PORT=3306
|
|||
_APP_DB_SCHEMA=appwrite
|
||||
_APP_DB_USER=user
|
||||
_APP_DB_PASS=password
|
||||
_APP_STORAGE_DEVICE=Local
|
||||
_APP_STORAGE_ANTIVIRUS=disabled
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||
|
|
@ -32,7 +33,7 @@ _APP_SMTP_PORT=1025
|
|||
_APP_SMTP_SECURE=
|
||||
_APP_SMTP_USERNAME=
|
||||
_APP_SMTP_PASSWORD=
|
||||
_APP_STORAGE_LIMIT=10000000
|
||||
_APP_STORAGE_LIMIT=30000000
|
||||
_APP_FUNCTIONS_TIMEOUT=900
|
||||
_APP_FUNCTIONS_CONTAINERS=10
|
||||
_APP_FUNCTIONS_CPUS=1
|
||||
|
|
|
|||
2
.github/workflows/tests.yml
vendored
|
|
@ -42,4 +42,4 @@ jobs:
|
|||
if: always()
|
||||
run: |
|
||||
docker compose down -v
|
||||
docker ps -aq | xargs docker rm --force
|
||||
docker ps -aq | xargs docker rm --force
|
||||
|
|
@ -1,3 +1,12 @@
|
|||
# Version 0.12.3
|
||||
|
||||
## Bugs
|
||||
- Fix update membership roles (#2799)
|
||||
- Fix migration to 0.12.x to populate search fields (#2799)
|
||||
|
||||
## Security
|
||||
- Fix URL schema Validation to only allow http/https (#2801)
|
||||
|
||||
# Version 0.12.2
|
||||
|
||||
## Bugs
|
||||
|
|
|
|||
|
|
@ -152,6 +152,15 @@ ENV _APP_SERVER=swoole \
|
|||
_APP_STORAGE_ANTIVIRUS=enabled \
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav \
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310 \
|
||||
_APP_STORAGE_DEVICE=Local \
|
||||
_APP_STORAGE_DEVICE_S3_ACCESS_KEY= \
|
||||
_APP_STORAGE_DEVICE_S3_SECRET= \
|
||||
_APP_STORAGE_DEVICE_S3_REGION= \
|
||||
_APP_STORAGE_DEVICE_S3_BUCKET= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_SECRET= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_REGION= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_BUCKET= \
|
||||
_APP_REDIS_HOST=redis \
|
||||
_APP_REDIS_PORT=6379 \
|
||||
_APP_DB_HOST=mariadb \
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:0.12.2
|
||||
appwrite/appwrite:0.12.3
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
|
@ -71,7 +71,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:0.12.2
|
||||
appwrite/appwrite:0.12.3
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
|
@ -81,7 +81,7 @@ docker run -it --rm ,
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock ,
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
|
||||
--entrypoint="install" ,
|
||||
appwrite/appwrite:0.12.2
|
||||
appwrite/appwrite:0.12.3
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:0.12.2
|
||||
appwrite/appwrite:0.12.3
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
|
@ -74,7 +74,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:0.12.2
|
||||
appwrite/appwrite:0.12.3
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
|
@ -84,7 +84,7 @@ docker run -it --rm ,
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock ,
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
|
||||
--entrypoint="install" ,
|
||||
appwrite/appwrite:0.12.2
|
||||
appwrite/appwrite:0.12.3
|
||||
```
|
||||
|
||||
Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after installation completes.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,15 @@ use Utopia\Database\Database;
|
|||
$providers = Config::getParam('providers', []);
|
||||
$auth = Config::getParam('auth', []);
|
||||
|
||||
/**
|
||||
* $collection => id of the parent collection where this will be inserted
|
||||
* $id => id of this collection
|
||||
* name => name of this collection
|
||||
* project => whether or not this collection should be created per project
|
||||
* attributes => list of attributes
|
||||
* indexes => list of indexes
|
||||
*/
|
||||
|
||||
$collections = [
|
||||
'collections' => [
|
||||
'$collection' => Database::METADATA,
|
||||
|
|
@ -1576,196 +1585,6 @@ $collections = [
|
|||
],
|
||||
],
|
||||
|
||||
'files' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'files',
|
||||
'name' => 'Files',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'dateCreated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => 'bucketId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'signature',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'mimeType',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 127, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeOriginal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeActual',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'algorithm',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 255,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'comment',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLVersion',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLCipher',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLTag',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLIV',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_bucket',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['bucketId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'functions' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'functions',
|
||||
|
|
@ -1999,6 +1818,28 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksTotal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksUploaded',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
|
|
@ -2250,6 +2091,131 @@ $collections = [
|
|||
],
|
||||
],
|
||||
|
||||
'buckets' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'buckets',
|
||||
'name' => 'Buckets',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'dateCreated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'signed' => false,
|
||||
'size' => 0,
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'dateUpdated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'enabled',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => 128,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'permission',
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'maximumFileSize',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'signed' => false,
|
||||
'size' => 8,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'allowedFileExtensions',
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => 64,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => true,
|
||||
],
|
||||
[
|
||||
'$id' => 'encryption',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'antivirus',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_fulltext_name',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['name'],
|
||||
'lengths' => [1024],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
|
||||
'stats' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'stats',
|
||||
|
|
@ -2385,6 +2351,228 @@ $collections = [
|
|||
],
|
||||
]
|
||||
],
|
||||
'files' => [
|
||||
'$collection' => 'buckets',
|
||||
'$id' => 'files',
|
||||
'$name' => 'Files',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'dateCreated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => 'bucketId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'signature',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'mimeType',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 127, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'metadata',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeOriginal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 8,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeActual',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 8,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'algorithm',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 255,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'comment',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLVersion',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLCipher',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLTag',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLIV',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksTotal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksUploaded',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_bucket',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['bucketId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
return $collections;
|
||||
|
|
|
|||
473
app/config/errors.php
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* List of server wide error codes and their respective messages.
|
||||
*/
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
|
||||
return [
|
||||
/** General Errors */
|
||||
Exception::GENERAL_UNKNOWN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN,
|
||||
'description' => 'An unknown error has occured. Please check the logs for more information.',
|
||||
'code' => 500,
|
||||
],
|
||||
Exception::GENERAL_MOCK => [
|
||||
'name' => Exception::GENERAL_MOCK,
|
||||
'description' => 'General errors thrown by the mock controller used for testing.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GENERAL_ACCESS_FORBIDDEN => [
|
||||
'name' => Exception::GENERAL_ACCESS_FORBIDDEN,
|
||||
'description' => 'Access to this API is forbidden.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::GENERAL_UNKNOWN_ORIGIN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN_ORIGIN,
|
||||
'description' => 'The request originated from an unknown origin. If you trust this domain, please list it as a trusted platform in the Appwrite console.',
|
||||
'code' => 403,
|
||||
],
|
||||
Exception::GENERAL_SERVICE_DISABLED => [
|
||||
'name' => Exception::GENERAL_SERVICE_DISABLED,
|
||||
'description' => 'The requested service is disabled. You can enable the service from the Appwrite console.',
|
||||
'code' => 503,
|
||||
],
|
||||
Exception::GENERAL_UNAUTHORIZED_SCOPE => [
|
||||
'name' => Exception::GENERAL_UNAUTHORIZED_SCOPE,
|
||||
'description' => 'The current user or API key does not have the required scopes to access the requested resource.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::GENERAL_RATE_LIMIT_EXCEEDED => [
|
||||
'name' => Exception::GENERAL_RATE_LIMIT_EXCEEDED,
|
||||
'description' => 'Rate limit for the current endpoint has been exceeded. Please try again after some time.',
|
||||
'code' => 429,
|
||||
],
|
||||
Exception::GENERAL_SMTP_DISABLED => [
|
||||
'name' => Exception::GENERAL_SMTP_DISABLED,
|
||||
'description' => 'SMTP is disabled on your Appwrite instance. You can <a href="/docs/email-delivery">learn more about setting up SMTP</a> in our docs.',
|
||||
'code' => 503,
|
||||
],
|
||||
Exception::GENERAL_ARGUMENT_INVALID => [
|
||||
'name' => Exception::GENERAL_ARGUMENT_INVALID,
|
||||
'description' => 'The request contains one or more invalid arguments. Please refer to the endpoint documentation.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GENERAL_QUERY_LIMIT_EXCEEDED => [
|
||||
'name' => Exception::GENERAL_QUERY_LIMIT_EXCEEDED,
|
||||
'description' => 'Query limit exceeded for the current attribute. Usage of more than 100 query values on a single attribute is prohibited.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GENERAL_QUERY_INVALID => [
|
||||
'name' => Exception::GENERAL_QUERY_INVALID,
|
||||
'description' => 'The query\'s syntax is invalid. Please check the query and try again.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GENERAL_ROUTE_NOT_FOUND => [
|
||||
'name' => Exception::GENERAL_ROUTE_NOT_FOUND,
|
||||
'description' => 'The requested route was not found. Please refer to the docs and try again.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::GENERAL_CURSOR_NOT_FOUND => [
|
||||
'name' => Exception::GENERAL_CURSOR_NOT_FOUND,
|
||||
'description' => 'The cursor is invalid. This can happen if the item represented by the cursor has been deleted.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GENERAL_SERVER_ERROR => [
|
||||
'name' => Exception::GENERAL_SERVER_ERROR,
|
||||
'description' => 'An internal server error occurred.',
|
||||
'code' => 500,
|
||||
],
|
||||
|
||||
/** User Errors */
|
||||
Exception::USER_COUNT_EXCEEDED => [
|
||||
'name' => Exception::USER_COUNT_EXCEEDED,
|
||||
'description' => 'The current project has exceeded the maximum number of users. Please check your user limit in the Appwrite console.',
|
||||
'code' => 501,
|
||||
],
|
||||
Exception::USER_JWT_INVALID => [
|
||||
'name' => Exception::USER_JWT_INVALID,
|
||||
'description' => 'The JWT token is invalid. Please check the value of the X-Appwrite-JWT header to ensure the correct token is being used.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_ALREADY_EXISTS => [
|
||||
'name' => Exception::USER_ALREADY_EXISTS,
|
||||
'description' => 'A user with the same email ID already exists in your project.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::USER_BLOCKED => [
|
||||
'name' => Exception::USER_BLOCKED,
|
||||
'description' => 'The current user has been blocked. You can unblock the user from the Appwrite console.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_INVALID_TOKEN => [
|
||||
'name' => Exception::USER_INVALID_TOKEN,
|
||||
'description' => 'Invalid token passed in the request.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_PASSWORD_RESET_REQUIRED => [
|
||||
'name' => Exception::USER_PASSWORD_RESET_REQUIRED,
|
||||
'description' => 'The current user requires a password reset.',
|
||||
'code' => 412,
|
||||
],
|
||||
Exception::USER_EMAIL_NOT_WHITELISTED => [
|
||||
'name' => Exception::USER_EMAIL_NOT_WHITELISTED,
|
||||
'description' => 'The user\'s email is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_EMAILS environment variable of your Appwrite server.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_IP_NOT_WHITELISTED => [
|
||||
'name' => Exception::USER_IP_NOT_WHITELISTED,
|
||||
'description' => 'The user\'s IP address is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_IPS environment variable of your Appwrite server.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_INVALID_CREDENTIALS => [
|
||||
'name' => Exception::USER_INVALID_CREDENTIALS,
|
||||
'description' => 'Invalid credentials. Please check the email and password.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED => [
|
||||
'name' => Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED,
|
||||
'description' => 'Anonymous users cannot be created for the console project.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_SESSION_ALREADY_EXISTS => [
|
||||
'name' => Exception::USER_SESSION_ALREADY_EXISTS,
|
||||
'description' => 'Creation of anonymous users is prohibited when a session is active.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_NOT_FOUND => [
|
||||
'name' => Exception::USER_NOT_FOUND,
|
||||
'description' => 'User with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::USER_EMAIL_ALREADY_EXISTS => [
|
||||
'name' => Exception::USER_EMAIL_ALREADY_EXISTS,
|
||||
'description' => 'Another user with the same email already exists in the current project.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::USER_PASSWORD_MISMATCH => [
|
||||
'name' => Exception::USER_PASSWORD_MISMATCH,
|
||||
'description' => 'Passwords do not match. Please check the password and confirm password.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::USER_SESSION_NOT_FOUND => [
|
||||
'name' => Exception::USER_SESSION_NOT_FOUND,
|
||||
'description' => 'The current user session could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::USER_UNAUTHORIZED => [
|
||||
'name' => Exception::USER_UNAUTHORIZED,
|
||||
'description' => 'The current user is not authorized to perform the requested action.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_AUTH_METHOD_UNSUPPORTED => [
|
||||
'name' => Exception::USER_AUTH_METHOD_UNSUPPORTED,
|
||||
'description' => 'The requested authentication method is either disabled or unsupported. Please check the supported authentication methods in the Appwrite console.',
|
||||
'code' => 501,
|
||||
],
|
||||
|
||||
/** Teams */
|
||||
Exception::TEAM_NOT_FOUND => [
|
||||
'name' => Exception::TEAM_NOT_FOUND,
|
||||
'description' => 'Team with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::TEAM_INVITE_ALREADY_EXISTS => [
|
||||
'name' => Exception::TEAM_INVITE_ALREADY_EXISTS,
|
||||
'description' => 'The current user has already received an invitation to join the team.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::TEAM_INVITE_NOT_FOUND => [
|
||||
'name' => Exception::TEAM_INVITE_NOT_FOUND,
|
||||
'description' => 'The requested team invitation could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::TEAM_INVALID_SECRET => [
|
||||
'name' => Exception::TEAM_INVALID_SECRET,
|
||||
'description' => 'The team invitation secret is invalid.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::TEAM_MEMBERSHIP_MISMATCH => [
|
||||
'name' => Exception::TEAM_MEMBERSHIP_MISMATCH,
|
||||
'description' => 'The membership ID does not belong to the team ID.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::TEAM_INVITE_MISMATCH => [
|
||||
'name' => Exception::TEAM_INVITE_MISMATCH,
|
||||
'description' => 'The invite does not belong to the current user.',
|
||||
'code' => 401,
|
||||
],
|
||||
|
||||
|
||||
/** Membership */
|
||||
Exception::MEMBERSHIP_NOT_FOUND => [
|
||||
'name' => Exception::MEMBERSHIP_NOT_FOUND,
|
||||
'description' => 'Membership with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Avatars */
|
||||
Exception::AVATAR_SET_NOT_FOUND => [
|
||||
'name' => Exception::AVATAR_SET_NOT_FOUND,
|
||||
'description' => 'The requested avatar set could not be found.',
|
||||
'code' => 404
|
||||
],
|
||||
Exception::AVATAR_NOT_FOUND => [
|
||||
'name' => Exception::AVATAR_NOT_FOUND,
|
||||
'description' => 'The request avatar could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::AVATAR_IMAGE_NOT_FOUND => [
|
||||
'name' => Exception::AVATAR_IMAGE_NOT_FOUND,
|
||||
'description' => 'The requested image was not found at the URL.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::AVATAR_REMOTE_URL_FAILED => [
|
||||
'name' => Exception::AVATAR_REMOTE_URL_FAILED,
|
||||
'description' => 'Failed to fetch favicon from the requested URL.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::AVATAR_ICON_NOT_FOUND => [
|
||||
'name' => Exception::AVATAR_ICON_NOT_FOUND,
|
||||
'description' => 'The requested favicon could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Storage */
|
||||
Exception::STORAGE_FILE_NOT_FOUND => [
|
||||
'name' => Exception::STORAGE_FILE_NOT_FOUND,
|
||||
'description' => 'The requested file could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::STORAGE_DEVICE_NOT_FOUND => [
|
||||
'name' => Exception::STORAGE_DEVICE_NOT_FOUND,
|
||||
'description' => 'The requested storage device could not be found.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::STORAGE_FILE_EMPTY => [
|
||||
'name' => Exception::STORAGE_FILE_EMPTY,
|
||||
'description' => 'Empty file passed to the endpoint.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::STORAGE_FILE_TYPE_UNSUPPORTED => [
|
||||
'name' => Exception::STORAGE_FILE_TYPE_UNSUPPORTED,
|
||||
'description' => 'The file type is not supported.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::STORAGE_INVALID_FILE_SIZE => [
|
||||
'name' => Exception::STORAGE_INVALID_FILE_SIZE,
|
||||
'description' => 'The file size is either not valid or exceeds the maximum allowed size. Please check the file or the value of the _APP_STORAGE_LIMIT environment variable.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::STORAGE_INVALID_FILE => [
|
||||
'name' => Exception::STORAGE_INVALID_FILE,
|
||||
'description' => 'The uploaded file is invalid. Please check the file and try again.',
|
||||
'code' => 403,
|
||||
],
|
||||
Exception::STORAGE_BUCKET_ALREADY_EXISTS => [
|
||||
'name' => Exception::STORAGE_BUCKET_ALREADY_EXISTS,
|
||||
'description' => 'A storage bucket with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::STORAGE_BUCKET_NOT_FOUND => [
|
||||
'name' => Exception::STORAGE_BUCKET_NOT_FOUND,
|
||||
'description' => 'Storage bucket with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::STORAGE_INVALID_CONTENT_RANGE => [
|
||||
'name' => Exception::STORAGE_INVALID_CONTENT_RANGE,
|
||||
'description' => 'The content range is invalid. Please check the value of the Content-Range header.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::STORAGE_INVALID_RANGE => [
|
||||
'name' => Exception::STORAGE_INVALID_RANGE,
|
||||
'description' => 'The requested range is not satisfiable. Please check the value of the Range header.',
|
||||
'code' => 416,
|
||||
],
|
||||
|
||||
/** Functions */
|
||||
Exception::FUNCTION_NOT_FOUND => [
|
||||
'name' => Exception::FUNCTION_NOT_FOUND,
|
||||
'description' => 'Function with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Deployments */
|
||||
Exception::DEPLOYMENT_NOT_FOUND => [
|
||||
'name' => Exception::DEPLOYMENT_NOT_FOUND,
|
||||
'description' => 'Deployment with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Executions */
|
||||
Exception::EXECUTION_NOT_FOUND => [
|
||||
'name' => Exception::EXECUTION_NOT_FOUND,
|
||||
'description' => 'Execution with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Collections */
|
||||
Exception::COLLECTION_NOT_FOUND => [
|
||||
'name' => Exception::COLLECTION_NOT_FOUND,
|
||||
'description' => 'Collection with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::COLLECTION_ALREADY_EXISTS => [
|
||||
'name' => Exception::COLLECTION_ALREADY_EXISTS,
|
||||
'description' => 'A collection with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::COLLECTION_LIMIT_EXCEEDED => [
|
||||
'name' => Exception::COLLECTION_LIMIT_EXCEEDED,
|
||||
'description' => 'The maximum number of collections has been reached.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Documents */
|
||||
Exception::DOCUMENT_NOT_FOUND => [
|
||||
'name' => Exception::DOCUMENT_NOT_FOUND,
|
||||
'description' => 'Document with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::DOCUMENT_INVALID_STRUCTURE => [
|
||||
'name' => Exception::DOCUMENT_INVALID_STRUCTURE,
|
||||
'description' => 'The document structure is invalid. Please ensure the attributes match the collection definition.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::DOCUMENT_MISSING_PAYLOAD => [
|
||||
'name' => Exception::DOCUMENT_MISSING_PAYLOAD,
|
||||
'description' => 'The document payload is missing.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::DOCUMENT_ALREADY_EXISTS => [
|
||||
'name' => Exception::DOCUMENT_ALREADY_EXISTS,
|
||||
'description' => 'Document with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Attributes */
|
||||
Exception::ATTRIBUTE_NOT_FOUND => [
|
||||
'name' => Exception::ATTRIBUTE_NOT_FOUND,
|
||||
'description' => 'Attribute with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::ATTRIBUTE_UNKNOWN => [
|
||||
'name' => Exception::ATTRIBUTE_UNKNOWN,
|
||||
'description' => 'The attribute required for the index could not be found. Please confirm all your attributes are in the <span class="tag">available</span> state.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_NOT_AVAILABLE => [
|
||||
'name' => Exception::ATTRIBUTE_NOT_AVAILABLE,
|
||||
'description' => 'The requested attribute is not yet <span class="tag">available</span>. Please try again later.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_FORMAT_UNSUPPORTED => [
|
||||
'name' => Exception::ATTRIBUTE_FORMAT_UNSUPPORTED,
|
||||
'description' => 'The requested attribute format is not supported.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED => [
|
||||
'name' => Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED,
|
||||
'description' => 'Default values cannot be set for <span class="tag">array</span> and <span class="tag">required</span> attributes.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_ALREADY_EXISTS => [
|
||||
'name' => Exception::ATTRIBUTE_ALREADY_EXISTS,
|
||||
'description' => 'Attribute with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::ATTRIBUTE_LIMIT_EXCEEDED => [
|
||||
'name' => Exception::ATTRIBUTE_LIMIT_EXCEEDED,
|
||||
'description' => 'The maximum number of attributes has been reached.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_VALUE_INVALID => [
|
||||
'name' => Exception::ATTRIBUTE_VALUE_INVALID,
|
||||
'description' => 'The attribute value is invalid. Please check the type, range and value of the attribute.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Indexes */
|
||||
Exception::INDEX_NOT_FOUND => [
|
||||
'name' => Exception::INDEX_NOT_FOUND,
|
||||
'description' => 'Index with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::INDEX_LIMIT_EXCEEDED => [
|
||||
'name' => Exception::INDEX_LIMIT_EXCEEDED,
|
||||
'description' => 'The maximum number of indexes has been reached.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::INDEX_ALREADY_EXISTS => [
|
||||
'name' => Exception::INDEX_ALREADY_EXISTS,
|
||||
'description' => 'Index with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Project Errors */
|
||||
Exception::PROJECT_NOT_FOUND => [
|
||||
'name' => Exception::PROJECT_NOT_FOUND,
|
||||
'description' => 'Project with the requested ID could not be found. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::PROJECT_UNKNOWN => [
|
||||
'name' => Exception::PROJECT_UNKNOWN,
|
||||
'description' => 'The project ID is either missing or not valid. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::PROJECT_PROVIDER_DISABLED => [
|
||||
'name' => Exception::PROJECT_PROVIDER_DISABLED,
|
||||
'description' => 'The chosen OAuth provider is disabled. You can enable the OAuth provider using the Appwrite console.',
|
||||
'code' => 412,
|
||||
],
|
||||
Exception::PROJECT_PROVIDER_UNSUPPORTED => [
|
||||
'name' => Exception::PROJECT_PROVIDER_UNSUPPORTED,
|
||||
'description' => 'The chosen OAuth provider is unsupported. Please check <a href="/docs/client/account?sdk=web-default#accountCreateOAuth2Session"> the docs</a> for the complete list of supported OAuth providers.',
|
||||
'code' => 501,
|
||||
],
|
||||
Exception::PROJECT_INVALID_SUCCESS_URL => [
|
||||
'name' => Exception::PROJECT_INVALID_SUCCESS_URL,
|
||||
'description' => 'Invalid URL received for OAuth success redirect.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::PROJECT_INVALID_FAILURE_URL => [
|
||||
'name' => Exception::PROJECT_INVALID_FAILURE_URL,
|
||||
'description' => 'Invalid URL received for OAuth failure redirect.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::PROJECT_MISSING_USER_ID => [
|
||||
'name' => Exception::PROJECT_MISSING_USER_ID,
|
||||
'description' => 'Failed to obtain user ID from the OAuth provider.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::WEBHOOK_NOT_FOUND => [
|
||||
'name' => Exception::WEBHOOK_NOT_FOUND,
|
||||
'description' => 'Webhook with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::KEY_NOT_FOUND => [
|
||||
'name' => Exception::KEY_NOT_FOUND,
|
||||
'description' => 'Key with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::PLATFORM_NOT_FOUND => [
|
||||
'name' => Exception::PLATFORM_NOT_FOUND,
|
||||
'description' => 'Platform with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::DOMAIN_NOT_FOUND => [
|
||||
'name' => Exception::DOMAIN_NOT_FOUND,
|
||||
'description' => 'Domain with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::DOMAIN_ALREADY_EXISTS => [
|
||||
'name' => Exception::DOMAIN_ALREADY_EXISTS,
|
||||
'description' => 'A Domain with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::DOMAIN_VERIFICATION_FAILED => [
|
||||
'name' => Exception::DOMAIN_VERIFICATION_FAILED,
|
||||
'description' => 'Domain verification for the requested domain has failed.',
|
||||
'code' => 401,
|
||||
]
|
||||
];
|
||||
|
|
@ -192,6 +192,21 @@ return [
|
|||
'model' => Response::MODEL_FILE,
|
||||
'note' => '',
|
||||
],
|
||||
'storage.buckets.create' => [
|
||||
'description' => 'This event triggers when a storage bucket is created.',
|
||||
'model' => Response::MODEL_BUCKET,
|
||||
'note' => '',
|
||||
],
|
||||
'storage.buckets.update' => [
|
||||
'description' => 'This event triggers when a storage bucket is updated.',
|
||||
'model' => Response::MODEL_BUCKET,
|
||||
'note' => '',
|
||||
],
|
||||
'storage.buckets.delete' => [
|
||||
'description' => 'This event triggers when a storage bucket is deleted.',
|
||||
'model' => Response::MODEL_BUCKET,
|
||||
'note' => '',
|
||||
],
|
||||
'users.create' => [
|
||||
'description' => 'This event triggers when a user is created from the users API.',
|
||||
'model' => Response::MODEL_USER,
|
||||
|
|
|
|||
|
|
@ -205,6 +205,24 @@ return [
|
|||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'master',
|
||||
],
|
||||
[
|
||||
'key' => 'node-cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '0.13.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_PLATFORM_CONSOLE,
|
||||
'prism' => 'bash',
|
||||
'source' => \realpath(__DIR__ . '/../sdks/server-cli'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-cli.git',
|
||||
'gitRepoName' => 'sdk-for-node-cli',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'master',
|
||||
],
|
||||
[
|
||||
'key' => 'deno',
|
||||
'name' => 'Deno',
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ return [ // Ordered by ABC.
|
|||
'github' => [
|
||||
'name' => 'GitHub',
|
||||
'developers' => 'https://developer.github.com/',
|
||||
'icon' => 'icon-github-circled',
|
||||
'icon' => 'icon-github',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ $admins = [
|
|||
'documents.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
'buckets.read',
|
||||
'buckets.write',
|
||||
'users.read',
|
||||
'users.write',
|
||||
'collections.read',
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ return [ // List of publicly visible scopes
|
|||
'files.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s storage files',
|
||||
],
|
||||
'buckets.read' => [
|
||||
'description' => 'Access to read your project\'s storage buckets',
|
||||
],
|
||||
'buckets.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s storage buckets',
|
||||
],
|
||||
'functions.read' => [
|
||||
'description' => 'Access to read your project\'s functions and code tags',
|
||||
],
|
||||
|
|
|
|||
1
app/config/specs/open-api3-0.13.x-client.json
Normal file
1
app/config/specs/open-api3-0.13.x-console.json
Normal file
1
app/config/specs/open-api3-0.13.x-server.json
Normal file
1
app/config/specs/swagger2-0.13.x-client.json
Normal file
1
app/config/specs/swagger2-0.13.x-console.json
Normal file
1
app/config/specs/swagger2-0.13.x-server.json
Normal file
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
||||
'default' => __DIR__.'/logos/none.png',
|
||||
'default_image' => __DIR__.'/logos/image.png',
|
||||
|
||||
// Video Files
|
||||
'video/mp4' => __DIR__.'/logos/video.png',
|
||||
|
|
@ -14,30 +15,30 @@ return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
|||
'video/x-ms-wmv' => __DIR__.'/logos/video.png',
|
||||
|
||||
// // Microsoft Word
|
||||
'application/msword' => __DIR__.'/logos/word.png',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => __DIR__.'/logos/word.png',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => __DIR__.'/logos/word.png',
|
||||
'application/vnd.ms-word.document.macroEnabled.12' => __DIR__.'/logos/word.png',
|
||||
// 'application/msword' => __DIR__.'/logos/word.png',
|
||||
// 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => __DIR__.'/logos/word.png',
|
||||
// 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => __DIR__.'/logos/word.png',
|
||||
// 'application/vnd.ms-word.document.macroEnabled.12' => __DIR__.'/logos/word.png',
|
||||
|
||||
// // Microsoft Excel
|
||||
'application/vnd.ms-excel' => __DIR__.'/logos/excel.png',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => __DIR__.'/logos/excel.png',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => __DIR__.'/logos/excel.png',
|
||||
'application/vnd.ms-excel.sheet.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
'application/vnd.ms-excel.template.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
'application/vnd.ms-excel.addin.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
// 'application/vnd.ms-excel' => __DIR__.'/logos/excel.png',
|
||||
// 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => __DIR__.'/logos/excel.png',
|
||||
// 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => __DIR__.'/logos/excel.png',
|
||||
// 'application/vnd.ms-excel.sheet.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
// 'application/vnd.ms-excel.template.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
// 'application/vnd.ms-excel.addin.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
// 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => __DIR__.'/logos/excel.png',
|
||||
|
||||
// // Microsoft Power Point
|
||||
'application/vnd.ms-powerpoint' => __DIR__.'/logos/ppt.png',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => __DIR__.'/logos/ppt.png',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template' => __DIR__.'/logos/ppt.png',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => __DIR__.'/logos/ppt.png',
|
||||
'application/vnd.ms-powerpoint.addin.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
'application/vnd.ms-powerpoint.template.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.ms-powerpoint' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.openxmlformats-officedocument.presentationml.template' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.ms-powerpoint.addin.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.ms-powerpoint.template.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
// 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => __DIR__.'/logos/ppt.png',
|
||||
|
||||
// Adobe PDF
|
||||
'application/pdf' => __DIR__.'/logos/pdf.png',
|
||||
// 'application/pdf' => __DIR__.'/logos/pdf.png',
|
||||
];
|
||||
|
Before Width: | Height: | Size: 16 KiB |
BIN
app/config/storage/logos/image.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
|
@ -420,6 +420,78 @@ return [
|
|||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE',
|
||||
'description' => 'Select default storage device. The default value is \'Local\'. List of supported adapters are \'Local\', \'S3\' and \'DOSpaces\'.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'Local',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_ACCESS_KEY',
|
||||
'description' => 'AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_SECRET',
|
||||
'description' => 'AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_REGION',
|
||||
'description' => 'AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'us-eas-1',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_BUCKET',
|
||||
'description' => 'AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY',
|
||||
'description' => 'DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_SECRET',
|
||||
'description' => 'DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_REGION',
|
||||
'description' => 'DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'us-eas-1',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_BUCKET',
|
||||
'description' => 'DigitalOcean spaces bucket. Required when storage adapter is set to DOSpaces. You can create spaces in your DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use Utopia\Database\Exception\Duplicate;
|
|||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
|
@ -69,11 +69,11 @@ App::post('/v1/account')
|
|||
$whitelistIPs = $project->getAttribute('authWhitelistIPs');
|
||||
|
||||
if (!empty($whitelistEmails) && !\in_array($email, $whitelistEmails)) {
|
||||
throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401);
|
||||
throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401, Exception::USER_EMAIL_NOT_WHITELISTED);
|
||||
}
|
||||
|
||||
if (!empty($whitelistIPs) && !\in_array($request->getIP(), $whitelistIPs)) {
|
||||
throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401);
|
||||
throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401, Exception::USER_IP_NOT_WHITELISTED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ App::post('/v1/account')
|
|||
], APP_LIMIT_USERS);
|
||||
|
||||
if ($sum >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ App::post('/v1/account')
|
|||
'deleted' => false
|
||||
])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
Authorization::unsetRole('role:' . Auth::USER_ROLE_GUEST);
|
||||
|
|
@ -176,11 +176,11 @@ App::post('/v1/account/sessions')
|
|||
->setParam('resource', 'user/'.($profile ? $profile->getId() : ''))
|
||||
;
|
||||
|
||||
throw new Exception('Invalid credentials', 401); // Wrong password or username
|
||||
throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); // Wrong password or username
|
||||
}
|
||||
|
||||
if (false === $profile->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception('Invalid credentials. User is blocked', 401); // User is in status blocked
|
||||
throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); // User is in status blocked
|
||||
}
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
|
|
@ -283,13 +283,13 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
|||
}
|
||||
|
||||
if (empty($appId) || empty($appSecret)) {
|
||||
throw new Exception('This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.', 412);
|
||||
throw new Exception('This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.', 412, Exception::PROJECT_PROVIDER_DISABLED);
|
||||
}
|
||||
|
||||
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
|
||||
|
||||
if (!\class_exists($className)) {
|
||||
throw new Exception('Provider is not supported', 501);
|
||||
throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if(empty($success)) {
|
||||
|
|
@ -407,7 +407,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
||||
|
||||
if (!\class_exists($className)) {
|
||||
throw new Exception('Provider is not supported', 501);
|
||||
throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$oauth2 = new $className($appId, $appSecret, $callback);
|
||||
|
|
@ -416,18 +416,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
try {
|
||||
$state = \array_merge($defaultState, $oauth2->parseState($state));
|
||||
} catch (\Exception$exception) {
|
||||
throw new Exception('Failed to parse login state params as passed from OAuth2 provider');
|
||||
throw new Exception('Failed to parse login state params as passed from OAuth2 provider', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
$state = $defaultState;
|
||||
}
|
||||
|
||||
if (!$validateURL->isValid($state['success'])) {
|
||||
throw new Exception('Invalid redirect URL for success login', 400);
|
||||
throw new Exception('Invalid redirect URL for success login', 400, Exception::PROJECT_INVALID_SUCCESS_URL);
|
||||
}
|
||||
|
||||
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
|
||||
throw new Exception('Invalid redirect URL for failure login', 400);
|
||||
throw new Exception('Invalid redirect URL for failure login', 400, Exception::PROJECT_INVALID_FAILURE_URL);
|
||||
}
|
||||
|
||||
$state['failure'] = null;
|
||||
|
|
@ -441,7 +441,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
$response->redirect($state['failure'], 301, 0);
|
||||
}
|
||||
|
||||
throw new Exception('Failed to obtain access token');
|
||||
throw new Exception('Failed to obtain access token', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$oauth2ID = $oauth2->getUserID($accessToken);
|
||||
|
|
@ -451,7 +451,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
$response->redirect($state['failure'], 301, 0);
|
||||
}
|
||||
|
||||
throw new Exception('Missing ID from OAuth2 provider', 400);
|
||||
throw new Exception('Missing ID from OAuth2 provider', 400, Exception::PROJECT_MISSING_USER_ID);
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
|
@ -486,7 +486,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
$sum = $dbForProject->count('users', [ new Query('deleted', Query::TYPE_EQUAL, [false]),], APP_LIMIT_USERS);
|
||||
|
||||
if ($sum >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -512,13 +512,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
'deleted' => false
|
||||
])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $user->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception('Invalid credentials. User is blocked', 401); // User is in status blocked
|
||||
throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); // User is in status blocked
|
||||
}
|
||||
|
||||
// Create session token, verify user account and update OAuth2 ID and Access Token
|
||||
|
|
@ -643,7 +643,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
/** @var Appwrite\Event\Event $mails */
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503);
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
|
|
@ -661,7 +661,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
], APP_LIMIT_USERS);
|
||||
|
||||
if ($sum >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -711,7 +711,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed to save user to DB', 500);
|
||||
throw new Exception('Failed to save user to DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if(empty($url)) {
|
||||
|
|
@ -789,13 +789,13 @@ App::put('/v1/account/sessions/magic-url')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$token = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_MAGIC_URL, $secret);
|
||||
|
||||
if (!$token) {
|
||||
throw new Exception('Invalid login token', 401);
|
||||
throw new Exception('Invalid login token', 401, Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
|
|
@ -845,7 +845,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$audits
|
||||
|
|
@ -918,11 +918,11 @@ App::post('/v1/account/sessions/anonymous')
|
|||
$protocol = $request->getProtocol();
|
||||
|
||||
if ('console' === $project->getId()) {
|
||||
throw new Exception('Failed to create anonymous user.', 401);
|
||||
throw new Exception('Failed to create anonymous user.', 401, Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED);
|
||||
}
|
||||
|
||||
if (!$user->isEmpty()) {
|
||||
throw new Exception('Cannot create an anonymous user when logged in.', 401);
|
||||
throw new Exception('Cannot create an anonymous user when logged in.', 401, Exception::USER_SESSION_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
|
@ -933,7 +933,7 @@ App::post('/v1/account/sessions/anonymous')
|
|||
], APP_LIMIT_USERS);
|
||||
|
||||
if ($sum >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1057,7 +1057,7 @@ App::post('/v1/account/jwt')
|
|||
}
|
||||
|
||||
if ($current->isEmpty()) {
|
||||
throw new Exception('No valid session found', 401);
|
||||
throw new Exception('No valid session found', 404, Exception::USER_SESSION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
|
||||
|
|
@ -1303,7 +1303,7 @@ App::get('/v1/account/sessions/:sessionId')
|
|||
}
|
||||
}
|
||||
|
||||
throw new Exception('Session not found', 404);
|
||||
throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/name')
|
||||
|
|
@ -1377,7 +1377,7 @@ App::patch('/v1/account/password')
|
|||
|
||||
// Check old password only if its an existing user.
|
||||
if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password
|
||||
throw new Exception('Invalid credentials', 401);
|
||||
throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user
|
||||
|
|
@ -1429,14 +1429,14 @@ App::patch('/v1/account/email')
|
|||
!$isAnonymousUser &&
|
||||
!Auth::passwordVerify($password, $user->getAttribute('password'))
|
||||
) { // Double check user password
|
||||
throw new Exception('Invalid credentials', 401);
|
||||
throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
$email = \strtolower($email);
|
||||
$profile = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
|
||||
|
||||
if ($profile) {
|
||||
throw new Exception('User already registered', 409);
|
||||
throw new Exception('User already registered', 409, Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -1447,7 +1447,7 @@ App::patch('/v1/account/email')
|
|||
->setAttribute('search', implode(' ', [$user->getId(), $user->getAttribute('name'), $user->getAttribute('email')]))
|
||||
);
|
||||
} catch(Duplicate $th) {
|
||||
throw new Exception('Email already exists', 409);
|
||||
throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$audits
|
||||
|
|
@ -1651,7 +1651,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
|||
}
|
||||
}
|
||||
|
||||
throw new Exception('Session not found', 404);
|
||||
throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/sessions/:sessionId')
|
||||
|
|
@ -1873,7 +1873,7 @@ App::post('/v1/account/recovery')
|
|||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503);
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
|
|
@ -1884,11 +1884,11 @@ App::post('/v1/account/recovery')
|
|||
$profile = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
|
||||
|
||||
if (!$profile) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (false === $profile->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception('Invalid credentials. User is blocked', 401);
|
||||
throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED);
|
||||
}
|
||||
|
||||
$expire = \time() + Auth::TOKEN_EXPIRATION_RECOVERY;
|
||||
|
|
@ -1979,20 +1979,20 @@ App::put('/v1/account/recovery')
|
|||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
if ($password !== $passwordAgain) {
|
||||
throw new Exception('Passwords must match', 400);
|
||||
throw new Exception('Passwords must match', 400, Exception::USER_PASSWORD_MISMATCH);
|
||||
}
|
||||
|
||||
$profile = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($profile->isEmpty() || $profile->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$tokens = $profile->getAttribute('tokens', []);
|
||||
$recovery = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_RECOVERY, $secret);
|
||||
|
||||
if (!$recovery) {
|
||||
throw new Exception('Invalid recovery token', 401);
|
||||
throw new Exception('Invalid recovery token', 401, Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
|
|
@ -2066,7 +2066,7 @@ App::post('/v1/account/verification')
|
|||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503);
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
|
|
@ -2164,14 +2164,14 @@ App::put('/v1/account/verification')
|
|||
$profile = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($profile->isEmpty()) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$tokens = $profile->getAttribute('tokens', []);
|
||||
$verification = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_VERIFICATION, $secret);
|
||||
|
||||
if (!$verification) {
|
||||
throw new Exception('Invalid verification token', 401);
|
||||
throw new Exception('Invalid verification token', 401, Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use Utopia\App;
|
|||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Image\Image;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\HexColor;
|
||||
|
|
@ -25,15 +25,15 @@ $avatarCallback = function ($type, $code, $width, $height, $quality, $response)
|
|||
$set = Config::getParam('avatar-' . $type, []);
|
||||
|
||||
if (empty($set)) {
|
||||
throw new Exception('Avatar set not found', 404);
|
||||
throw new Exception('Avatar set not found', 404, Exception::AVATAR_SET_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!\array_key_exists($code, $set)) {
|
||||
throw new Exception('Avatar not found', 404);
|
||||
throw new Exception('Avatar not found', 404, Exception::AVATAR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!\extension_loaded('imagick')) {
|
||||
throw new Exception('Imagick extension is missing', 500);
|
||||
throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$output = 'png';
|
||||
|
|
@ -43,7 +43,7 @@ $avatarCallback = function ($type, $code, $width, $height, $quality, $response)
|
|||
$type = 'png';
|
||||
|
||||
if (!\is_readable($path)) {
|
||||
throw new Exception('File not readable in ' . $path, 500);
|
||||
throw new Exception('File not readable in ' . $path, 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-0')); // Limit file number or size
|
||||
|
|
@ -169,19 +169,19 @@ App::get('/v1/avatars/image')
|
|||
}
|
||||
|
||||
if (!\extension_loaded('imagick')) {
|
||||
throw new Exception('Imagick extension is missing', 500);
|
||||
throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$fetch = @\file_get_contents($url, false);
|
||||
|
||||
if (!$fetch) {
|
||||
throw new Exception('Image not found', 404);
|
||||
throw new Exception('Image not found', 404, Exception::AVATAR_IMAGE_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$image = new Image($fetch);
|
||||
} catch (\Exception$exception) {
|
||||
throw new Exception('Unable to parse image', 500);
|
||||
throw new Exception('Unable to parse image', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$image->crop((int) $width, (int) $height);
|
||||
|
|
@ -238,7 +238,7 @@ App::get('/v1/avatars/favicon')
|
|||
}
|
||||
|
||||
if (!\extension_loaded('imagick')) {
|
||||
throw new Exception('Imagick extension is missing', 500);
|
||||
throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$curl = \curl_init();
|
||||
|
|
@ -259,7 +259,7 @@ App::get('/v1/avatars/favicon')
|
|||
\curl_close($curl);
|
||||
|
||||
if (!$html) {
|
||||
throw new Exception('Failed to fetch remote URL', 404);
|
||||
throw new Exception('Failed to fetch remote URL', 404, Exception::AVATAR_REMOTE_URL_FAILED);
|
||||
}
|
||||
|
||||
$doc = new DOMDocument();
|
||||
|
|
@ -317,7 +317,7 @@ App::get('/v1/avatars/favicon')
|
|||
$data = @\file_get_contents($outputHref, false);
|
||||
|
||||
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
|
||||
throw new Exception('Favicon not found', 404);
|
||||
throw new Exception('Favicon not found', 404, Exception::AVATAR_ICON_NOT_FOUND);
|
||||
}
|
||||
|
||||
$cache->save($key, $data);
|
||||
|
|
@ -333,7 +333,7 @@ App::get('/v1/avatars/favicon')
|
|||
$fetch = @\file_get_contents($outputHref, false);
|
||||
|
||||
if (!$fetch) {
|
||||
throw new Exception('Icon not found', 404);
|
||||
throw new Exception('Icon not found', 404, Exception::AVATAR_ICON_NOT_FOUND);
|
||||
}
|
||||
|
||||
$image = new Image($fetch);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\FloatValidator;
|
||||
|
|
@ -65,22 +65,22 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($format)) {
|
||||
if (!Structure::hasFormat($format, $type)) {
|
||||
throw new Exception("Format {$format} not available for {$type} attributes.", 400);
|
||||
throw new Exception("Format {$format} not available for {$type} attributes.", 400, Exception::ATTRIBUTE_FORMAT_UNSUPPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
// Must throw here since dbForProject->createAttribute is performed by db worker
|
||||
if ($required && $default) {
|
||||
throw new Exception('Cannot set default value for required attribute', 400);
|
||||
throw new Exception('Cannot set default value for required attribute', 400, Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if ($array && $default) {
|
||||
throw new Exception('Cannot set default value for array attributes', 400);
|
||||
throw new Exception('Cannot set default value for array attributes', 400, Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -104,10 +104,10 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
|
|||
$attribute = $dbForProject->createDocument('attributes', $attribute);
|
||||
}
|
||||
catch (DuplicateException $exception) {
|
||||
throw new Exception('Attribute already exists', 409);
|
||||
throw new Exception('Attribute already exists', 409, Exception::ATTRIBUTE_ALREADY_EXISTS);
|
||||
}
|
||||
catch (LimitException $exception) {
|
||||
throw new Exception('Attribute limit exceeded', 400);
|
||||
throw new Exception('Attribute limit exceeded', 400, Exception::ATTRIBUTE_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('collections', $collectionId);
|
||||
|
|
@ -181,9 +181,9 @@ App::post('/v1/database/collections')
|
|||
|
||||
$dbForProject->createCollection('collection_' . $collection->getInternalId());
|
||||
} catch (DuplicateException $th) {
|
||||
throw new Exception('Collection already exists', 409);
|
||||
throw new Exception('Collection already exists', 409, Exception::COLLECTION_ALREADY_EXISTS);
|
||||
} catch (LimitException $th) {
|
||||
throw new Exception('Collection limit exceeded', 400);
|
||||
throw new Exception('Collection limit exceeded', 400, Exception::COLLECTION_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
$audits
|
||||
|
|
@ -226,7 +226,7 @@ App::get('/v1/database/collections')
|
|||
$cursorCollection = $dbForProject->getDocument('collections', $cursor);
|
||||
|
||||
if ($cursorCollection->isEmpty()) {
|
||||
throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ App::get('/v1/database/collections/:collectionId')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage->setParam('database.collections.read', 1);
|
||||
|
|
@ -407,7 +407,7 @@ App::get('/v1/database/:collectionId/usage')
|
|||
$collection = $dbForProject->getCollection('collection_' . $collectionDocument->getInternalId());
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage = [];
|
||||
|
|
@ -519,7 +519,7 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
$collection = $dbForProject->getCollection('collection_' . $collectionDocument->getInternalId());
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
|
|
@ -608,7 +608,7 @@ App::put('/v1/database/collections/:collectionId')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$read ??= $collection->getRead() ?? []; // By default inherit read permissions
|
||||
|
|
@ -627,10 +627,10 @@ App::put('/v1/database/collections/:collectionId')
|
|||
);
|
||||
}
|
||||
catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
catch (StructureException $exception) {
|
||||
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
|
||||
throw new Exception('Bad structure. '.$exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
|
||||
$usage->setParam('database.collections.update', 1);
|
||||
|
|
@ -672,11 +672,11 @@ App::delete('/v1/database/collections/:collectionId')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteDocument('collections', $collectionId)) {
|
||||
throw new Exception('Failed to remove collection from DB', 500);
|
||||
throw new Exception('Failed to remove collection from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
|
||||
|
|
@ -734,7 +734,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
|
|||
// Ensure attribute default is within required size
|
||||
$validator = new Text($size);
|
||||
if (!is_null($default) && !$validator->isValid($default)) {
|
||||
throw new Exception($validator->getDescription(), 400);
|
||||
throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
}
|
||||
|
||||
$attribute = createAttribute($collectionId, new Document([
|
||||
|
|
@ -826,14 +826,14 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
|
|||
foreach ($elements as $element) {
|
||||
$length = \strlen($element);
|
||||
if ($length === 0) {
|
||||
throw new Exception('Each enum element must not be empty', 400);
|
||||
throw new Exception('Each enum element must not be empty', 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
|
||||
}
|
||||
$size = ($length > $size) ? $length : $size;
|
||||
}
|
||||
|
||||
if (!is_null($default) && !in_array($default, $elements)) {
|
||||
throw new Exception('Default value not found in elements', 400);
|
||||
throw new Exception('Default value not found in elements', 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
}
|
||||
|
||||
$attribute = createAttribute($collectionId, new Document([
|
||||
|
|
@ -969,13 +969,13 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
|
|||
$max = (is_null($max)) ? PHP_INT_MAX : \intval($max);
|
||||
|
||||
if ($min > $max) {
|
||||
throw new Exception('Minimum value must be lesser than maximum value', 400);
|
||||
throw new Exception('Minimum value must be lesser than maximum value', 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
}
|
||||
|
||||
$validator = new Range($min, $max, Database::VAR_INTEGER);
|
||||
|
||||
if (!is_null($default) && !$validator->isValid($default)) {
|
||||
throw new Exception($validator->getDescription(), 400);
|
||||
throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
}
|
||||
|
||||
$size = $max > 2147483647 ? 8 : 4; // Automatically create BigInt depending on max value
|
||||
|
|
@ -1040,7 +1040,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
|||
$max = (is_null($max)) ? PHP_FLOAT_MAX : \floatval($max);
|
||||
|
||||
if ($min > $max) {
|
||||
throw new Exception('Minimum value must be lesser than maximum value', 400);
|
||||
throw new Exception('Minimum value must be lesser than maximum value', 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
}
|
||||
|
||||
// Ensure default value is a float
|
||||
|
|
@ -1051,7 +1051,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
|||
$validator = new Range($min, $max, Database::VAR_FLOAT);
|
||||
|
||||
if (!is_null($default) && !$validator->isValid($default)) {
|
||||
throw new Exception($validator->getDescription(), 400);
|
||||
throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
}
|
||||
|
||||
$attribute = createAttribute($collectionId, new Document([
|
||||
|
|
@ -1141,7 +1141,7 @@ App::get('/v1/database/collections/:collectionId/attributes')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$attributes = $collection->getAttribute('attributes');
|
||||
|
|
@ -1185,13 +1185,13 @@ App::get('/v1/database/collections/:collectionId/attributes/:key')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$attribute = $dbForProject->getDocument('attributes', $collectionId.'_'.$key);
|
||||
|
||||
if ($attribute->isEmpty()) {
|
||||
throw new Exception('Attribute not found', 404);
|
||||
throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Select response model based on type and format
|
||||
|
|
@ -1247,13 +1247,13 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$attribute = $dbForProject->getDocument('attributes', $collectionId.'_'.$key);
|
||||
|
||||
if ($attribute->isEmpty()) {
|
||||
throw new Exception('Attribute not found', 404);
|
||||
throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Only update status if removing available attribute
|
||||
|
|
@ -1335,7 +1335,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$count = $dbForProject->count('indexes', [
|
||||
|
|
@ -1345,7 +1345,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
$limit = 64 - MariaDB::getNumberOfDefaultIndexes();
|
||||
|
||||
if ($count >= $limit) {
|
||||
throw new Exception('Index limit exceeded', 400);
|
||||
throw new Exception('Index limit exceeded', 400, Exception::INDEX_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
// Convert Document[] to array of attribute metadata
|
||||
|
|
@ -1359,7 +1359,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
$attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key'));
|
||||
|
||||
if ($attributeIndex === false) {
|
||||
throw new Exception('Unknown attribute: ' . $attribute, 400);
|
||||
throw new Exception('Unknown attribute: ' . $attribute, 400, Exception::ATTRIBUTE_UNKNOWN);
|
||||
}
|
||||
|
||||
$attributeStatus = $oldAttributes[$attributeIndex]['status'];
|
||||
|
|
@ -1368,7 +1368,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
|
||||
// ensure attribute is available
|
||||
if ($attributeStatus !== 'available') {
|
||||
throw new Exception ('Attribute not available: ' . $oldAttributes[$attributeIndex]['key'], 400);
|
||||
throw new Exception ('Attribute not available: ' . $oldAttributes[$attributeIndex]['key'], 400, Exception::ATTRIBUTE_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
// set attribute size as index length only for strings
|
||||
|
|
@ -1387,7 +1387,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
'orders' => $orders,
|
||||
]));
|
||||
} catch (DuplicateException $th) {
|
||||
throw new Exception('Index already exists', 409);
|
||||
throw new Exception('Index already exists', 409, Exception::INDEX_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('collections', $collectionId);
|
||||
|
|
@ -1432,7 +1432,7 @@ App::get('/v1/database/collections/:collectionId/indexes')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$indexes = $collection->getAttribute('indexes');
|
||||
|
|
@ -1468,7 +1468,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:key')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$indexes = $collection->getAttribute('indexes');
|
||||
|
|
@ -1477,7 +1477,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:key')
|
|||
$indexIndex = array_search($key, array_column($indexes, 'key'));
|
||||
|
||||
if ($indexIndex === false) {
|
||||
throw new Exception('Index not found', 404);
|
||||
throw new Exception('Index not found', 404, Exception::INDEX_NOT_FOUND);
|
||||
}
|
||||
|
||||
$index = new Document([\array_merge($indexes[$indexIndex], [
|
||||
|
|
@ -1519,13 +1519,13 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$index = $dbForProject->getDocument('indexes', $collectionId.'_'.$key);
|
||||
|
||||
if (empty($index->getId())) {
|
||||
throw new Exception('Index not found', 404);
|
||||
throw new Exception('Index not found', 404, Exception::INDEX_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Only update status if removing available index
|
||||
|
|
@ -1592,11 +1592,11 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
if (empty($data)) {
|
||||
throw new Exception('Missing payload', 400);
|
||||
throw new Exception('Missing payload', 400, Exception::DOCUMENT_MISSING_PAYLOAD);
|
||||
}
|
||||
|
||||
if (isset($data['$id'])) {
|
||||
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
|
||||
throw new Exception('$id is not allowed for creating new documents, try update instead', 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1608,7 +1608,7 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1616,7 +1616,7 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1631,12 +1631,13 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
// TODO: Isn't this a 401: Unauthorized Error ?
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1651,10 +1652,10 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
$document->setAttribute('$collection', $collectionId);
|
||||
}
|
||||
catch (StructureException $exception) {
|
||||
throw new Exception($exception->getMessage(), 400);
|
||||
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
catch (DuplicateException $exception) {
|
||||
throw new Exception('Document already exists', 409);
|
||||
throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$events->setParam('collection', $collection->getArrayCopy());
|
||||
|
|
@ -1712,7 +1713,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1720,7 +1721,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($collection->getRead())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1728,7 +1729,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
$query = Query::parse($query);
|
||||
|
||||
if (\count($query->getValues()) > 100) {
|
||||
throw new Exception("You cannot use more than 100 query values on attribute '{$query->getAttribute()}'", 400);
|
||||
throw new Exception("You cannot use more than 100 query values on attribute '{$query->getAttribute()}'", 400, Exception::GENERAL_QUERY_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
return $query;
|
||||
|
|
@ -1737,7 +1738,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
if (!empty($queries)) {
|
||||
$validator = new QueriesValidator(new QueryValidator($collection->getAttribute('attributes', [])), $collection->getAttribute('indexes', []), true);
|
||||
if (!$validator->isValid($queries)) {
|
||||
throw new Exception($validator->getDescription(), 400);
|
||||
throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1748,7 +1749,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
: $dbForProject->getDocument('collection_' . $collection->getInternalId(), $cursor);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1806,7 +1807,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1814,7 +1815,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($collection->getRead())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1831,7 +1832,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
$document->setAttribute('$collection', $collectionId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404);
|
||||
throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage
|
||||
|
|
@ -1871,13 +1872,13 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
|
|||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$document = $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404);
|
||||
throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
|
|
@ -1972,7 +1973,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1980,7 +1981,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId));
|
||||
|
|
@ -1990,17 +1991,17 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('Document not found', 404);
|
||||
throw new Exception('Document not found', 404, Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
if (empty($data)) {
|
||||
throw new Exception('Missing payload', 400);
|
||||
throw new Exception('Missing payload', 400, Exception::DOCUMENT_MISSING_PAYLOAD);
|
||||
}
|
||||
|
||||
if (!\is_array($data)) {
|
||||
throw new Exception('Data param should be a valid JSON object', 400);
|
||||
throw new Exception('Data param should be a valid JSON object', 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
|
||||
$data = \array_merge($document->getArrayCopy(), $data);
|
||||
|
|
@ -2017,14 +2018,14 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
if(!is_null($read)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!is_null($write)) {
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400);
|
||||
throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2043,13 +2044,13 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
$document->setAttribute('$collection', $collectionId);
|
||||
}
|
||||
catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
catch (DuplicateException $exception) {
|
||||
throw new Exception('Document already exists', 409);
|
||||
throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
}
|
||||
catch (StructureException $exception) {
|
||||
throw new Exception($exception->getMessage(), 400);
|
||||
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
|
||||
$events->setParam('collection', $collection->getArrayCopy());
|
||||
|
|
@ -2104,7 +2105,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
|
||||
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
|
||||
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2112,7 +2113,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2124,7 +2125,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
}
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404);
|
||||
throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use Utopia\Storage\Validator\Upload;
|
|||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Task\Validator\Cron;
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
|
|
@ -103,7 +103,7 @@ App::get('/v1/functions')
|
|||
$cursorFunction = $dbForProject->getDocument('functions', $cursor);
|
||||
|
||||
if ($cursorFunction->isEmpty()) {
|
||||
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ App::get('/v1/functions/:functionId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($function, Response::MODEL_FUNCTION);
|
||||
|
|
@ -197,7 +197,7 @@ App::get('/v1/functions/:functionId/usage')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage = [];
|
||||
|
|
@ -306,7 +306,7 @@ App::put('/v1/functions/:functionId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$original = $function->getAttribute('schedule', '');
|
||||
|
|
@ -364,11 +364,11 @@ App::patch('/v1/functions/:functionId/tag')
|
|||
$tag = $dbForProject->getDocument('tags', $tag);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($tag->isEmpty()) {
|
||||
throw new Exception('Tag not found', 404);
|
||||
throw new Exception('Tag not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$schedule = $function->getAttribute('schedule', '');
|
||||
|
|
@ -416,11 +416,11 @@ App::delete('/v1/functions/:functionId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteDocument('functions', $function->getId())) {
|
||||
throw new Exception('Failed to remove function from DB', 500);
|
||||
throw new Exception('Failed to remove function from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$deletes
|
||||
|
|
@ -452,65 +452,128 @@ App::post('/v1/functions/:functionId/tags')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($functionId, $command, $file, $request, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
->inject('deviceFunctions')
|
||||
->inject('deviceLocal')
|
||||
->action(function ($functionId, $command, $file, $request, $response, $dbForProject, $usage, $deviceFunctions, $deviceLocal) {
|
||||
/** @var Utopia\Swoole\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $usage */
|
||||
/** @var Utopia\Storage\Device $deviceFunctions */
|
||||
/** @var Utopia\Storage\Device $deviceLocal */
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = $request->getFiles('code');
|
||||
$device = Storage::getDevice('functions');
|
||||
$fileExt = new FileExt([FileExt::TYPE_GZIP]);
|
||||
$fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
|
||||
$fileSizeValidator = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
|
||||
$upload = new Upload();
|
||||
|
||||
if (empty($file)) {
|
||||
throw new Exception('No file sent', 400);
|
||||
throw new Exception('No file sent', 400, Exception::STORAGE_FILE_EMPTY);
|
||||
}
|
||||
|
||||
// Make sure we handle a single file and multiple files the same way
|
||||
$file['name'] = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
|
||||
$file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
|
||||
$file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
$fileName = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
|
||||
$fileTmpName = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
|
||||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
|
||||
throw new Exception('File type not allowed', 400);
|
||||
throw new Exception('File type not allowed', 400, Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit
|
||||
throw new Exception('File size not allowed', 400);
|
||||
$contentRange = $request->getHeader('content-range');
|
||||
$tagId = $dbForProject->getId();
|
||||
$chunk = 1;
|
||||
$chunks = 1;
|
||||
|
||||
if (!empty($contentRange)) {
|
||||
$start = $request->getContentRangeStart();
|
||||
$end = $request->getContentRangeEnd();
|
||||
$fileSize = $request->getContentRangeSize();
|
||||
$tagId = $request->getHeader('x-appwrite-id', $tagId);
|
||||
if(is_null($start) || is_null($end) || is_null($fileSize)) {
|
||||
throw new Exception('Invalid content-range header', 400, Exception::STORAGE_INVALID_CONTENT_RANGE);
|
||||
}
|
||||
|
||||
if ($end === $fileSize) {
|
||||
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to notify it's last chunk
|
||||
$chunks = $chunk = -1;
|
||||
} else {
|
||||
// Calculate total number of chunks based on the chunk size i.e ($rangeEnd - $rangeStart)
|
||||
$chunks = (int) ceil($fileSize / ($end + 1 - $start));
|
||||
$chunk = (int) ($start / ($end + 1 - $start)) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$upload->isValid($file['tmp_name'])) {
|
||||
throw new Exception('Invalid file', 403);
|
||||
if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
|
||||
throw new Exception('File size not allowed', 400, Exception::STORAGE_INVALID_FILE_SIZE);
|
||||
}
|
||||
|
||||
if (!$upload->isValid($fileTmpName)) {
|
||||
throw new Exception('Invalid file', 403, Exception::STORAGE_INVALID_FILE);
|
||||
}
|
||||
|
||||
// Save to storage
|
||||
$size = $device->getFileSize($file['tmp_name']);
|
||||
$path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
$fileSize ??= $deviceLocal->getFileSize($fileTmpName);
|
||||
$path = $deviceFunctions->getPath($tagId.'.'.\pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move'
|
||||
throw new Exception('Failed moving file', 500);
|
||||
$tag = $dbForProject->getDocument('tags', $tagId);
|
||||
|
||||
if(!$tag->isEmpty()) {
|
||||
$chunks = $tag->getAttribute('chunksTotal', 1);
|
||||
if($chunk == -1) {
|
||||
$chunk = $chunks;
|
||||
}
|
||||
}
|
||||
|
||||
$chunksUploaded = $deviceFunctions->upload($fileTmpName, $path, $chunk, $chunks);
|
||||
|
||||
if (empty($chunksUploaded)) {
|
||||
throw new Exception('Failed moving file', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$tagId = $dbForProject->getId();
|
||||
$tag = $dbForProject->createDocument('tags', new Document([
|
||||
'$id' => $tagId,
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'functionId' => $function->getId(),
|
||||
'dateCreated' => time(),
|
||||
'command' => $command,
|
||||
'path' => $path,
|
||||
'size' => $size,
|
||||
'search' => implode(' ', [$tagId, $command]),
|
||||
]));
|
||||
if($chunksUploaded === $chunks) {
|
||||
$fileSize = $deviceFunctions->getFileSize($path);
|
||||
|
||||
if ($tag->isEmpty()) {
|
||||
$tag = $dbForProject->createDocument('tags', new Document([
|
||||
'$id' => $tagId,
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'functionId' => $function->getId(),
|
||||
'dateCreated' => time(),
|
||||
'command' => $command,
|
||||
'path' => $path,
|
||||
'size' => $fileSize,
|
||||
'search' => implode(' ', [$tagId, $command]),
|
||||
]));
|
||||
} else {
|
||||
$tag = $dbForProject->updateDocument('tags', $tagId, $tag->setAttribute('size', $fileSize));
|
||||
}
|
||||
} else {
|
||||
if($tag->isEmpty()) {
|
||||
$tag = $dbForProject->createDocument('tags', new Document([
|
||||
'$id' => $tagId,
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'functionId' => $function->getId(),
|
||||
'dateCreated' => time(),
|
||||
'command' => $command,
|
||||
'path' => $path,
|
||||
'size' => 0,
|
||||
'chunksTotal' => $chunks,
|
||||
'chunksUploaded' => $chunksUploaded,
|
||||
'search' => implode(' ', [$tagId, $command]),
|
||||
]));
|
||||
} else {
|
||||
$tag = $dbForProject->updateDocument('tags', $tagId, $tag->setAttribute('chunksUploaded', $chunksUploaded));
|
||||
}
|
||||
}
|
||||
|
||||
$usage
|
||||
->setParam('storage', $tag->getAttribute('size', 0))
|
||||
|
|
@ -547,14 +610,15 @@ App::get('/v1/functions/:functionId/tags')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorTag = $dbForProject->getDocument('tags', $cursor);
|
||||
|
||||
if ($cursorTag->isEmpty()) {
|
||||
throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
// TODO: Shouldn't this be a 404 error ?
|
||||
throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -597,17 +661,17 @@ App::get('/v1/functions/:functionId/tags/:tagId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$tag = $dbForProject->getDocument('tags', $tagId);
|
||||
|
||||
if ($tag->getAttribute('functionId') !== $function->getId()) {
|
||||
throw new Exception('Tag not found', 404);
|
||||
throw new Exception('Tag not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($tag->isEmpty()) {
|
||||
throw new Exception('Tag not found', 404);
|
||||
throw new Exception('Tag not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($tag, Response::MODEL_TAG);
|
||||
|
|
@ -629,32 +693,32 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($functionId, $tagId, $response, $dbForProject, $usage) {
|
||||
->inject('deviceFunctions')
|
||||
->action(function ($functionId, $tagId, $response, $dbForProject, $usage, $deviceFunctions) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $usage */
|
||||
/** @var Utopia\Storage\Device $deviceFunctions */
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$tag = $dbForProject->getDocument('tags', $tagId);
|
||||
|
||||
if ($tag->getAttribute('functionId') !== $function->getId()) {
|
||||
throw new Exception('Tag not found', 404);
|
||||
throw new Exception('Tag not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($tag->isEmpty()) {
|
||||
throw new Exception('Tag not found', 404);
|
||||
throw new Exception('Tag not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$device = Storage::getDevice('functions');
|
||||
|
||||
if ($device->delete($tag->getAttribute('path', ''))) {
|
||||
if ($deviceFunctions->delete($tag->getAttribute('path', ''))) {
|
||||
if (!$dbForProject->deleteDocument('tags', $tag->getId())) {
|
||||
throw new Exception('Failed to remove tag from DB', 500);
|
||||
throw new Exception('Failed to remove tag from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -701,23 +765,23 @@ App::post('/v1/functions/:functionId/executions')
|
|||
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$tag = Authorization::skip(fn() => $dbForProject->getDocument('tags', $function->getAttribute('tag')));
|
||||
|
||||
if ($tag->getAttribute('functionId') !== $function->getId()) {
|
||||
throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404);
|
||||
throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($tag->isEmpty()) {
|
||||
throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404);
|
||||
throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$validator = new Authorization('execute');
|
||||
|
||||
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
|
||||
throw new Exception($validator->getDescription(), 401);
|
||||
throw new Exception($validator->getDescription(), 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$executionId = $dbForProject->getId();
|
||||
|
|
@ -800,14 +864,14 @@ App::get('/v1/functions/:functionId/executions')
|
|||
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorExecution = $dbForProject->getDocument('executions', $cursor);
|
||||
|
||||
if ($cursorExecution->isEmpty()) {
|
||||
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -850,17 +914,17 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
|||
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$execution = $dbForProject->getDocument('executions', $executionId);
|
||||
|
||||
if ($execution->getAttribute('functionId') !== $function->getId()) {
|
||||
throw new Exception('Execution not found', 404);
|
||||
throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($execution->isEmpty()) {
|
||||
throw new Exception('Execution not found', 404);
|
||||
throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($execution, Response::MODEL_EXECUTION);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Storage;
|
||||
use Appwrite\ClamAV\Network;
|
||||
|
|
@ -75,7 +75,7 @@ App::get('/v1/health/db')
|
|||
|
||||
$statement->execute();
|
||||
} catch (Exception $_e) {
|
||||
throw new Exception('Database is not available', 500);
|
||||
throw new Exception('Database is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$output = [
|
||||
|
|
@ -109,7 +109,7 @@ App::get('/v1/health/cache')
|
|||
$redis = $utopia->getResource('cache');
|
||||
|
||||
if (!$redis->ping(true)) {
|
||||
throw new Exception('Cache is not available', 500);
|
||||
throw new Exception('Cache is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$output = [
|
||||
|
|
@ -166,7 +166,7 @@ App::get('/v1/health/time')
|
|||
$diff = ($timestamp - \time());
|
||||
|
||||
if ($diff > $gap || $diff < ($gap * -1)) {
|
||||
throw new Exception('Server time gaps detected');
|
||||
throw new Exception('Server time gaps detected', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$output = [
|
||||
|
|
@ -294,11 +294,11 @@ App::get('/v1/health/storage/local')
|
|||
$device = new Local($volume);
|
||||
|
||||
if (!\is_readable($device->getRoot())) {
|
||||
throw new Exception('Device '.$key.' dir is not readable');
|
||||
throw new Exception('Device '.$key.' dir is not readable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if (!\is_writable($device->getRoot())) {
|
||||
throw new Exception('Device '.$key.' dir is not writable');
|
||||
throw new Exception('Device '.$key.' dir is not writable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -341,7 +341,7 @@ App::get('/v1/health/anti-virus')
|
|||
$output['version'] = @$antivirus->version();
|
||||
$output['status'] = (@$antivirus->ping()) ? 'pass' : 'fail';
|
||||
} catch( \Exception $e) {
|
||||
throw new Exception('Antivirus is not available', 500);
|
||||
throw new Exception('Antivirus is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -358,11 +358,12 @@ App::get('/v1/health/stats') // Currently only used internally
|
|||
->label('docs', false)
|
||||
->inject('response')
|
||||
->inject('register')
|
||||
->action(function ($response, $register) {
|
||||
->inject('deviceFiles')
|
||||
->action(function ($response, $register, $deviceFiles) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
/** @var Utopia\Storage\Device $deviceFiles */
|
||||
|
||||
$device = Storage::getDevice('files');
|
||||
$cache = $register->get('cache');
|
||||
|
||||
$cacheStats = $cache->info();
|
||||
|
|
@ -370,9 +371,9 @@ App::get('/v1/health/stats') // Currently only used internally
|
|||
$response
|
||||
->json([
|
||||
'storage' => [
|
||||
'used' => Storage::human($device->getDirectorySize($device->getRoot().'/')),
|
||||
'partitionTotal' => Storage::human($device->getPartitionTotalSpace()),
|
||||
'partitionFree' => Storage::human($device->getPartitionFreeSpace()),
|
||||
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot().'/')),
|
||||
'partitionTotal' => Storage::human($deviceFiles->getPartitionTotalSpace()),
|
||||
'partitionFree' => Storage::human($deviceFiles->getPartitionFreeSpace()),
|
||||
],
|
||||
'cache' => [
|
||||
'uptime' => $cacheStats['uptime_in_seconds'] ?? 0,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use Utopia\Database\Query;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Integer;
|
||||
|
|
@ -29,7 +29,7 @@ App::init(function ($project) {
|
|||
/** @var Utopia\Database\Document $project */
|
||||
|
||||
if ($project->getId() !== 'console') {
|
||||
throw new Exception('Access to this API is forbidden.', 401);
|
||||
throw new Exception('Access to this API is forbidden.', 401, Exception::GENERAL_ACCESS_FORBIDDEN);
|
||||
}
|
||||
}, ['project'], 'projects');
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ App::post('/v1/projects')
|
|||
$team = $dbForConsole->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$auth = Config::getParam('auth', []);
|
||||
|
|
@ -114,6 +114,9 @@ App::post('/v1/projects')
|
|||
$adapter->setup();
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if(($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
|
||||
|
|
@ -172,7 +175,7 @@ App::get('/v1/projects')
|
|||
$cursorProject = $dbForConsole->getDocument('projects', $cursor);
|
||||
|
||||
if ($cursorProject->isEmpty()) {
|
||||
throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +214,7 @@ App::get('/v1/projects/:projectId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
|
|
@ -242,7 +245,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage = [];
|
||||
|
|
@ -361,7 +364,7 @@ App::patch('/v1/projects/:projectId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
|
|
@ -404,7 +407,7 @@ App::patch('/v1/projects/:projectId/service')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$services = $project->getAttribute('services', []);
|
||||
|
|
@ -438,7 +441,7 @@ App::patch('/v1/projects/:projectId/oauth2')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$providers = $project->getAttribute('providers', []);
|
||||
|
|
@ -471,7 +474,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
|
|
@ -509,7 +512,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
|||
$status = ($status === '1' || $status === 'true' || $status === 1 || $status === true);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
|
|
@ -542,13 +545,13 @@ App::delete('/v1/projects/:projectId')
|
|||
/** @var Appwrite\Event\Event $deletes */
|
||||
|
||||
if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password
|
||||
throw new Exception('Invalid credentials', 401);
|
||||
throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$deletes
|
||||
|
|
@ -557,11 +560,11 @@ App::delete('/v1/projects/:projectId')
|
|||
;
|
||||
|
||||
if (!$dbForConsole->deleteDocument('teams', $project->getAttribute('teamId', null))) {
|
||||
throw new Exception('Failed to remove project team from DB', 500);
|
||||
throw new Exception('Failed to remove project team from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if (!$dbForConsole->deleteDocument('projects', $projectId)) {
|
||||
throw new Exception('Failed to remove project from DB', 500);
|
||||
throw new Exception('Failed to remove project from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$response->noContent();
|
||||
|
|
@ -595,7 +598,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
|
||||
|
|
@ -641,7 +644,7 @@ App::get('/v1/projects/:projectId/webhooks')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$webhooks = $dbForConsole->find('webhooks', [
|
||||
|
|
@ -675,7 +678,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$webhook = $dbForConsole->findOne('webhooks', [
|
||||
|
|
@ -684,7 +687,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
]);
|
||||
|
||||
if ($webhook === false || $webhook->isEmpty()) {
|
||||
throw new Exception('Webhook not found', 404);
|
||||
throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||
|
|
@ -717,7 +720,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
|
||||
|
|
@ -728,7 +731,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
]);
|
||||
|
||||
if ($webhook === false || $webhook->isEmpty()) {
|
||||
throw new Exception('Webhook not found', 404);
|
||||
throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND);
|
||||
}
|
||||
|
||||
$webhook
|
||||
|
|
@ -767,7 +770,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$webhook = $dbForConsole->findOne('webhooks', [
|
||||
|
|
@ -776,7 +779,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
]);
|
||||
|
||||
if($webhook === false || $webhook->isEmpty()) {
|
||||
throw new Exception('Webhook not found', 404);
|
||||
throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForConsole->deleteDocument('webhooks', $webhook->getId());
|
||||
|
|
@ -810,7 +813,7 @@ App::post('/v1/projects/:projectId/keys')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$key = new Document([
|
||||
|
|
@ -851,7 +854,7 @@ App::get('/v1/projects/:projectId/keys')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$keys = $dbForConsole->find('keys', [
|
||||
|
|
@ -885,7 +888,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$key = $dbForConsole->findOne('keys', [
|
||||
|
|
@ -894,7 +897,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
|||
]);
|
||||
|
||||
if ($key === false || $key->isEmpty()) {
|
||||
throw new Exception('Key not found', 404);
|
||||
throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($key, Response::MODEL_KEY);
|
||||
|
|
@ -923,7 +926,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$key = $dbForConsole->findOne('keys', [
|
||||
|
|
@ -932,7 +935,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
|||
]);
|
||||
|
||||
if ($key === false || $key->isEmpty()) {
|
||||
throw new Exception('Key not found', 404);
|
||||
throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$key
|
||||
|
|
@ -967,7 +970,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$key = $dbForConsole->findOne('keys', [
|
||||
|
|
@ -976,7 +979,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
|||
]);
|
||||
|
||||
if($key === false || $key->isEmpty()) {
|
||||
throw new Exception('Key not found', 404);
|
||||
throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForConsole->deleteDocument('keys', $key->getId());
|
||||
|
|
@ -1013,7 +1016,7 @@ App::post('/v1/projects/:projectId/platforms')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$platform = new Document([
|
||||
|
|
@ -1058,7 +1061,7 @@ App::get('/v1/projects/:projectId/platforms')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$platforms = $dbForConsole->find('platforms', [
|
||||
|
|
@ -1092,7 +1095,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$platform = $dbForConsole->findOne('platforms', [
|
||||
|
|
@ -1101,7 +1104,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
|||
]);
|
||||
|
||||
if ($platform === false || $platform->isEmpty()) {
|
||||
throw new Exception('Platform not found', 404);
|
||||
throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($platform, Response::MODEL_PLATFORM);
|
||||
|
|
@ -1132,7 +1135,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$platform = $dbForConsole->findOne('platforms', [
|
||||
|
|
@ -1141,7 +1144,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
|||
]);
|
||||
|
||||
if ($platform === false || $platform->isEmpty()) {
|
||||
throw new Exception('Platform not found', 404);
|
||||
throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$platform
|
||||
|
|
@ -1179,7 +1182,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$platform = $dbForConsole->findOne('platforms', [
|
||||
|
|
@ -1188,7 +1191,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
|||
]);
|
||||
|
||||
if ($platform === false || $platform->isEmpty()) {
|
||||
throw new Exception('Platform not found', 404);
|
||||
throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForConsole->deleteDocument('platforms', $platformId);
|
||||
|
|
@ -1221,7 +1224,7 @@ App::post('/v1/projects/:projectId/domains')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$document = $dbForConsole->findOne('domains', [
|
||||
|
|
@ -1230,13 +1233,13 @@ App::post('/v1/projects/:projectId/domains')
|
|||
]);
|
||||
|
||||
if ($document && !$document->isEmpty()) {
|
||||
throw new Exception('Domain already exists', 409);
|
||||
throw new Exception('Domain already exists', 409, Exception::DOMAIN_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
if (!$target->isKnown() || $target->isTest()) {
|
||||
throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500);
|
||||
throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$domain = new Domain($domain);
|
||||
|
|
@ -1282,7 +1285,7 @@ App::get('/v1/projects/:projectId/domains')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$domains = $dbForConsole->find('domains', [
|
||||
|
|
@ -1316,7 +1319,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$domain = $dbForConsole->findOne('domains', [
|
||||
|
|
@ -1325,7 +1328,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
|
|||
]);
|
||||
|
||||
if ($domain === false || $domain->isEmpty()) {
|
||||
throw new Exception('Domain not found', 404);
|
||||
throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($domain, Response::MODEL_DOMAIN);
|
||||
|
|
@ -1352,7 +1355,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$domain = $dbForConsole->findOne('domains', [
|
||||
|
|
@ -1361,13 +1364,13 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
|
|||
]);
|
||||
|
||||
if ($domain === false || $domain->isEmpty()) {
|
||||
throw new Exception('Domain not found', 404);
|
||||
throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND);
|
||||
}
|
||||
|
||||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
if (!$target->isKnown() || $target->isTest()) {
|
||||
throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500);
|
||||
throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if ($domain->getAttribute('verification') === true) {
|
||||
|
|
@ -1377,7 +1380,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
|
|||
$validator = new CNAME($target->get()); // Verify Domain with DNS records
|
||||
|
||||
if (!$validator->isValid($domain->getAttribute('domain', ''))) {
|
||||
throw new Exception('Failed to verify domain', 401);
|
||||
throw new Exception('Failed to verify domain', 401, Exception::DOMAIN_VERIFICATION_FAILED);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1414,7 +1417,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$domain = $dbForConsole->findOne('domains', [
|
||||
|
|
@ -1423,7 +1426,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
|
|||
]);
|
||||
|
||||
if ($domain === false || $domain->isEmpty()) {
|
||||
throw new Exception('Domain not found', 404);
|
||||
throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForConsole->deleteDocument('domains', $domain->getId());
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Appwrite\Template\Template;
|
|||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
|
@ -17,7 +18,6 @@ use Utopia\Database\Query;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\ArrayList;
|
||||
|
|
@ -120,7 +120,7 @@ App::get('/v1/teams')
|
|||
$cursorTeam = $dbForProject->getDocument('teams', $cursor);
|
||||
|
||||
if ($cursorTeam->isEmpty()) {
|
||||
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ App::get('/v1/teams/:teamId')
|
|||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
|
|
@ -189,7 +189,7 @@ App::put('/v1/teams/:teamId')
|
|||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(),$team
|
||||
|
|
@ -225,7 +225,7 @@ App::delete('/v1/teams/:teamId')
|
|||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$memberships = $dbForProject->find('memberships', [
|
||||
|
|
@ -235,12 +235,12 @@ App::delete('/v1/teams/:teamId')
|
|||
// TODO delete all members individually from the user object
|
||||
foreach ($memberships as $membership) {
|
||||
if (!$dbForProject->deleteDocument('memberships', $membership->getId())) {
|
||||
throw new Exception('Failed to remove membership for team from DB', 500);
|
||||
throw new Exception('Failed to remove membership for team from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteDocument('teams', $teamId)) {
|
||||
throw new Exception('Failed to remove team from DB', 500);
|
||||
throw new Exception('Failed to remove team from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$deletes
|
||||
|
|
@ -290,7 +290,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
/** @var Appwrite\Event\Event $mails */
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503);
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
|
@ -301,7 +301,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$invitee = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
|
||||
|
|
@ -314,7 +314,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
$sum = $dbForProject->count('users', [], APP_LIMIT_USERS);
|
||||
|
||||
if($sum >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -344,14 +344,14 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
$isOwner = Authorization::isRole('team:'.$team->getId().'/owner');;
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception('User is not allowed to send invitations for this team', 401);
|
||||
throw new Exception('User is not allowed to send invitations for this team', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
|
@ -375,7 +375,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
try {
|
||||
$membership = Authorization::skip(fn() => $dbForProject->createDocument('memberships', $membership));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409);
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
}
|
||||
$team->setAttribute('sum', $team->getAttribute('sum', 0) + 1);
|
||||
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
|
||||
|
|
@ -388,7 +388,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
try {
|
||||
$membership = $dbForProject->createDocument('memberships', $membership);
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409);
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -452,14 +452,14 @@ App::get('/v1/teams/:teamId/memberships')
|
|||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorMembership = $dbForProject->getDocument('memberships', $cursor);
|
||||
|
||||
if ($cursorMembership->isEmpty()) {
|
||||
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -526,13 +526,13 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
|||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if($membership->isEmpty() || empty($membership->getAttribute('userId'))) {
|
||||
throw new Exception('Membership not found', 404);
|
||||
throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
|
@ -574,17 +574,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
|||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
if ($membership->isEmpty()) {
|
||||
throw new Exception('Membership not found', 404);
|
||||
throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$profile = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
if ($profile->isEmpty()) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
|
@ -592,7 +592,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
|||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');;
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception('User is not allowed to modify roles', 401);
|
||||
throw new Exception('User is not allowed to modify roles', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -660,25 +660,25 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if ($membership->isEmpty()) {
|
||||
throw new Exception('Membership not found', 404);
|
||||
throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('teamId') !== $teamId) {
|
||||
throw new Exception('Team IDs don\'t match', 404);
|
||||
throw new Exception('Team IDs don\'t match', 404, Exception::TEAM_MEMBERSHIP_MISMATCH);
|
||||
}
|
||||
|
||||
$team = Authorization::skip(fn() => $dbForProject->getDocument('teams', $teamId));
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (Auth::hash($secret) !== $membership->getAttribute('secret')) {
|
||||
throw new Exception('Secret key not valid', 401);
|
||||
throw new Exception('Secret key not valid', 401, Exception::TEAM_INVALID_SECRET);
|
||||
}
|
||||
|
||||
if ($userId != $membership->getAttribute('userId')) {
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401);
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
}
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
|
|
@ -686,7 +686,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
}
|
||||
|
||||
if ($membership->getAttribute('userId') !== $user->getId()) {
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401);
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
}
|
||||
|
||||
$membership // Attach user to team
|
||||
|
|
@ -782,7 +782,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
|||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if ($membership->isEmpty()) {
|
||||
throw new Exception('Invite not found', 404);
|
||||
throw new Exception('Invite not found', 404, Exception::TEAM_INVITE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('teamId') !== $teamId) {
|
||||
|
|
@ -792,21 +792,21 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
|||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->deleteDocument('memberships', $membership->getId());
|
||||
} catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception('Failed to remove membership from DB', 500);
|
||||
throw new Exception('Failed to remove membership from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$memberships = $user->getAttribute('memberships', []);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use Appwrite\Utopia\Response;
|
|||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Validator\UID;
|
||||
|
|
@ -70,7 +70,7 @@ App::post('/v1/users')
|
|||
'deleted' => false
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409);
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$usage
|
||||
|
|
@ -110,7 +110,7 @@ App::get('/v1/users')
|
|||
$cursorUser = $dbForProject->getDocument('users', $cursor);
|
||||
|
||||
if ($cursorUser->isEmpty()) {
|
||||
throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ App::get('/v1/users/:userId')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage
|
||||
|
|
@ -187,7 +187,7 @@ App::get('/v1/users/:userId/prefs')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$prefs = $user->getAttribute('prefs', new \stdClass());
|
||||
|
|
@ -223,7 +223,7 @@ App::get('/v1/users/:userId/sessions')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
|
@ -277,7 +277,7 @@ App::get('/v1/users/:userId/logs')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
|
|
@ -378,7 +378,7 @@ App::patch('/v1/users/:userId/status')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status));
|
||||
|
|
@ -414,7 +414,7 @@ App::patch('/v1/users/:userId/verification')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification));
|
||||
|
|
@ -450,7 +450,7 @@ App::patch('/v1/users/:userId/name')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('name', $name));
|
||||
|
|
@ -489,7 +489,7 @@ App::patch('/v1/users/:userId/password')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user
|
||||
|
|
@ -532,7 +532,7 @@ App::patch('/v1/users/:userId/email')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); // Check if request is from an anonymous account for converting
|
||||
|
|
@ -545,7 +545,7 @@ App::patch('/v1/users/:userId/email')
|
|||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('email', $email));
|
||||
} catch(Duplicate $th) {
|
||||
throw new Exception('Email already exists', 409);
|
||||
throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$audits
|
||||
|
|
@ -582,7 +582,7 @@ App::patch('/v1/users/:userId/prefs')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
||||
|
|
@ -619,7 +619,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
|
@ -674,7 +674,7 @@ App::delete('/v1/users/:userId/sessions')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
|
@ -723,7 +723,7 @@ App::delete('/v1/users/:userId')
|
|||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
throw new Exception('User not found', 404);
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use Utopia\Logger\Log\User;
|
|||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\Exception;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Domains\Domain;
|
||||
use Appwrite\Auth\Auth;
|
||||
|
|
@ -106,11 +106,11 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
}
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($route->getLabel('sdk.auth', [])) && $project->isEmpty() && ($route->getLabel('scope', '') !== 'public')) {
|
||||
throw new Exception('Missing or unknown project ID', 400);
|
||||
throw new Exception('Missing or unknown project ID', 400, Exception::PROJECT_UNKNOWN);
|
||||
}
|
||||
|
||||
$referrer = $request->getReferer();
|
||||
|
|
@ -185,7 +185,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
->addHeader('Server', 'Appwrite')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, X-Appwrite-ID, Content-Range, Range, Cache-Control, Expires, Pragma')
|
||||
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Origin', $refDomain)
|
||||
->addHeader('Access-Control-Allow-Credentials', 'true')
|
||||
|
|
@ -203,7 +203,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
&& \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
|
||||
&& $route->getLabel('origin', false) !== '*'
|
||||
&& empty($request->getHeader('x-appwrite-key', ''))) {
|
||||
throw new Exception($originValidator->getDescription(), 403);
|
||||
throw new Exception($originValidator->getDescription(), 403, Exception::GENERAL_UNKNOWN_ORIGIN);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -272,24 +272,24 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
if(array_key_exists($service, $project->getAttribute('services',[]))
|
||||
&& !$project->getAttribute('services',[])[$service]
|
||||
&& !Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
throw new Exception('Service is disabled', 503);
|
||||
throw new Exception('Service is disabled', 503, Exception::GENERAL_SERVICE_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!\in_array($scope, $scopes)) {
|
||||
if ($project->isEmpty()) { // Check if permission is denied because project is missing
|
||||
throw new Exception('Project not found', 404);
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
throw new Exception($user->getAttribute('email', 'User').' (role: '.\strtolower($roles[$role]['label']).') missing scope ('.$scope.')', 401);
|
||||
throw new Exception($user->getAttribute('email', 'User').' (role: '.\strtolower($roles[$role]['label']).') missing scope ('.$scope.')', 401, Exception::GENERAL_UNAUTHORIZED_SCOPE);
|
||||
}
|
||||
|
||||
if (false === $user->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception('Invalid credentials. User is blocked', 401);
|
||||
throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED);
|
||||
}
|
||||
|
||||
if ($user->getAttribute('reset')) {
|
||||
throw new Exception('Password reset is required', 412);
|
||||
throw new Exception('Password reset is required', 412, Exception::USER_PASSWORD_RESET_REQUIRED);
|
||||
}
|
||||
|
||||
}, ['utopia', 'request', 'response', 'console', 'project', 'dbForConsole', 'user', 'locale', 'clients']);
|
||||
|
|
@ -303,7 +303,7 @@ App::options(function ($request, $response) {
|
|||
$response
|
||||
->addHeader('Server', 'Appwrite')
|
||||
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, X-Appwrite-ID, Content-Range, Range, Cache-Control, Expires, Pragma, X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Origin', $origin)
|
||||
->addHeader('Access-Control-Allow-Credentials', 'true')
|
||||
|
|
@ -376,6 +376,26 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
throw $error;
|
||||
}
|
||||
|
||||
|
||||
/** Handle Utopia Errors */
|
||||
if ($error instanceof Utopia\Exception) {
|
||||
$code = $error->getCode();
|
||||
$error = new Exception($error->getMessage(), $code, Exception::GENERAL_UNKNOWN, $error);
|
||||
switch($code) {
|
||||
case 400:
|
||||
$error->setType(Exception::GENERAL_ARGUMENT_INVALID);
|
||||
break;
|
||||
case 404:
|
||||
$error->setType(Exception::GENERAL_ROUTE_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Wrap all exceptions inside Appwrite\Extend\Exception */
|
||||
if (!($error instanceof Exception)) {
|
||||
$error = new Exception($error->getMessage(), $error->getCode(), Exception::GENERAL_UNKNOWN, $error);
|
||||
}
|
||||
|
||||
$template = ($route) ? $route->getLabel('error', null) : null;
|
||||
|
||||
if (php_sapi_name() === 'cli') {
|
||||
|
|
@ -400,6 +420,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
case 404: // Error allowed publicly
|
||||
case 409: // Error allowed publicly
|
||||
case 412: // Error allowed publicly
|
||||
case 416: // Error allowed publicly
|
||||
case 429: // Error allowed publicly
|
||||
case 501: // Error allowed publicly
|
||||
case 503: // Error allowed publicly
|
||||
|
|
@ -420,10 +441,12 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
'line' => $error->getLine(),
|
||||
'trace' => $error->getTrace(),
|
||||
'version' => $version,
|
||||
'type' => $error->getType(),
|
||||
] : [
|
||||
'message' => $message,
|
||||
'code' => $code,
|
||||
'version' => $version,
|
||||
'type' => $error->getType(),
|
||||
];
|
||||
|
||||
$response
|
||||
|
|
@ -536,7 +559,7 @@ App::get('/.well-known/acme-challenge')
|
|||
$absolute = \realpath($base.'/.well-known/acme-challenge/'.$path);
|
||||
|
||||
if (!$base) {
|
||||
throw new Exception('Storage error', 500);
|
||||
throw new Exception('Storage error', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if (!$absolute) {
|
||||
|
|
@ -554,7 +577,7 @@ App::get('/.well-known/acme-challenge')
|
|||
$content = @\file_get_contents($absolute);
|
||||
|
||||
if (!$content) {
|
||||
throw new Exception('Failed to get contents', 500);
|
||||
throw new Exception('Failed to get contents', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$response->text($content);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
global $utopia, $request, $response;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Document;
|
||||
use Appwrite\Network\Validator\Host;
|
||||
use Appwrite\Utopia\Response;
|
||||
|
|
@ -251,32 +252,32 @@ App::post('/v1/mock/tests/general/upload')
|
|||
$file['size'] = (\is_array($file['size'])) ? $file['size'] : [$file['size']];
|
||||
|
||||
if(is_null($start) || is_null($end) || is_null($size)) {
|
||||
throw new Exception('Invalid content-range header', 400);
|
||||
throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($start > $end || $end > $size) {
|
||||
throw new Exception('Invalid content-range header', 400);
|
||||
throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($start === 0 && !empty($id)) {
|
||||
throw new Exception('First chunked request cannot have id header', 400);
|
||||
throw new Exception('First chunked request cannot have id header', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($start !== 0 && $id !== 'newfileid') {
|
||||
throw new Exception('All chunked request must have id header (except first)', 400);
|
||||
throw new Exception('All chunked request must have id header (except first)', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($end !== $size && $end-$start+1 !== 5*1024*1024) {
|
||||
throw new Exception('Chunk size must be 5MB (except last chunk)', 400);
|
||||
throw new Exception('Chunk size must be 5MB (except last chunk)', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
foreach ($file['size'] as $i => $sz) {
|
||||
if ($end !== $size && $sz !== 5*1024*1024) {
|
||||
throw new Exception('Wrong chunk size', 400);
|
||||
throw new Exception('Wrong chunk size', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($sz > 5*1024*1024) {
|
||||
throw new Exception('Chunk size must be 5MB or less', 400);
|
||||
throw new Exception('Chunk size must be 5MB or less', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
}
|
||||
if($end !== $size) {
|
||||
|
|
@ -289,19 +290,19 @@ App::post('/v1/mock/tests/general/upload')
|
|||
|
||||
foreach ($file['name'] as $i => $name) {
|
||||
if ($name !== 'file.png') {
|
||||
throw new Exception('Wrong file name', 400);
|
||||
throw new Exception('Wrong file name', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($file['size'] as $i => $size) {
|
||||
if ($size !== 38756) {
|
||||
throw new Exception('Wrong file size', 400);
|
||||
throw new Exception('Wrong file size', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($file['tmp_name'] as $i => $tmpName) {
|
||||
if (\md5(\file_get_contents($tmpName)) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') {
|
||||
throw new Exception('Wrong file uploaded', 400);
|
||||
throw new Exception('Wrong file uploaded', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -379,7 +380,7 @@ App::get('/v1/mock/tests/general/get-cookie')
|
|||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
if ($request->getCookie('cookieName', '') !== 'cookieValue') {
|
||||
throw new Exception('Missing cookie value', 400);
|
||||
throw new Exception('Missing cookie value', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -414,7 +415,7 @@ App::get('/v1/mock/tests/general/400-error')
|
|||
->label('sdk.response.model', Response::MODEL_ERROR)
|
||||
->label('sdk.mock', true)
|
||||
->action(function () {
|
||||
throw new Exception('Mock 400 error', 400);
|
||||
throw new Exception('Mock 400 error', 400, Exception::GENERAL_MOCK);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/500-error')
|
||||
|
|
@ -430,7 +431,7 @@ App::get('/v1/mock/tests/general/500-error')
|
|||
->label('sdk.response.model', Response::MODEL_ERROR)
|
||||
->label('sdk.mock', true)
|
||||
->action(function () {
|
||||
throw new Exception('Mock 500 error', 500);
|
||||
throw new Exception('Mock 500 error', 500, Exception::GENERAL_MOCK);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/502-error')
|
||||
|
|
@ -489,11 +490,11 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
|||
/** @var Appwrite\Utopia\Response $response */
|
||||
|
||||
if ($client_id != '1') {
|
||||
throw new Exception('Invalid client ID');
|
||||
throw new Exception('Invalid client ID', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if ($client_secret != '123456') {
|
||||
throw new Exception('Invalid client secret');
|
||||
throw new Exception('Invalid client secret', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
$responseJson = [
|
||||
|
|
@ -504,18 +505,18 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
|||
|
||||
if($grantType === 'authorization_code') {
|
||||
if ($code !== 'abcdef') {
|
||||
throw new Exception('Invalid token');
|
||||
throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
$response->json($responseJson);
|
||||
} else if($grantType === 'refresh_token') {
|
||||
if ($refreshToken !== 'tuvwxyz') {
|
||||
throw new Exception('Invalid refresh token');
|
||||
throw new Exception('Invalid refresh token', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
$response->json($responseJson);
|
||||
} else {
|
||||
throw new Exception('Invalid grant type');
|
||||
throw new Exception('Invalid grant type', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -530,7 +531,7 @@ App::get('/v1/mock/tests/general/oauth2/user')
|
|||
/** @var Appwrite\Utopia\Response $response */
|
||||
|
||||
if ($token != '123456') {
|
||||
throw new Exception('Invalid token');
|
||||
throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
$response->json([
|
||||
|
|
@ -581,7 +582,7 @@ App::shutdown(function($utopia, $response, $request) {
|
|||
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
|
||||
|
||||
if (!\is_array($tests)) {
|
||||
throw new Exception('Failed to read results', 500);
|
||||
throw new Exception('Failed to read results', 500, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
$result[$route->getMethod() . ':' . $route->getPath()] = true;
|
||||
|
|
@ -589,7 +590,7 @@ App::shutdown(function($utopia, $response, $request) {
|
|||
$tests = \array_merge($tests, $result);
|
||||
|
||||
if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) {
|
||||
throw new Exception('Failed to save results', 500);
|
||||
throw new Exception('Failed to save results', 500, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
$response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK);
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Utopia\App;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $usage, $deletes, $database, $dbForProject, $mode) {
|
||||
|
|
@ -26,13 +28,10 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
/** @var Appwrite\Event\Event $functions */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
|
||||
Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
|
||||
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId()));
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope
|
||||
throw new Exception('Missing or unknown project ID', 400);
|
||||
throw new Exception('Missing or unknown project ID', 400, Exception::PROJECT_UNKNOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -81,7 +80,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
&& App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not disabled
|
||||
&& (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
|
||||
{
|
||||
throw new Exception('Too many requests', 429);
|
||||
throw new Exception('Too many requests', 429, Exception::GENERAL_RATE_LIMIT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,36 +149,36 @@ App::init(function ($utopia, $request, $project) {
|
|||
switch ($route->getLabel('auth.type', '')) {
|
||||
case 'emailPassword':
|
||||
if(($auths['emailPassword'] ?? true) === false) {
|
||||
throw new Exception('Email / Password authentication is disabled for this project', 501);
|
||||
throw new Exception('Email / Password authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'magic-url':
|
||||
if($project->getAttribute('usersAuthMagicURL', true) === false) {
|
||||
throw new Exception('Magic URL authentication is disabled for this project', 501);
|
||||
throw new Exception('Magic URL authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'anonymous':
|
||||
if(($auths['anonymous'] ?? true) === false) {
|
||||
throw new Exception('Anonymous authentication is disabled for this project', 501);
|
||||
throw new Exception('Anonymous authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'invites':
|
||||
if(($auths['invites'] ?? true) === false) {
|
||||
throw new Exception('Invites authentication is disabled for this project', 501);
|
||||
throw new Exception('Invites authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'jwt':
|
||||
if(($auths['JWT'] ?? true) === false) {
|
||||
throw new Exception('JWT authentication is disabled for this project', 501);
|
||||
throw new Exception('JWT authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Unsupported authentication route');
|
||||
throw new Exception('Unsupported authentication route', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -218,12 +217,14 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
|
|||
if ($project->getId() !== 'console') {
|
||||
$payload = new Document($response->getPayload());
|
||||
$collection = new Document($events->getParam('collection') ?? []);
|
||||
$bucket = new Document($events->getParam('bucket') ?? []);
|
||||
|
||||
$target = Realtime::fromPayload(
|
||||
event: $events->getParam('event'),
|
||||
payload: $payload,
|
||||
project: $project,
|
||||
collection: $collection
|
||||
collection: $collection,
|
||||
bucket: $bucket,
|
||||
);
|
||||
|
||||
Realtime::send(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
|
|
@ -315,6 +316,36 @@ App::get('/console/storage')
|
|||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/storage/bucket')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('id', '', new UID(), 'Bucket unique ID.')
|
||||
->inject('response')
|
||||
->inject('layout')
|
||||
->action(function ($id, $response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/storage/bucket.phtml');
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', 0))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Storage Buckets')
|
||||
->setParam('body', $page)
|
||||
;
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Expires', 0)
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/console/users')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
|
|
@ -419,9 +450,9 @@ App::get('/console/version')
|
|||
if ($version && isset($version['version'])) {
|
||||
return $response->json(['version' => $version['version']]);
|
||||
} else {
|
||||
throw new Exception('Failed to check for a newer version', 500);
|
||||
throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
throw new Exception('Failed to check for a newer version', 500);
|
||||
throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,14 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Specification\Format\OpenAPI3;
|
||||
use Appwrite\Specification\Format\Swagger2;
|
||||
use Appwrite\Specification\Specification;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::init(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
|
|
|||
62
app/http.php
|
|
@ -13,6 +13,7 @@ use Utopia\Config\Config;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Swoole\Files;
|
||||
use Appwrite\Utopia\Request;
|
||||
|
|
@ -21,7 +22,7 @@ use Utopia\Logger\Log\User;
|
|||
|
||||
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
|
||||
|
||||
$payloadSize = max(4000000 /* 4mb */, App::getEnv('_APP_STORAGE_LIMIT', 10000000 /* 10mb */));
|
||||
$payloadSize = 6 * (1024 * 1024); // 6MB
|
||||
|
||||
$http
|
||||
->set([
|
||||
|
|
@ -109,10 +110,12 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
}
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if(($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
if(!$dbForConsole->getCollection($key)->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
|
||||
|
||||
$attributes = [];
|
||||
|
|
@ -141,8 +144,61 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
}
|
||||
|
||||
$dbForConsole->createCollection($key, $attributes, $indexes);
|
||||
|
||||
}
|
||||
|
||||
if($dbForConsole->getDocument('buckets', 'default')->isEmpty()) {
|
||||
Console::success('[Setup] - Creating default bucket...');
|
||||
$dbForConsole->createDocument('buckets', new Document([
|
||||
'$id' => 'default',
|
||||
'$collection' => 'buckets',
|
||||
'dateCreated' => \time(),
|
||||
'dateUpdated' => \time(),
|
||||
'name' => 'Default',
|
||||
'permission' => 'file',
|
||||
'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
|
||||
'allowedFileExtensions' => [],
|
||||
'enabled' => true,
|
||||
'encryption' => true,
|
||||
'antivirus' => true,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'search' => 'buckets Default',
|
||||
]));
|
||||
|
||||
Console::success('[Setup] - Creating files collection for default bucket...');
|
||||
$files = $collections['files'] ?? [];
|
||||
if(empty($files)) {
|
||||
throw new Exception('Files collection is not configured.');
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
|
||||
foreach ($files['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
'signed' => $attribute['signed'],
|
||||
'array' => $attribute['array'],
|
||||
'filters' => $attribute['filters'],
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($files['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
'orders' => $index['orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
$dbForConsole->createCollection('bucket_' . 'default', $attributes, $indexes);
|
||||
}
|
||||
|
||||
Console::success('[Setup] - Server database init completed...');
|
||||
});
|
||||
|
||||
|
|
|
|||
67
app/init.php
|
|
@ -20,6 +20,7 @@ error_reporting(E_ALL);
|
|||
use Appwrite\Extend\PDO;
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
|
|
@ -49,6 +50,10 @@ use Swoole\Database\PDOPool;
|
|||
use Swoole\Database\RedisConfig;
|
||||
use Swoole\Database\RedisPool;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
|
||||
const APP_NAME = 'Appwrite';
|
||||
const APP_DOMAIN = 'appwrite.io';
|
||||
|
|
@ -60,8 +65,12 @@ const APP_MODE_ADMIN = 'admin';
|
|||
const APP_PAGING_LIMIT = 12;
|
||||
const APP_LIMIT_COUNT = 5000;
|
||||
const APP_LIMIT_USERS = 10000;
|
||||
const APP_LIMIT_ANTIVIRUS = 20000000; //20MB
|
||||
const APP_LIMIT_ENCRYPTION = 20000000; //20MB
|
||||
const APP_LIMIT_COMPRESSION = 20000000; //20MB
|
||||
const APP_LIMIT_PREVIEW = 10000000; //10MB file size limit for preview endpoint
|
||||
const APP_CACHE_BUSTER = 201;
|
||||
const APP_VERSION_STABLE = '0.13.0';
|
||||
const APP_VERSION_STABLE = '0.12.3';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
|
|
@ -74,6 +83,7 @@ const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
|||
const APP_STORAGE_CACHE = '/storage/cache';
|
||||
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||
const APP_STORAGE_CONFIG = '/storage/config';
|
||||
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
|
||||
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
|
||||
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
|
||||
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
|
||||
|
|
@ -106,6 +116,7 @@ const DELETE_TYPE_ABUSE = 'abuse';
|
|||
const DELETE_TYPE_CERTIFICATES = 'certificates';
|
||||
const DELETE_TYPE_USAGE = 'usage';
|
||||
const DELETE_TYPE_REALTIME = 'realtime';
|
||||
const DELETE_TYPE_BUCKETS = 'buckets';
|
||||
// Mail Types
|
||||
const MAIL_TYPE_VERIFICATION = 'verification';
|
||||
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
|
||||
|
|
@ -116,6 +127,8 @@ const APP_AUTH_TYPE_SESSION = 'Session';
|
|||
const APP_AUTH_TYPE_JWT = 'JWT';
|
||||
const APP_AUTH_TYPE_KEY = 'Key';
|
||||
const APP_AUTH_TYPE_ADMIN = 'Admin';
|
||||
// Response related
|
||||
const MAX_OUTPUT_CHUNK_SIZE = 2*1024*1024; // 2MB
|
||||
|
||||
$register = new Registry();
|
||||
|
||||
|
|
@ -126,6 +139,7 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
|
|||
*/
|
||||
Config::load('events', __DIR__.'/config/events.php');
|
||||
Config::load('auth', __DIR__.'/config/auth.php');
|
||||
Config::load('errors', __DIR__.'/config/errors.php');
|
||||
Config::load('providers', __DIR__.'/config/providers.php');
|
||||
Config::load('platforms', __DIR__.'/config/platforms.php');
|
||||
Config::load('collections', __DIR__.'/config/collections.php');
|
||||
|
|
@ -350,7 +364,7 @@ $register->set('logger', function () { // Register error logger
|
|||
}
|
||||
|
||||
if(!Logger::hasProvider($providerName)) {
|
||||
throw new Exception("Logging provider not supported. Logging disabled.");
|
||||
throw new Exception("Logging provider not supported. Logging disabled.", 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\'.\ucfirst($providerName);
|
||||
|
|
@ -695,7 +709,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response
|
|||
try {
|
||||
$payload = $jwt->decode($authJWT);
|
||||
} catch (JWTException $error) {
|
||||
throw new Exception('Failed to verify JWT. '.$error->getMessage(), 401);
|
||||
throw new Exception('Failed to verify JWT. '.$error->getMessage(), 401, Exception::USER_JWT_INVALID);
|
||||
}
|
||||
|
||||
$jwtUserId = $payload['userId'] ?? '';
|
||||
|
|
@ -781,6 +795,53 @@ App::setResource('dbForConsole', function($db, $cache) {
|
|||
return $database;
|
||||
}, ['db', 'cache']);
|
||||
|
||||
|
||||
App::setResource('deviceLocal', function() {
|
||||
return new Local();
|
||||
});
|
||||
|
||||
App::setResource('deviceFiles', function($project) {
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_LOCAL:default:
|
||||
return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_DEVICE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_DEVICE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_DEVICE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_DEVICE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
return new S3(APP_STORAGE_UPLOADS . '/app-' . $project->getId(), $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
return new DOSpaces(APP_STORAGE_UPLOADS . '/app-' . $project->getId(), $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
}
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('deviceFunctions', function($project) {
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_LOCAL:default:
|
||||
return new Local(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_DEVICE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_DEVICE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_DEVICE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_DEVICE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
return new S3(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId(), $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
return new DOSpaces(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId(), $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
}
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('mode', function($request) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ $cli
|
|||
$production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false;
|
||||
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
|
||||
|
||||
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', 'latest'])) {
|
||||
if(!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', 'latest'])) {
|
||||
throw new Exception('Unknown version given');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,15 @@ use Utopia\Database\Validator\Authorization;
|
|||
* database.collections.{collectionId}.documents.delete
|
||||
*
|
||||
* Storage
|
||||
*
|
||||
*
|
||||
* storage.buckets.create
|
||||
* storage.buckets.read
|
||||
* storage.buckets.update
|
||||
* storage.buckets.delete
|
||||
* storage.files.create
|
||||
* storage.files.read
|
||||
* storage.files.update
|
||||
* storage.files.delete
|
||||
* storage.buckets.{bucketId}.files.create
|
||||
* storage.buckets.{bucketId}.files.read
|
||||
* storage.buckets.{bucketId}.files.update
|
||||
|
|
@ -61,7 +69,9 @@ use Utopia\Database\Validator\Authorization;
|
|||
* Counters
|
||||
*
|
||||
* users.count
|
||||
* storage.buckets.count
|
||||
* storage.files.count
|
||||
* storage.buckets.{bucketId}.files.count
|
||||
* database.collections.count
|
||||
* database.documents.count
|
||||
* database.collections.{collectionId}.documents.count
|
||||
|
|
@ -142,6 +152,30 @@ $cli
|
|||
'table' => 'appwrite_usage_database_documents_delete',
|
||||
'groupBy' => 'collectionId',
|
||||
],
|
||||
'storage.buckets.create' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_create',
|
||||
],
|
||||
'storage.buckets.read' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_read',
|
||||
],
|
||||
'storage.buckets.update' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_update',
|
||||
],
|
||||
'storage.buckets.delete' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_delete',
|
||||
],
|
||||
'storage.files.create' => [
|
||||
'table' => 'appwrite_usage_storage_files_create',
|
||||
],
|
||||
'storage.files.read' => [
|
||||
'table' => 'appwrite_usage_storage_files_read',
|
||||
],
|
||||
'storage.files.update' => [
|
||||
'table' => 'appwrite_usage_storage_files_update',
|
||||
],
|
||||
'storage.files.delete' => [
|
||||
'table' => 'appwrite_usage_storage_files_delete',
|
||||
],
|
||||
'storage.buckets.bucketId.files.create' => [
|
||||
'table' => 'appwrite_usage_storage_files_create',
|
||||
'groupBy' => 'bucketId',
|
||||
|
|
@ -202,6 +236,8 @@ $cli
|
|||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
$db = null;
|
||||
$redis = null;
|
||||
do { // connect to db
|
||||
try {
|
||||
$attempts++;
|
||||
|
|
@ -241,47 +277,47 @@ $cli
|
|||
* @var InfluxDB\Client $client
|
||||
*/
|
||||
$client = $register->get('influxdb');
|
||||
if ($client) {
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
do { // check if telegraf database is ready
|
||||
try {
|
||||
$attempts++;
|
||||
$database = $client->selectDB('telegraf');
|
||||
if(in_array('telegraf', $client->listDatabases())) {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('InfluxDB database not ready yet');
|
||||
}
|
||||
sleep($sleep);
|
||||
do { // check if telegraf database is ready
|
||||
try {
|
||||
$attempts++;
|
||||
$database = $client->selectDB('telegraf');
|
||||
if(in_array('telegraf', $client->listDatabases())) {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('InfluxDB database not ready yet');
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
|
||||
// sync data
|
||||
foreach ($globalMetrics as $metric => $options) { //for each metrics
|
||||
foreach ($periods as $period) { // aggregate data for each period
|
||||
$start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339);
|
||||
if (!empty($latestTime[$metric][$period['key']])) {
|
||||
$start = DateTime::createFromFormat('U', $latestTime[$metric][$period['key']])->format(DateTime::RFC3339);
|
||||
}
|
||||
$end = DateTime::createFromFormat('U', \strtotime('now'))->format(DateTime::RFC3339);
|
||||
// sync data
|
||||
foreach ($globalMetrics as $metric => $options) { //for each metrics
|
||||
foreach ($periods as $period) { // aggregate data for each period
|
||||
$start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339);
|
||||
if (!empty($latestTime[$metric][$period['key']])) {
|
||||
$start = DateTime::createFromFormat('U', $latestTime[$metric][$period['key']])->format(DateTime::RFC3339);
|
||||
}
|
||||
$end = DateTime::createFromFormat('U', \strtotime('now'))->format(DateTime::RFC3339);
|
||||
|
||||
$table = $options['table']; //Which influxdb table to query for this metric
|
||||
$groupBy = empty($options['groupBy']) ? '' : ', "' . $options['groupBy'] . '"'; //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc
|
||||
$table = $options['table']; //Which influxdb table to query for this metric
|
||||
$groupBy = empty($options['groupBy']) ? '' : ', "' . $options['groupBy'] . '"'; //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc
|
||||
|
||||
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
|
||||
if (!empty($filters)) {
|
||||
$filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters)));
|
||||
} else {
|
||||
$filters = '';
|
||||
}
|
||||
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
|
||||
if (!empty($filters)) {
|
||||
$filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters)));
|
||||
} else {
|
||||
$filters = '';
|
||||
}
|
||||
|
||||
$query = "SELECT sum(value) AS \"value\" FROM \"{$table}\" WHERE \"time\" > '{$start}' AND \"time\" < '{$end}' AND \"metric_type\"='counter' {$filters} GROUP BY time({$period['key']}), \"projectId\" {$groupBy} FILL(null)";
|
||||
$query = "SELECT sum(value) AS \"value\" FROM \"{$table}\" WHERE \"time\" > '{$start}' AND \"time\" < '{$end}' AND \"metric_type\"='counter' {$filters} GROUP BY time({$period['key']}), \"projectId\" {$groupBy} FILL(null)";
|
||||
try {
|
||||
$result = $database->query($query);
|
||||
|
||||
$points = $result->getPoints();
|
||||
|
|
@ -289,7 +325,7 @@ $cli
|
|||
$projectId = $point['projectId'];
|
||||
|
||||
if (!empty($projectId) && $projectId !== 'console') {
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$dbForProject->setNamespace('_' . $projectId);
|
||||
$metricUpdated = $metric;
|
||||
|
||||
if (!empty($groupBy)) {
|
||||
|
|
@ -328,115 +364,305 @@ $cli
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Failed to Query: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($iterations % 30 != 0) { // Aggregate aggregate number of objects in database only after 15 minutes
|
||||
$iterations++;
|
||||
$loopTook = microtime(true) - $loopStart;
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate MariaDB every 15 minutes
|
||||
* Some of the queries here might contain full-table scans.
|
||||
*/
|
||||
if ($iterations % 30 === 0) { // Every 15 minutes aggregate number of objects in database
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregating database counters.");
|
||||
|
||||
$latestProject = null;
|
||||
do { // Loop over all the projects
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
$latestProject = null;
|
||||
|
||||
do { // Loop over all the projects
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
do { // list projects
|
||||
try {
|
||||
$attempts++;
|
||||
$projects = $dbForConsole->find('projects', [], 100, cursor: $latestProject);
|
||||
break; // leave the do-while if successful
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('Failed access console db: ' . $e->getMessage());
|
||||
}
|
||||
sleep($sleep);
|
||||
do { // list projects
|
||||
try {
|
||||
$attempts++;
|
||||
$projects = $dbForConsole->find('projects', [], 100, cursor: $latestProject);
|
||||
break; // leave the do-while if successful
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('Failed access console db: ' . $e->getMessage());
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
|
||||
if (empty($projects)) {
|
||||
continue;
|
||||
if (empty($projects)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestProject = $projects[array_key_last($projects)];
|
||||
|
||||
foreach ($projects as $project) {
|
||||
$projectId = $project->getId();
|
||||
|
||||
// Get total storage
|
||||
$dbForProject->setNamespace('_' . $projectId);
|
||||
$storageTotal = $dbForProject->sum('tags', 'size');
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_storage.tags.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => '30m',
|
||||
'time' => $time,
|
||||
'metric' => 'storage.tags.total',
|
||||
'value' => $storageTotal,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
$latestProject = $projects[array_key_last($projects)];
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_storage.tags.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => '1d',
|
||||
'time' => $time,
|
||||
'metric' => 'storage.tags.total',
|
||||
'value' => $storageTotal,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($projects as $project) {
|
||||
$projectId = $project->getId();
|
||||
|
||||
// Get total storage
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$storageTotal = $dbForProject->sum('files', 'sizeOriginal') + $dbForProject->sum('tags', 'size');
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_storage.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => '30m',
|
||||
'time' => $time,
|
||||
'metric' => 'storage.total',
|
||||
'value' => $storageTotal,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_storage.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => '1d',
|
||||
'time' => $time,
|
||||
'metric' => 'storage.total',
|
||||
'value' => $storageTotal,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
$collections = [
|
||||
'users' => [
|
||||
'namespace' => 'internal',
|
||||
],
|
||||
'collections' => [
|
||||
'metricPrefix' => 'database',
|
||||
'namespace' => 'internal',
|
||||
'subCollections' => [ // Some collections, like collections and later buckets have child collections that need counting
|
||||
'documents' => [
|
||||
'namespace' => 'external',
|
||||
],
|
||||
$collections = [
|
||||
'users' => [
|
||||
'namespace' => '',
|
||||
],
|
||||
'collections' => [
|
||||
'metricPrefix' => 'database',
|
||||
'namespace' => '',
|
||||
'subCollections' => [ // Some collections, like collections and later buckets have child collections that need counting
|
||||
'documents' => [
|
||||
'collectionPrefix' => 'collection_',
|
||||
'namespace' => '',
|
||||
],
|
||||
],
|
||||
'files' => [
|
||||
'metricPrefix' => 'storage',
|
||||
'namespace' => 'internal',
|
||||
],
|
||||
];
|
||||
],
|
||||
'buckets' => [
|
||||
'metricPrefix' => 'storage',
|
||||
'namespace' => '',
|
||||
'subCollections' => [
|
||||
'files' => [
|
||||
'namespace' => '',
|
||||
'collectionPrefix' => 'bucket_',
|
||||
'sum' => [
|
||||
'field' => 'sizeOriginal'
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($collections as $collection => $options) {
|
||||
try {
|
||||
foreach ($collections as $collection => $options) {
|
||||
try {
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$count = $dbForProject->count($collection);
|
||||
$metricPrefix = $options['metricPrefix'] ?? '';
|
||||
$metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count";
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$subCollections = $options['subCollections'] ?? [];
|
||||
|
||||
if (empty($subCollections)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestParent = null;
|
||||
$subCollectionCounts = []; //total project level count of sub collections
|
||||
$subCollectionTotals = []; //total project level sum of sub collections
|
||||
|
||||
do { // Loop over all the parent collection document for each sub collection
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$count = $dbForProject->count($collection);
|
||||
$metricPrefix = $options['metricPrefix'] ?? '';
|
||||
$metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count";
|
||||
$parents = $dbForProject->find($collection, [], 100, cursor: $latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||
|
||||
if (empty($parents)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestParent = $parents[array_key_last($parents)];
|
||||
|
||||
foreach ($parents as $parent) {
|
||||
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$count = $dbForProject->count(($subOptions['collectionPrefix'] ?? '') . $parent->getId());
|
||||
|
||||
$subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count
|
||||
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.count";
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
// check if sum calculation is required
|
||||
$sum = $subOptions['sum'] ?? [];
|
||||
if(empty($sum)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$total = (int) $dbForProject->sum(($subOptions['collectionPrefix'] ?? '') . $parent->getId(), $sum['field']);
|
||||
|
||||
$subCollectionTotals[$subCollection] = ($ssubCollectionTotals[$subCollection] ?? 0) + $total; // Project level sum for sub collections like storage.total
|
||||
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.total" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.total";
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $total,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $total));
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $total,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $total));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} while (!empty($parents));
|
||||
|
||||
/**
|
||||
* Inserting project level counts for sub collections like database.documents.count
|
||||
*/
|
||||
foreach ($subCollectionCounts as $subCollection => $count) {
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count";
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
|
|
@ -477,134 +703,56 @@ $cli
|
|||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$subCollections = $options['subCollections'] ?? [];
|
||||
|
||||
if (empty($subCollections)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestParent = null;
|
||||
$subCollectionCounts = []; //total project level count of sub collections
|
||||
|
||||
do { // Loop over all the parent collection document for each sub collection
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$parents = $dbForProject->find($collection, [], 100, cursor: $latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||
|
||||
if (empty($parents)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latestParent = $parents[array_key_last($parents)];
|
||||
|
||||
foreach ($parents as $parent) {
|
||||
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$count = $dbForProject->count($parent->getId());
|
||||
|
||||
$subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count
|
||||
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.count";
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!empty($parents));
|
||||
|
||||
/**
|
||||
* Inserting project level counts for sub collections like database.documents.count
|
||||
*/
|
||||
foreach ($subCollectionCounts as $subCollection => $count) {
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count";
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserting project level sums for sub collections like storage.total
|
||||
*/
|
||||
foreach ($subCollectionTotals as $subCollection => $count) {
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$subCollection}.total" : "{$metricPrefix}.{$subCollection}.total";
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
}
|
||||
}
|
||||
} catch (\Exception$e) {
|
||||
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
} while (!empty($projects));
|
||||
}
|
||||
}
|
||||
} while (!empty($projects));
|
||||
|
||||
$iterations++;
|
||||
$loopTook = microtime(true) - $loopStart;
|
||||
|
|
@ -612,4 +760,4 @@ $cli
|
|||
|
||||
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||
}, $interval);
|
||||
});
|
||||
});
|
||||
|
|
@ -251,8 +251,10 @@
|
|||
</span>
|
||||
|
||||
<div class="pull-start margin-end avatar-container">
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=60&height=60&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
|
||||
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" data-ls-if="{{session.clientCode|lowercase}} !== 'cli'"/>
|
||||
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" data-ls-if="{{session.clientCode|lowercase}} === 'cli'" >
|
||||
|
||||
<div data-ls-if="{{session.provider}} !== 'email'" class="corner">
|
||||
<img data-ls-attrs="src=/images/users/{{session.provider}}.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.provider}},alt={{session.provider}}" class="avatar xs" loading="lazy" width="30" height="30" />
|
||||
</div>
|
||||
|
|
@ -317,7 +319,10 @@
|
|||
<td data-title="Date: "><span data-ls-bind="{{log.time|dateTime}}"></span></td>
|
||||
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
|
||||
<td data-title="Client: ">
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" data-ls-if="{{log.clientCode|lowercase}} !== 'cli'"/>
|
||||
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" />
|
||||
|
||||
<span data-ls-if="(!{{log.clientName}})">Unknown</span>
|
||||
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ $version = $this->getParam('version', '') . '.' . APP_CACHE_BUSTER;
|
|||
data-analytics-event="click"
|
||||
data-analytics-category="console/footer"
|
||||
data-analytics-label="GitHub Link"
|
||||
href="https://github.com/appwrite/appwrite" target="_blank" rel="noopener"><i class="icon-github-circled"></i> GitHub</a>
|
||||
href="https://github.com/appwrite/appwrite" target="_blank" rel="noopener"><i class="icon-github"></i> GitHub</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="link-animation-enabled"
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
<i class="icon-sun-inv force-light"></i>
|
||||
<i class="icon-moon-inv force-dark"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -222,3 +222,38 @@
|
|||
</footer>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<section class="upload-box" x-data x-show="$store.uploader.files().length > 0">
|
||||
<div class="upload-box-header">
|
||||
<h4 class="upload-box-title">
|
||||
<span class="text">Uploading files</span>
|
||||
<span class="amount" x-text="$store.uploader.files().length"></span>
|
||||
</h4>
|
||||
<button class="icon-button" :class="$store.uploader.isOpen ? '' : 'is-open'" aria-label="toggle upload box" @click="$store.uploader.toggle()"><span class="icon-chevron-down" aria-hidden="true"></span></button>
|
||||
<button class="icon-button" @click="$store.uploader.cancelAll()" aria-label="close upload box"><span class="icon-x" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
<div class="upload-box-content" :class="$store.uploader.isOpen ? 'is-open' : ''">
|
||||
<ul class="upload-box-list">
|
||||
<template x-if="$store.uploader.files().length > 0">
|
||||
<template x-for="file in $store.uploader.files()" :key="file.id">
|
||||
<li x-show="!file.cancelled" class="upload-box-item">
|
||||
<div class="upload-image u-margin-inline-end-16" :class="file.completed ? 'is-finished' : ''">
|
||||
<div class="progress"
|
||||
:style="'--progress-value:' + file.progress"
|
||||
:aria-valuenow="file.progress"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"></div>
|
||||
<span x-show="!file.completed" class="icon" x-text="file.progress + '%'"></span>
|
||||
<span x-show="file.completed" class="icon icon-file"></span>
|
||||
</div>
|
||||
<label for="file1" class="file-name trim-inner-text" x-text="file.name" :title="file.name"></label>
|
||||
<span class="pill is-failed" x-show="file.failed" :title="file.error">failed</span>
|
||||
<button x-show="file.completed" class="icon-button is-success" aria-label="Uploading"><span class="icon-check"></span></button>
|
||||
<button x-show="!file.completed" class="icon-button" aria-label="Uploading" @click="$store.uploader.removeFile(file.id)"><span class="icon-x"></span></button>
|
||||
</li>
|
||||
</template>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
6
app/views/console/comps/upload-modal.phtml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<div x-data id="upload-modal">
|
||||
<template x-for="$store.uploader.files as file">
|
||||
<p x-text="file"></p>
|
||||
</template>
|
||||
<button @click="$store.uploader.addFile('file-x')">add</button>
|
||||
</div>
|
||||
|
|
@ -494,7 +494,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
<input type="hidden" data-forms-key-value data-ls-attrs="name={{$index}}" data-ls-bind="{{var}}" />
|
||||
</div>
|
||||
<div class="col span-2">
|
||||
<button type="button" data-remove class="reverse danger round pull-end"><i class="icon-cancel"></i></button>
|
||||
<button type="button" data-remove class="close pull-end is-margin-top-10"><i class="icon-cancel"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -507,7 +507,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
<input type="hidden" data-ls-attrs="data-forms-key-value"/>
|
||||
</div>
|
||||
<div class="col span-2">
|
||||
<button type="button" data-remove class="reverse danger round pull-end"><i class="icon-cancel"></i></button>
|
||||
<button type="button" data-remove class="close pull-end is-margin-top-10"><i class="icon-cancel"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ $home = $this->getParam('home', '');
|
|||
data-analytics
|
||||
data-analytics-type="click"
|
||||
data-analytics-category="console/home"
|
||||
data-analytics-label="GitHub Link"><i class="icon-github-circled"></i></a>
|
||||
data-analytics-label="GitHub Link"><i class="icon-github"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col span-6 margin-bottom">
|
||||
|
|
|
|||
|
|
@ -241,11 +241,6 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="text-align-center text-size-small text-bold text-success" data-ls-if="!!({{console-project.serviceStatusFor<?php echo ucFirst($this->escape($key)); ?>}})">Enabled</div>
|
||||
<div class="text-align-center text-size-small text-bold text-danger" data-ls-if="(!{{console-project.serviceStatusFor<?php echo ucFirst($this->escape($key)); ?>}})">Disabled</div>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
|
|
@ -548,7 +543,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<div class="margin-bottom-tiny">
|
||||
<span data-ls-bind="{{member.name}}"></span> <span class="tag" data-ls-bind="{{member.roles.0}}"></span> <span data-ls-if="false === {{member.confirm}}" class="tag red">Pending Approval</span>
|
||||
</div>
|
||||
<span class="text-size-small text-fade" data-ls-bind="{{member.email}}"></small>
|
||||
<span class="text-size-small text-fade" data-ls-bind="{{member.email}}"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
505
app/views/console/storage/bucket.phtml
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
<?php
|
||||
$home = $this->getParam('home', '');
|
||||
$fileLimit = $this->getParam('fileLimit', 0);
|
||||
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
||||
?>
|
||||
|
||||
<div
|
||||
data-service="storage.getBucket"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-scope="sdk"
|
||||
data-event="load,storage.updateBucket"
|
||||
data-name="project-bucket">
|
||||
|
||||
<div class="cover">
|
||||
<h1 class="zone xl margin-bottom-large">
|
||||
<a data-ls-attrs="href=/console/storage?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Storage</a>
|
||||
|
||||
<br />
|
||||
|
||||
<span data-ls-bind="{{project-bucket.name}}"> </span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h2>JSON View</h2>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input type="hidden" data-ls-bind="{{project-bucket}}" data-forms-code />
|
||||
</div>
|
||||
|
||||
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</div>
|
||||
|
||||
<div class="zone xl">
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/storage/bucket?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<h2 class="margin-bottom">Files</h2>
|
||||
|
||||
<form class="box padding-small margin-bottom search"
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset=""
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<div class="row thin responsive">
|
||||
<div class="col span-10">
|
||||
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
|
||||
</div>
|
||||
<div class="col span-2 desktops-only">
|
||||
<button class="fill" title="Search" aria-label="Search">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div
|
||||
data-service="storage.listFiles"
|
||||
data-event="load,storage.createFile,storage.updateFile,storage.deleteFile"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files">
|
||||
|
||||
<div data-ls-if="0 == {{project-files.sum}}" class="box margin-bottom">
|
||||
<h3 class="margin-bottom-small text-bold">No Files Found</h3>
|
||||
|
||||
<p class="margin-bottom-no">Upload your first file to get started</p>
|
||||
</div>
|
||||
|
||||
<div data-ls-if="0 != {{project-files.sum}}">
|
||||
<div class="margin-bottom-small text-align-end text-size-small text-fade"><span data-ls-bind="{{project-files.sum}}"></span> files found</div>
|
||||
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical" style="overflow: visible">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40"></th>
|
||||
<th>Filename</th>
|
||||
<th width="140">Type</th>
|
||||
<th width="100">Size</th>
|
||||
<th width="120">Created</th>
|
||||
<th width="52"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="project-files.files" data-ls-as="file">
|
||||
<tr>
|
||||
<td class="hide">
|
||||
<img src="" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" alt="" />
|
||||
</td>
|
||||
<td class="col-name" data-title="Name: " data-ls-attrs="title={{file.name}}" >
|
||||
<div class="trim-inner-text">
|
||||
<span data-ls-ui-trigger="modal-file-update-{{file.$id}}" class="link text-one-liner" data-ls-bind="{{file.name}}"></span>
|
||||
<div data-ls-attrs="data-open-event=modal-file-update-{{file.$id}}" data-button-hide="1" data-ui-modal class="box modal sticky-footer width-large close" >
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Update File</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="row responsive modalize">
|
||||
<div class="col span-8">
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Update Storage File"
|
||||
data-service="storage.updateFile"
|
||||
data-event="file-update-{{file.$id}}"
|
||||
data-scope="sdk"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="File updated successfully"
|
||||
data-success-param-trigger-events="storage.updateFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<label for="files-fileId">File ID</label>
|
||||
<div class="input-copy">
|
||||
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
|
||||
</div>
|
||||
<input type="hidden" data-ls-attrs="id=file-bucketId-{{file.$id}}" name="bucketId" data-ls-bind="{{file.bucketId}}">
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</form>
|
||||
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Delete File"
|
||||
data-service="storage.deleteFile"
|
||||
data-scope="sdk"
|
||||
data-event="file-delete-{{file.$id}}"
|
||||
data-confirm="Are you sure you want to delete this file?"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Deleted file successfully"
|
||||
data-success-param-trigger-events="storage.deleteFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to delete file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<input type="hidden" name="bucketId" data-ls-bind="{{file.bucketId}}" />
|
||||
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="col span-4 text-size-small">
|
||||
<div class="margin-bottom-small">File Preview</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<div data-ls-if="{{file.chunksTotal}} != {{file.chunksUploaded}}" class="preview-box">
|
||||
<div class="preview-box-image">
|
||||
<img src="/images/default_preview.svg" width="200" height="100" alt=""/>
|
||||
</div>
|
||||
<span class="preview-box-text">Preview not available</span>
|
||||
</div>
|
||||
<img data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" src="" class="file-preview" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="225" height="160" />
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Type: <span data-ls-bind="{{file.mimeType}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Size: <span data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
|
||||
<button type="button" data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
<span class="pill is-pending" data-ls-if="{{file.chunksTotal}} != {{file.chunksUploaded}}">pending</span>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="Type: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.mimeType}}"></span>
|
||||
</td>
|
||||
<td data-title="Size: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</td>
|
||||
<td data-title="Created: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</td>
|
||||
<td data-title="" class="cell-options-more" style="overflow: visible">
|
||||
<div class="drop-list end" data-ls-ui-open="" data-button-aria="File Options" data-button-class="icon-dot-3 reset-inner-button" data-blur="1">
|
||||
<ul class="no-arrow">
|
||||
<li data-ls-ui-trigger="modal-file-update-{{file.$id}}"><span class="margin-start-small icon icon-edit"></span> <span class="link">Update</span></li>
|
||||
<li data-ls-ui-trigger="file-delete-{{file.$id}}"><span class="margin-start-small icon icon-trash-2"></span><span class="link">Delete</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-files.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="box modal sticky-footer close" data-button-text="Add File">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Upload File</h1>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Create Storage File"
|
||||
x-data
|
||||
@submit.prevent="$store.uploader.uploadFile($event.target)"
|
||||
>
|
||||
<input type="hidden" name="bucketId" id="files-bucketId" data-ls-bind="{{router.params.id}}">
|
||||
|
||||
<label for="fileId">File ID</label>
|
||||
<input
|
||||
type="hidden"
|
||||
data-custom-id
|
||||
data-id-type="auto"
|
||||
data-validator="storage.getFile"
|
||||
required
|
||||
maxlength="36"
|
||||
name="fileId"
|
||||
id="fileId" />
|
||||
|
||||
<label for="file-read">File</label>
|
||||
<input type="file" name="file" id="file-file" size="1" required>
|
||||
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-read" name="read" data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['role:all'])); ?>" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-write" name="write" data-forms-tags data-cast-to="json" value="" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</footer>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/storage/bucket/usage?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="storage.getBucketUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="storage.getBucketUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-bucket-id="{{router.params.id}}">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="storage.getBucketUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<div data-service="storage.getBucketUsage" data-event="load" data-name="usage" data-param-bucket-id="{{router.params.id}}">
|
||||
<h3 class="margin-bottom-tiny">Files</h3>
|
||||
<p class="text-fade">Count of files over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-show-y-axis="true" data-forms-chart="Files=filesCount" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Files </li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Operations</h3>
|
||||
<p class="text-fade">Count of files create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-show-y-axis="true" data-forms-chart="Create=filesCreate,Read=filesRead,Updated=filesUpdate,Deleted=filesDelete" data-colors="create,read,update,delete" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes crud margin-bottom-large">
|
||||
<li>Create</li>
|
||||
<li>Read</li>
|
||||
<li>Update</li>
|
||||
<li>Delete</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/storage/bucket/settings?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<h2>Settings</h2>
|
||||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Update Storage Bucket"
|
||||
data-service="storage.updateBucket"
|
||||
data-scope="sdk"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Updated bucket successfully"
|
||||
data-success-param-trigger-events="storage.updateBucket"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update bucket"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<label> </label>
|
||||
|
||||
<div class="box">
|
||||
<label for="bucket-name">Name</label>
|
||||
<input name="name" id="bucket-name" type="text" autocomplete="off" data-ls-bind="{{project-bucket.name}}" data-forms-text-direction required placeholder="Bucket Name" maxlength="128" />
|
||||
|
||||
<label for="bucket-maximum-file-size">Maximum File Size (bytes) <span class="tooltip small" data-tooltip="Limit file size allowed in the bucket."><i class="icon-info-circled"></i></span></label>
|
||||
<input name="maximumFileSize" id="bucket-maximum-file-size" type="number" autocomplete="off" data-ls-bind="{{project-bucket.maximumFileSize}}" min="1" data-cast-to="integer" />
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input name="enabled" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.enabled}}" /> Enabled <span class="tooltip" data-tooltip="Mark whether bucket is enabled"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input name="encryption" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.encryption}}" /> Encryption <span class="tooltip" data-tooltip="Mark whether bucket is encrypted"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input name="antivirus" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.antivirus}}" /> Anti Virus <span class="tooltip" data-tooltip="Mark whether anti virus scanning is enabled"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<label for="bucket-allowedFileExtensions">Allowed File Extensions</label>
|
||||
<input type="hidden" id="bucket-allowedFileExtensions" name="allowedFileExtensions" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.allowedFileExtensions}}" placeholder="Allowed file extensions (pdf, mp4)" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Leave empty to allow all.</div>
|
||||
|
||||
|
||||
<label class="margin-bottom-small">Permissions</label>
|
||||
|
||||
<p class="text-fade text-size-small">Choose the permissions model for this bucket.</p>
|
||||
|
||||
<hr class="margin-top-small" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="bucket" type="radio" class="margin-top-tiny" data-ls-bind="{{project-bucket.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Bucket Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Bucket Level permissions, you assign permissions only once in the bucket.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level permissions assigned to bucket takes the precedence and file permissions are ignored</p>
|
||||
<div data-ls-if="{{project-bucket.permission}} == 'bucket'">
|
||||
|
||||
<label for="bucket-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="bucket-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$permissions.read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="bucket-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="bucket-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$permissions.write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="file" type="radio" class="margin-top-no" data-ls-bind="{{project-bucket.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>File Level</b>
|
||||
<p class="text-fade margin-top-tiny">With File Level permissions, you have granular access control over every file. Users will only be able to access files for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level file permissions take precedence and bucket permissions are ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="margin-top-no" />
|
||||
|
||||
<button>Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col span-4 sticky-top">
|
||||
<label>Bucket ID</label>
|
||||
<div class="input-copy margin-bottom">
|
||||
<input id="id" type="text" autocomplete="off" placeholder="" data-ls-bind="{{project-bucket.$id}}" disabled data-forms-copy class="margin-bottom-no" />
|
||||
</div>
|
||||
|
||||
<ul class="margin-bottom-large text-fade text-size-small">
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-bucket.dateUpdated|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-bucket.dateCreated|dateText}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form name="storage.deleteBucket" class="margin-bottom"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Delete Storage Bucket"
|
||||
data-service="storage.deleteBucket"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-confirm="Are you sure you want to delete this bucket?"
|
||||
data-success="alert,trigger,redirect"
|
||||
data-success-param-alert-text="Bucket deleted successfully"
|
||||
data-success-param-trigger-events="storage.deleteBucket"
|
||||
data-success-param-redirect-url="/console/storage?project={{router.params.project}}"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to delete bucket"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<button type="submit" class="danger fill">Delete Bucket</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
<?php
|
||||
$home = $this->getParam('home', '');
|
||||
$fileLimit = $this->getParam('fileLimit', 0);
|
||||
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
||||
?>
|
||||
<div class="cover">
|
||||
<h1 class="zone xl margin-bottom-large">
|
||||
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
|
||||
|
|
@ -15,320 +10,203 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
<div class="zone xl">
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/storage?project={{router.params.project}}">
|
||||
<h2 class="margin-bottom">Files</h2>
|
||||
<h2>Buckets</h2>
|
||||
|
||||
<form class="box padding-small margin-bottom search"
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset=""
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<div class="row thin responsive">
|
||||
<div class="col span-10">
|
||||
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
|
||||
</div>
|
||||
<div class="col span-2 desktops-only">
|
||||
<button class="fill" title="Search" aria-label="Search">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div
|
||||
data-service="storage.listFiles"
|
||||
data-event="load,storage.createFile,storage.updateFile,storage.deleteFile"
|
||||
<div class="margin-top"
|
||||
data-service="storage.listBuckets"
|
||||
data-event="load,storage.createBucket,storage.updateBucket,storage.deleteBucket"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files">
|
||||
data-name="project-buckets">
|
||||
|
||||
<div data-ls-if="0 == {{project-files.sum}}" class="box margin-bottom">
|
||||
<h3 class="margin-bottom-small text-bold">No Files Found</h3>
|
||||
<div data-ls-if="(!{{project-buckets.sum}})" class="box dashboard margin-bottom">
|
||||
<div class="margin-bottom-small margin-top-small margin-end margin-start">
|
||||
<h3 class="margin-bottom-small text-bold">No Buckets Found</h3>
|
||||
|
||||
<p class="margin-bottom-no">Upload your first file to get started</p>
|
||||
<p class="margin-bottom-no">You haven't created any buckets for your project yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-ls-if="0 != {{project-files.sum}}">
|
||||
<div class="margin-bottom-small text-align-end text-size-small text-fade"><span data-ls-bind="{{project-files.sum}}"></span> files found</div>
|
||||
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40"></th>
|
||||
<th>Filename</th>
|
||||
<th width="140">Type</th>
|
||||
<th width="100">Size</th>
|
||||
<th width="120">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="project-files.files" data-ls-as="file">
|
||||
<tr>
|
||||
<td class="hide">
|
||||
<img src="" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" />
|
||||
</td>
|
||||
<td data-title="Name: " class="text-one-liner" data-ls-attrs="title={{file.name}}" >
|
||||
<div data-ui-modal class="box modal sticky-footer width-large close" data-button-text="{{file.name}}" data-button-class="link" data-button-element="span">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Update File</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="row responsive modalize">
|
||||
<div class="col span-8">
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Update Storage File"
|
||||
data-service="storage.updateFile"
|
||||
data-event="file-update-{{file.$id}}"
|
||||
data-scope="sdk"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="File updated successfully"
|
||||
data-success-param-trigger-events="storage.updateFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<label for="files-fileId">File ID</label>
|
||||
<div class="input-copy">
|
||||
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
|
||||
</div>
|
||||
<input type="hidden" data-ls-attrs="id=file-folderId-{{file.$id}}" name="folderId" data-cast-to="integer" value="1">
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</form>
|
||||
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Delete File"
|
||||
data-service="storage.deleteFile"
|
||||
data-scope="sdk"
|
||||
data-event="file-delete-{{file.$id}}"
|
||||
data-confirm="Are you sure you want to delete this file?"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Deleted file successfully"
|
||||
data-success-param-trigger-events="storage.deleteFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to delete file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="col span-4 text-size-small">
|
||||
<div class="margin-bottom-small">File Preview</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<img src="" class="file-preview" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="225" height="160" />
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Type: <span data-ls-bind="{{file.mimeType}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Size: <span data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
|
||||
<button type="button" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="Type: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.mimeType}}"></span>
|
||||
</td>
|
||||
<td data-title="Size: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</td>
|
||||
<td data-title="Created: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div data-ls-if="0 != {{project-buckets.sum}}">
|
||||
<ul data-ls-loop="project-buckets.buckets" data-ls-as="bucket" data-ls-append="" class="tiles cell-3 margin-bottom-small">
|
||||
<li class="margin-bottom">
|
||||
<a data-ls-attrs="href=/console/storage/bucket?id={{bucket.$id}}&project={{router.params.project}}" class="box">
|
||||
<div data-ls-bind="{{bucket.name}}" class="text-one-liner margin-bottom text-bold"> </div>
|
||||
<i class="icon-right-open"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-service="storage.listBuckets"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-name="project-buckets"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-buckets.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-files.sum|pageTotal}}"></span>
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-buckets.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-service="storage.listBuckets"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-name="project-buckets"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-buckets.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="box modal sticky-footer close" data-button-text="Add File">
|
||||
<div data-ui-modal class="modal close box sticky-footer" data-button-text="Add Bucket">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Upload File</h1>
|
||||
<h1>New Bucket</h1>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Create Storage File"
|
||||
data-service="storage.createFile"
|
||||
data-analytics-label="Create Storage Bucket"
|
||||
data-service="storage.createBucket"
|
||||
data-event="submit"
|
||||
data-scope="sdk"
|
||||
data-loading="Uploading File..."
|
||||
data-success="alert,trigger,reset"
|
||||
data-success-param-alert-text="File uploaded successfully"
|
||||
data-success-param-trigger-events="storage.createFile"
|
||||
data-success="alert,reset,redirect,trigger"
|
||||
data-success-param-alert-text="Bucket created successfully"
|
||||
data-success-param-redirect-url="/console/storage/bucket/settings?id={{serviceData.$id}}&project={{router.params.project}}"
|
||||
data-success-param-trigger-events="storage.createBucket"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to upload file"
|
||||
data-failure-param-alert-text="Failed to create bucket"
|
||||
data-failure-param-alert-classname="error">
|
||||
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="integer" value="1">
|
||||
|
||||
<label for="fileId">File ID</label>
|
||||
<label for="bucket-id">Bucket ID</label>
|
||||
<input
|
||||
type="hidden"
|
||||
data-custom-id
|
||||
data-id-type="auto"
|
||||
data-validator="storage.getFile"
|
||||
data-validator="storage.getBucket"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9._-]{1,36}$"
|
||||
name="fileId"
|
||||
id="fileId" />
|
||||
name="bucketId"
|
||||
id="bucketId" />
|
||||
|
||||
<label for="file-read">File</label>
|
||||
<input type="file" name="file" id="file-file" size="1" required>
|
||||
<label for="bucket-name">Name</label>
|
||||
<input type="text" class="full-width" id="bucket-name" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
|
||||
<input type="hidden" id="bucket-permission" name="permission" required value="bucket" />
|
||||
<input type="hidden" id="bucket-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="bucket-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-read" name="read" data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['role:all'])); ?>" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<hr />
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-write" name="write" data-forms-tags data-cast-to="json" value="" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/storage/usage?project={{router.params.project}}">
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
|
||||
</li>
|
||||
|
||||
<li data-state="/console/storage/usage?project={{router.params.project}}">
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h2>Usage</h2>
|
||||
|
||||
<div
|
||||
data-service="storage.getUsage"
|
||||
data-event="load"
|
||||
data-name="usage">
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Total Files=files" data-height="140" data-show-y-axis="true" />
|
||||
<div
|
||||
data-service="storage.getUsage"
|
||||
data-event="load"
|
||||
data-name="usage">
|
||||
<h3 class="margin-bottom-tiny">Objects</h3>
|
||||
<p class="text-fade">Count of buckets, files and total storage used over time</p>
|
||||
<div class="box">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-show-y-axis="true" data-forms-chart="Total Files=filesCount,Total Buckets=bucketsCount" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Files <span data-ls-bind="({{usage.files|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Total Storage=storage" data-height="140" data-show-y-axis="true" />
|
||||
<ul class="chart-notes margin-top-small margin-bottom-large">
|
||||
<li>Total Files</li>
|
||||
<li>Total Buckets</li>
|
||||
<!-- <li>Total Storage <span data-ls-bind="({{usage.filesStorage|statsGetLast|statsTotal}})"></span></li> -->
|
||||
</ul>
|
||||
|
||||
<!-- CRUDS class for graph fixed colors, use color codes from Docs for CRUD operations -->
|
||||
<h3 class="margin-bottom-tiny">Buckets</h3>
|
||||
<p class="text-fade">Count of bucket create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no crud margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Buckets create=bucketsCreate,Buckets read=bucketsRead,Buckets update=bucketsUpdate,Buckets delete=bucketsDelete" data-show-y-axis="true" data-colors="create,read,update,delete" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Storage (<span data-ls-bind="{{usage.storage|statsGetLast|humanFileSize}}"></span> <span data-ls-bind="{{usage.storage|statsGetLast|humanFileUnit}}"></span>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="chart-notes crud margin-bottom-large">
|
||||
<li class="green">Create</li>
|
||||
<li class="green">Read</li>
|
||||
<li class="green">Update</li>
|
||||
<li class="green">Delete</li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Files</h3>
|
||||
<p class="text-fade">Count of file create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no crud margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Files create=filesCreate,Files read=filesRead,Files update=filesUpdate,Files delete=filesDelete" data-show-y-axis="true" data-colors="create,read,update,delete" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes crud margin-bottom-large">
|
||||
<li class="green">Create</li>
|
||||
<li class="green">Read</li>
|
||||
<li class="green">Update</li>
|
||||
<li class="green">Delete</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -79,13 +79,13 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</td>
|
||||
<td data-title="Name: ">
|
||||
<a data-ls-attrs="href=/console/users/user?id={{user.$id}}&project={{router.params.project}}">
|
||||
<span data-ls-bind="{{user.name}}"></span>
|
||||
<span data-ls-bind="{{user.name}}" data-ls-attrs="title={{user.name}}"></span>
|
||||
<span data-ls-if="{{user.name|escape}} === '' && {{user.email}} !== ''">Unknown</span>
|
||||
<span data-ls-if="{{user.name|escape}} === '' && {{user.email}} === ''">Anonymous User</span>
|
||||
</a>
|
||||
</td>
|
||||
<td data-title="Email: ">
|
||||
<small data-ls-bind="{{user.email}}"></span>
|
||||
<small data-ls-bind="{{user.email}}" data-ls-attrs="title={{user.email}}"></span>
|
||||
</td>
|
||||
<td data-title="Status: ">
|
||||
<span data-ls-if="{{user.emailVerification}} === true && {{user.status}} === true">
|
||||
|
|
@ -245,7 +245,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<img src="" data-ls-attrs="src={{team.name|avatar}}" data-size="45" alt="Collection Avatar" class="avatar margin-end pull-start" loading="lazy" width="30" height="30" />
|
||||
</td>
|
||||
<td data-title="Name: ">
|
||||
<a data-ls-attrs="href=/console/users/teams/team?id={{team.$id}}&project={{router.params.project}}" data-ls-bind="{{team.name}}"></a>
|
||||
<a data-ls-attrs="href=/console/users/teams/team?id={{team.$id}}&project={{router.params.project}}" data-ls-bind="{{team.name}}" data-ls-attrs="title={{team.name}}"></a>
|
||||
</td>
|
||||
<td data-title="Members: "><span data-ls-bind="{{team.sum}} members"></span></td>
|
||||
<td data-title="Date Created: "><small data-ls-bind="{{team.dateCreated|dateText}}"></small></td>
|
||||
|
|
@ -406,7 +406,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<img src="<?php echo $this->escape($icon); ?>?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="Email/Password Logo" class="pull-start provider margin-end" />
|
||||
|
||||
<span class="text-size-small"><?php echo $this->escape($name); ?><?php if(!$enabled): ?> <spann class="text-fade text-size-xs">soon</span><?php endif; ?>
|
||||
<span class="text-size-small"><?php echo $this->escape($name); ?><?php if(!$enabled): ?> <span class="text-fade text-size-xs">soon</span><?php endif; ?>
|
||||
|
||||
<?php if( in_array($key, ['usersAuthMagicURL', 'usersAuthInvites']) && !$smtpEnabled): ?>
|
||||
<p class="margin-bottom-no text-one-liner text-size-small text-danger">
|
||||
|
|
|
|||
|
|
@ -295,8 +295,8 @@
|
|||
data-param-user-id="{{router.params.id}}"
|
||||
data-event="load,users.update">
|
||||
|
||||
<div data-ls-if="{{sessions.sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
|
||||
No sessions available.
|
||||
<div class="box margin-top margin-bottom" data-ls-if="{{sessions.sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
|
||||
<h3 class="text-bold margin-bottom-no">No sessions available.</h3>
|
||||
</div>
|
||||
|
||||
<div data-ls-if="{{sessions.sessions.length}} !== 0" style="display: none">
|
||||
|
|
@ -372,8 +372,8 @@
|
|||
data-param-user-id="{{router.params.id}}"
|
||||
data-event="load,logs-load">
|
||||
|
||||
<div data-ls-if="{{logs.logs.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
|
||||
No logs available.
|
||||
<div class="box margin-top margin-bottom" data-ls-if="{{logs.logs.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
|
||||
<h3 class="text-bold margin-bottom-no">No logs available.</h3>
|
||||
</div>
|
||||
|
||||
<div class="box" data-ls-if="{{logs.logs.length}} !== 0" style="display: none">
|
||||
|
|
@ -394,7 +394,7 @@
|
|||
<td data-title="Client: ">
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} !== 'cli'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
|
||||
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
|
||||
|
||||
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
|
||||
<div data-ls-if="(!{{log.clientName}})" class="text-align-center text-fade">Unknown</div>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Appwrite\Resque\Worker;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\CLI\Console;
|
||||
|
|
@ -56,6 +60,9 @@ class DeletesV1 extends Worker
|
|||
case DELETE_TYPE_TEAMS:
|
||||
$this->deleteMemberships($document, $projectId);
|
||||
break;
|
||||
case DELETE_TYPE_BUCKETS:
|
||||
$this->deleteBucket($document, $projectId);
|
||||
break;
|
||||
default:
|
||||
Console::error('No lazy delete operation available for document of type: ' . $document->getCollection());
|
||||
break;
|
||||
|
|
@ -448,4 +455,34 @@ class DeletesV1 extends Worker
|
|||
Console::info("No certificate files found for {$domain}");
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteBucket(Document $document, string $projectId)
|
||||
{
|
||||
$bucketId = $document->getId();
|
||||
$dbForProject = $this->getProjectDB($projectId);
|
||||
$dbForProject->deleteCollection('bucket_' . $bucketId);
|
||||
|
||||
$device = new Local(APP_STORAGE_UPLOADS.'/app-'.$projectId);
|
||||
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_DEVICE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_DEVICE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_DEVICE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_DEVICE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
$device = new S3(APP_STORAGE_UPLOADS . '/app-' . $projectId, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
break;
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
$device = new DOSpaces(APP_STORAGE_UPLOADS . '/app-' . $projectId, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
break;
|
||||
}
|
||||
|
||||
$device->deletePath($bucketId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,16 +44,16 @@
|
|||
"utopia-php/analytics": "0.2.*",
|
||||
"utopia-php/audit": "0.8.*",
|
||||
"utopia-php/cache": "0.4.*",
|
||||
"utopia-php/cli": "0.11.*",
|
||||
"utopia-php/cli": "0.12.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.15.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/orchestration": "0.2.*",
|
||||
"utopia-php/orchestration": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/domains": "1.1.*",
|
||||
"utopia-php/swoole": "0.3.*",
|
||||
"utopia-php/storage": "0.5.*",
|
||||
"utopia-php/storage": "0.7.*",
|
||||
"utopia-php/websocket": "0.1.0",
|
||||
"utopia-php/image": "0.5.*",
|
||||
|
||||
|
|
@ -66,8 +66,14 @@
|
|||
"adhocore/jwt": "1.1.2",
|
||||
"slickdeals/statsd": "3.1.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"appwrite/sdk-generator": "0.17.2",
|
||||
"appwrite/sdk-generator": "dev-feat-preps-for-0.13",
|
||||
"phpunit/phpunit": "9.5.10",
|
||||
"swoole/ide-helper": "4.8.3",
|
||||
"textalk/websocket": "1.5.5",
|
||||
|
|
|
|||
177
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "616810ade71f45030c2c678e2d90c51d",
|
||||
"content-hash": "318c53aaac3cdfbdb728acc5e59b8057",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
|
@ -2037,16 +2037,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/cli",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/cli.git",
|
||||
"reference": "c7a6908a8dbe9234b8b2c954e5487d34cb079af6"
|
||||
"reference": "6d164b752efeb1ca089e3a517bc274d8b383474b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/c7a6908a8dbe9234b8b2c954e5487d34cb079af6",
|
||||
"reference": "c7a6908a8dbe9234b8b2c954e5487d34cb079af6",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/6d164b752efeb1ca089e3a517bc274d8b383474b",
|
||||
"reference": "6d164b752efeb1ca089e3a517bc274d8b383474b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2084,9 +2084,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/cli/issues",
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.11.0"
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.12.0"
|
||||
},
|
||||
"time": "2021-04-16T15:16:08+00:00"
|
||||
"time": "2022-02-18T22:10:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/config",
|
||||
|
|
@ -2476,21 +2476,21 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
"version": "0.2.1",
|
||||
"version": "0.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/orchestration.git",
|
||||
"reference": "55da7a331a45d5887de8122268dfccd15fee94d1"
|
||||
"reference": "67cf0ab15a096d274c093ea918aa4ace14ac7af7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55da7a331a45d5887de8122268dfccd15fee94d1",
|
||||
"reference": "55da7a331a45d5887de8122268dfccd15fee94d1",
|
||||
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/67cf0ab15a096d274c093ea918aa4ace14ac7af7",
|
||||
"reference": "67cf0ab15a096d274c093ea918aa4ace14ac7af7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cli": "0.11.*"
|
||||
"utopia-php/cli": "0.12.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
|
|
@ -2525,9 +2525,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/orchestration/issues",
|
||||
"source": "https://github.com/utopia-php/orchestration/tree/0.2.1"
|
||||
"source": "https://github.com/utopia-php/orchestration/tree/0.4.1"
|
||||
},
|
||||
"time": "2021-09-03T11:29:20+00:00"
|
||||
"time": "2022-02-20T09:23:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/preloader",
|
||||
|
|
@ -2636,20 +2636,20 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/storage",
|
||||
"version": "0.5.1",
|
||||
"version": "0.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage.git",
|
||||
"reference": "e672aa3fc2a8ba689aff65f68ff29f1d608223b8"
|
||||
"reference": "1921d5da3d155c1e03b26f8f6184dba3a69cd5e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/e672aa3fc2a8ba689aff65f68ff29f1d608223b8",
|
||||
"reference": "e672aa3fc2a8ba689aff65f68ff29f1d608223b8",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/1921d5da3d155c1e03b26f8f6184dba3a69cd5e4",
|
||||
"reference": "1921d5da3d155c1e03b26f8f6184dba3a69cd5e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/framework": "0.*.*"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
@ -2682,9 +2682,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/storage/issues",
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.5.1"
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.7.1"
|
||||
},
|
||||
"time": "2021-12-13T15:17:14+00:00"
|
||||
"time": "2022-02-20T13:27:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
|
|
@ -3083,17 +3083,11 @@
|
|||
},
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.17.2",
|
||||
"version": "dev-feat-preps-for-0.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "37bc6fc1b4b4940c7659748d7d2d5110da5457a4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/37bc6fc1b4b4940c7659748d7d2d5110da5457a4",
|
||||
"reference": "37bc6fc1b4b4940c7659748d7d2d5110da5457a4",
|
||||
"shasum": ""
|
||||
"url": "https://github.com/appwrite/sdk-generator",
|
||||
"reference": "4a43aa70c2f0b243edfc689f618a85f7d0817287"
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
|
|
@ -3101,10 +3095,10 @@
|
|||
"ext-mbstring": "*",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"php": ">=7.0.0",
|
||||
"twig/twig": "^2.14"
|
||||
"twig/twig": "^3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^9.5.13"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
|
@ -3113,7 +3107,11 @@
|
|||
"Appwrite\\Spec\\": "src/Spec"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
|
@ -3124,11 +3122,7 @@
|
|||
}
|
||||
],
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.17.2"
|
||||
},
|
||||
"time": "2022-01-28T08:25:10+00:00"
|
||||
"time": "2022-02-22T10:51:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
|
|
@ -3703,6 +3697,9 @@
|
|||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"myclabs/deep-copy": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/common": "^2.6",
|
||||
|
|
@ -6062,82 +6059,6 @@
|
|||
],
|
||||
"time": "2021-11-30T18:21:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php72",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||
"reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
|
||||
"reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php72\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php72/tree/v1.24.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-27T09:17:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.0.0",
|
||||
|
|
@ -6406,23 +6327,22 @@
|
|||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v2.14.11",
|
||||
"version": "v3.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "66baa66f29ee30e487e05f1679903e36eb01d727"
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/66baa66f29ee30e487e05f1679903e36eb01d727",
|
||||
"reference": "66baa66f29ee30e487e05f1679903e36eb01d727",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-php72": "^1.8"
|
||||
"symfony/polyfill-mbstring": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/container": "^1.0",
|
||||
|
|
@ -6431,13 +6351,10 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.14-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Twig_": "lib/"
|
||||
},
|
||||
"psr-4": {
|
||||
"Twig\\": "src/"
|
||||
}
|
||||
|
|
@ -6470,7 +6387,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v2.14.11"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6482,7 +6399,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-04T06:57:25+00:00"
|
||||
"time": "2022-02-04T06:59:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vimeo/psalm",
|
||||
|
|
@ -6644,7 +6561,9 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {
|
||||
"appwrite/sdk-generator": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
|
@ -6666,5 +6585,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.2.0"
|
||||
"plugin-api-version": "2.1.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ services:
|
|||
- ./app:/usr/src/code/app
|
||||
# - ./vendor:/usr/src/code/vendor
|
||||
- ./docs:/usr/src/code/docs
|
||||
- ./public:/usr/src/code/public
|
||||
- ./src:/usr/src/code/src
|
||||
# - ./debug:/tmp
|
||||
- ./dev:/usr/local/dev
|
||||
|
|
@ -112,6 +113,15 @@ services:
|
|||
- _APP_STORAGE_ANTIVIRUS
|
||||
- _APP_STORAGE_ANTIVIRUS_HOST
|
||||
- _APP_STORAGE_ANTIVIRUS_PORT
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_DEVICE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_S3_SECRET
|
||||
- _APP_STORAGE_DEVICE_S3_REGION
|
||||
- _APP_STORAGE_DEVICE_S3_BUCKET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_SECRET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_REGION
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_BUCKET
|
||||
- _APP_SMTP_HOST
|
||||
- _APP_SMTP_PORT
|
||||
- _APP_SMTP_SECURE
|
||||
|
|
@ -254,6 +264,15 @@ services:
|
|||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_DEVICE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_S3_SECRET
|
||||
- _APP_STORAGE_DEVICE_S3_REGION
|
||||
- _APP_STORAGE_DEVICE_S3_BUCKET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_SECRET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_REGION
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_BUCKET
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createAnonymousSession(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createJWT(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createMagicURLSession(
|
||||
"[USER_ID]",
|
||||
"email@example.com",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createOAuth2Session(
|
||||
this,
|
||||
"amazon",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createRecovery(
|
||||
"email@example.com",
|
||||
"https://example.com"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createSession(
|
||||
"email@example.com",
|
||||
"password"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createVerification(
|
||||
"https://example.com"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
50
docs/examples/0.13.x/client-android/java/account/create.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.create(
|
||||
"[USER_ID]",
|
||||
"email@example.com",
|
||||
"password",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.deleteSession(
|
||||
"[SESSION_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.deleteSessions(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
46
docs/examples/0.13.x/client-android/java/account/delete.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.delete(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
47
docs/examples/0.13.x/client-android/java/account/get-logs.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getLogs(
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getPrefs(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getSession(
|
||||
"[SESSION_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getSessions(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
46
docs/examples/0.13.x/client-android/java/account/get.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.get(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateEmail(
|
||||
"email@example.com",
|
||||
"password"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateMagicURLSession(
|
||||
"[USER_ID]",
|
||||
"[SECRET]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateName(
|
||||
"[NAME]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updatePassword(
|
||||
"password",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updatePrefs(
|
||||
mapOf( "a" to "b" )
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateRecovery(
|
||||
"[USER_ID]",
|
||||
"[SECRET]",
|
||||
"password",
|
||||
"password"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateVerification(
|
||||
"[USER_ID]",
|
||||
"[SECRET]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getBrowser(
|
||||
"aa",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getCreditCard(
|
||||
"amex",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getFavicon(
|
||||
"https://example.com"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
48
docs/examples/0.13.x/client-android/java/avatars/get-flag.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getFlag(
|
||||
"af",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getImage(
|
||||
"https://example.com",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getInitials(
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
48
docs/examples/0.13.x/client-android/java/avatars/get-q-r.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getQR(
|
||||
"[TEXT]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.createDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]",
|
||||
mapOf( "a" to "b" ),
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.deleteDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.getDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.listDocuments(
|
||||
"[COLLECTION_ID]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.updateDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]",
|
||||
mapOf( "a" to "b" ),
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Functions
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Functions functions = new Functions(client);
|
||||
|
||||
functions.createExecution(
|
||||
"[FUNCTION_ID]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Functions
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Functions functions = new Functions(client);
|
||||
|
||||
functions.getExecution(
|
||||
"[FUNCTION_ID]",
|
||||
"[EXECUTION_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Functions
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Functions functions = new Functions(client);
|
||||
|
||||
functions.listExecutions(
|
||||
"[FUNCTION_ID]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Locale
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Locale locale = new Locale(client);
|
||||
|
||||
locale.getContinents(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||