Merge pull request #1159 from appwrite/0.8.x

0.8.x
This commit is contained in:
Torsten Dittmann 2021-05-19 18:08:30 +02:00 committed by GitHub
commit a8dcd9efe3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1258 changed files with 26952 additions and 6639 deletions

8
.env
View file

@ -1,5 +1,9 @@
_APP_ENV=production _APP_ENV=production
_APP_ENV=development _APP_ENV=development
_APP_LOCALE=en
_APP_CONSOLE_WHITELIST_ROOT=disabled
_APP_CONSOLE_WHITELIST_EMAILS=
_APP_CONSOLE_WHITELIST_IPS=
_APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io
@ -16,7 +20,7 @@ _APP_DB_PORT=3306
_APP_DB_SCHEMA=appwrite _APP_DB_SCHEMA=appwrite
_APP_DB_USER=user _APP_DB_USER=user
_APP_DB_PASS=password _APP_DB_PASS=password
_APP_STORAGE_ANTIVIRUS=enabled _APP_STORAGE_ANTIVIRUS=disabled
_APP_STORAGE_ANTIVIRUS_HOST=clamav _APP_STORAGE_ANTIVIRUS_HOST=clamav
_APP_STORAGE_ANTIVIRUS_PORT=3310 _APP_STORAGE_ANTIVIRUS_PORT=3310
_APP_INFLUXDB_HOST=influxdb _APP_INFLUXDB_HOST=influxdb
@ -24,7 +28,7 @@ _APP_INFLUXDB_PORT=8086
_APP_STATSD_HOST=telegraf _APP_STATSD_HOST=telegraf
_APP_STATSD_PORT=8125 _APP_STATSD_PORT=8125
_APP_SMTP_HOST=maildev _APP_SMTP_HOST=maildev
_APP_SMTP_PORT=25 _APP_SMTP_PORT=1025
_APP_SMTP_SECURE= _APP_SMTP_SECURE=
_APP_SMTP_USERNAME= _APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD= _APP_SMTP_PASSWORD=

View file

@ -2,6 +2,7 @@ dist: xenial
arch: arch:
- amd64 - amd64
- arm64
os: linux os: linux
@ -10,9 +11,6 @@ language: shell
notifications: notifications:
email: email:
- team@appwrite.io - team@appwrite.io
services:
- docker
before_install: before_install:
- curl -fsSL https://get.docker.com | sh - curl -fsSL https://get.docker.com | sh
@ -27,6 +25,9 @@ before_install:
- docker --version - docker --version
- docker buildx create --use - docker buildx create --use
- chmod -R u+x ./.travis-ci - chmod -R u+x ./.travis-ci
- export COMPOSE_INTERACTIVE_NO_CLI=1
# Only pass a single runtime for CI stability
- echo "_APP_FUNCTIONS_RUNTIMES=php-8.0" >> .env
install: install:
- docker-compose up -d - docker-compose up -d

View file

@ -1,6 +1,53 @@
# Version 0.8.0 (Not Released Yet) # Version 0.8.0
- Anonymous login ## Features
- Refactoring SSL generation to work on every request so no domain environment variable is required for SSL generation (#1133)
- Added Anonymous Login ([RFC-010](https://github.com/appwrite/rfc/blob/main/010-anonymous-login.md), #914)
- Added events for functions and executions (#971)
- Added JWT support (#784)
- Added ARM support (#726)
- New awesome image preview features, supports borderRadius, borderColor, borderWidth
- Split token & session models to become 2 different internal entities (#922)
- Added Dart 2.12 as a new Cloud Functions runtime (#989)
- Added option to disable email/password (#947)
- Added option to disable anonymous login (need to merge and apply changed) (#947)
- Added option to disable JWT auth (#947)
- Added option to disable team invites (#947)
- Option to limit number of users (good for app launches + root account PR) (#947)
- Added 2 new endpoints to the projects API to allow new settings
- Enabled 501 errors (Not Implemented) from the error handler
- Added Python 3.9 as a new Cloud Functions runtime (#1044)
- Added Deno 1.8 as a new Cloud Functions runtime (#989)
- Upgraded to PHP 8.0 (#713)
- ClamAV is now disabled by default to allow lower min requirements for Appwrite (#1064)
- Added a new env var named `_APP_LOCALE` that allow to change the default `en` locale value (#1056)
- Updated all the console bottom control to be consistent. Dropped the `+` icon (#1062)
- Added Response Models for Documents and Preferences (#1075, #1102)
- Added new endpoint to update team membership roles (#1142)
- Removed DB connection from webhooks worker for improved performance (#1150)
## Bugs
- Fixed default value for HTTPS force option
- Fixed form array casting in dashboard (#1070)
- Fixed collection document rule form in dashboard (#1069)
- Bugs in the Teams API:
- Fixed incorrect audit worker event names (#1143)
- Increased limit of memberships fetched in `createTeamMembership` to 2000 (#1143)
- Fixed exception thrown when SSL certificate is already stored in the database (#1151)
## Breaking Changes (Read before upgrading!)
- Rename `deleteuser` to `delete` on Users Api (#1089)
- Environment variable `_APP_FUNCTIONS_ENVS` renamed to `_APP_FUNCTIONS_RUNTIMES` (#1101)
- Only logged in users can execute functions (for guests, use anonymous login) (#976)
- Only the user who has triggered the execution get access to the relevant execution logs (#1045)
- Function execution environment variable `APPWRITE_FUNCTION_EVENT_PAYLOAD` renamed to `APPWRITE_FUNCTION_EVENT_DATA` (#1045)
- Function execution environment variable `APPWRITE_FUNCTION_ENV_NAME` renamed to `APPWRITE_FUNCTION_RUNTIME_NAME` (#1101)
- Function execution environment variable `APPWRITE_FUNCTION_ENV_VERSION` renamed to `APPWRITE_FUNCTION_RUNTIME_VERSION` (#1101)
- Introduces rate limits for:
- Team invite (10 requests in every 60 minutes per IP address) (#1088)
- Rename param `inviteId` to the more accurate `membershipId` in the Teams API (#1129)
# Version 0.7.2 # Version 0.7.2
@ -31,6 +78,7 @@
- Force adding a security email on setup - Force adding a security email on setup
- SMTP is now disabled by default, no dummy SMTP is included in setup - SMTP is now disabled by default, no dummy SMTP is included in setup
- Added a new endpoint that returns the server and SDKs latest versions numbers #941 - Added a new endpoint that returns the server and SDKs latest versions numbers #941
- Custom data strings, userId, and JWT available for cloud functions #967
## Upgrades ## Upgrades

View file

@ -278,13 +278,9 @@ Before running the command, make sure you have proper write permissions to the A
```bash ```bash
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x -t appwrite/appwrite:dev --push . docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x -t appwrite/appwrite:dev --push .
``` ```
**Build Functions Envs** **Build Functions Runtimes**
Build envs for all supported cloud functions (multicore builds) The Runtimes for all supported cloud functions (multicore builds) can be found at the [appwrite/php-runtimes](https://github.com/appwrite/php-runtimes) repository.
```bash
bash ./docker/environments/build.sh
```
## Tests ## Tests

View file

@ -12,12 +12,13 @@ RUN composer update --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \ --no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM php:7.4-cli-alpine as step1 FROM php:8.0-cli-alpine as step1
ENV PHP_REDIS_VERSION=5.3.3 \ ENV PHP_REDIS_VERSION=5.3.4 \
PHP_SWOOLE_VERSION=v4.5.8 \ PHP_SWOOLE_VERSION=v4.6.6 \
PHP_MAXMINDDB_VERSION=v1.10.0 \ PHP_IMAGICK_VERSION=master \
PHP_XDEBUG_VERSION=sdebug_2_9-beta PHP_YAML_VERSION=2.2.1 \
PHP_MAXMINDDB_VERSION=v1.10.1
RUN \ RUN \
apk add --no-cache --virtual .deps \ apk add --no-cache --virtual .deps \
@ -29,38 +30,52 @@ RUN \
git \ git \
zlib-dev \ zlib-dev \
brotli-dev \ brotli-dev \
yaml-dev \
imagemagick \
imagemagick-dev \
libmaxminddb-dev libmaxminddb-dev
RUN docker-php-ext-install sockets RUN docker-php-ext-install sockets
RUN \ RUN \
# Redis Extension # Redis Extension
git clone https://github.com/phpredis/phpredis.git && \ git clone --depth 1 --branch $PHP_REDIS_VERSION https://github.com/phpredis/phpredis.git && \
cd phpredis && \ cd phpredis && \
git checkout $PHP_REDIS_VERSION && \
phpize && \ phpize && \
./configure && \ ./configure && \
make && make install && \ make && make install && \
cd .. && \ cd .. && \
## Swoole Extension ## Swoole Extension
git clone https://github.com/swoole/swoole-src.git && \ git clone --depth 1 --branch $PHP_SWOOLE_VERSION https://github.com/swoole/swoole-src.git && \
cd swoole-src && \ cd swoole-src && \
git checkout $PHP_SWOOLE_VERSION && \
phpize && \ phpize && \
./configure --enable-sockets --enable-http2 && \ ./configure --enable-http2 && \
make && make install && \
cd .. && \
## Imagick Extension
git clone --depth 1 --branch $PHP_IMAGICK_VERSION https://github.com/Imagick/imagick && \
cd imagick && \
phpize && \
./configure && \
make && make install && \
cd .. && \
## YAML Extension
git clone --depth 1 --branch $PHP_YAML_VERSION https://github.com/php/pecl-file_formats-yaml && \
cd pecl-file_formats-yaml && \
phpize && \
./configure && \
make && make install && \ make && make install && \
cd .. && \ cd .. && \
## Maxminddb extension ## Maxminddb extension
git clone https://github.com/maxmind/MaxMind-DB-Reader-php.git && \ git clone --depth 1 --branch $PHP_MAXMINDDB_VERSION https://github.com/maxmind/MaxMind-DB-Reader-php.git && \
cd MaxMind-DB-Reader-php && \ cd MaxMind-DB-Reader-php && \
git checkout $PHP_MAXMINDDB_VERSION && \
cd ext && \ cd ext && \
phpize && \ phpize && \
./configure && \ ./configure && \
make && make install && \ make && make install && \
cd ../.. cd ../..
FROM php:7.4-cli-alpine as final FROM php:8.0-cli-alpine as final
LABEL maintainer="team@appwrite.io" LABEL maintainer="team@appwrite.io"
@ -68,10 +83,18 @@ ARG VERSION=dev
ENV _APP_SERVER=swoole \ ENV _APP_SERVER=swoole \
_APP_ENV=production \ _APP_ENV=production \
_APP_LOCALE=en \
_APP_DOMAIN=localhost \ _APP_DOMAIN=localhost \
_APP_DOMAIN_TARGET=localhost \ _APP_DOMAIN_TARGET=localhost \
_APP_HOME=https://appwrite.io \ _APP_HOME=https://appwrite.io \
_APP_EDITION=community \ _APP_EDITION=community \
_APP_CONSOLE_WHITELIST_ROOT=enabled \
_APP_CONSOLE_WHITELIST_EMAILS= \
_APP_CONSOLE_WHITELIST_IPS= \
_APP_SYSTEM_EMAIL_NAME= \
_APP_SYSTEM_EMAIL_ADDRESS= \
_APP_SYSTEM_RESPONSE_FORMAT= \
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS= \
_APP_OPTIONS_ABUSE=enabled \ _APP_OPTIONS_ABUSE=enabled \
_APP_OPTIONS_FORCE_HTTPS=disabled \ _APP_OPTIONS_FORCE_HTTPS=disabled \
_APP_OPENSSL_KEY_V1=your-secret-key \ _APP_OPENSSL_KEY_V1=your-secret-key \
@ -123,16 +146,14 @@ RUN \
curl-dev \ curl-dev \
&& apk add --no-cache \ && apk add --no-cache \
libstdc++ \ libstdc++ \
certbot \
brotli-dev \
yaml-dev \ yaml-dev \
imagemagick \ imagemagick \
imagemagick-dev \ libmaxminddb-dev \
certbot \ certbot \
docker-cli \ docker-cli \
docker-compose \ docker-compose \
libmaxminddb \
libmaxminddb-dev \
&& pecl install imagick yaml \
&& docker-php-ext-enable imagick yaml \
&& docker-php-ext-install sockets opcache pdo_mysql \ && docker-php-ext-install sockets opcache pdo_mysql \
&& apk del .deps \ && apk del .deps \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
@ -140,9 +161,11 @@ RUN \
WORKDIR /usr/src/code WORKDIR /usr/src/code
COPY --from=step0 /usr/local/src/vendor /usr/src/code/vendor COPY --from=step0 /usr/local/src/vendor /usr/src/code/vendor
COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20190902/swoole.so /usr/local/lib/php/extensions/no-debug-non-zts-20190902/ COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20200930/swoole.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20190902/redis.so /usr/local/lib/php/extensions/no-debug-non-zts-20190902/ COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20200930/redis.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20190902/maxminddb.so /usr/local/lib/php/extensions/no-debug-non-zts-20190902/ COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20200930/imagick.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20200930/yaml.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
COPY --from=step1 /usr/local/lib/php/extensions/no-debug-non-zts-20200930/maxminddb.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
# Add Source Code # Add Source Code
COPY ./app /usr/src/code/app COPY ./app /usr/src/code/app
@ -190,11 +213,15 @@ RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
# Enable Extensions # Enable Extensions
RUN echo extension=swoole.so >> /usr/local/etc/php/conf.d/swoole.ini RUN echo extension=swoole.so >> /usr/local/etc/php/conf.d/swoole.ini
RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini
RUN echo extension=imagick.so >> /usr/local/etc/php/conf.d/imagick.ini
RUN echo extension=yaml.so >> /usr/local/etc/php/conf.d/yaml.ini
RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini
RUN echo "opcache.preload_user=www-data" >> /usr/local/etc/php/conf.d/appwrite.ini RUN echo "opcache.preload_user=www-data" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.preload=/usr/src/code/app/preload.php" >> /usr/local/etc/php/conf.d/appwrite.ini RUN echo "opcache.preload=/usr/src/code/app/preload.php" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/appwrite.ini RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.jit_buffer_size=100M" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.jit=1235" >> /usr/local/etc/php/conf.d/appwrite.ini
EXPOSE 80 EXPOSE 80

View file

@ -54,7 +54,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \ --entrypoint="install" \
appwrite/appwrite:0.7.2 appwrite/appwrite:0.8.0
``` ```
### Windows ### Windows
@ -66,7 +66,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^ --volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^ --entrypoint="install" ^
appwrite/appwrite:0.7.2 appwrite/appwrite:0.8.0
``` ```
#### PowerShell #### PowerShell
@ -76,7 +76,7 @@ docker run -it --rm ,
--volume /var/run/docker.sock:/var/run/docker.sock , --volume /var/run/docker.sock:/var/run/docker.sock ,
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
--entrypoint="install" , --entrypoint="install" ,
appwrite/appwrite:0.7.2 appwrite/appwrite:0.8.0
``` ```
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. 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.

42
app/config/auth.php Normal file
View file

@ -0,0 +1,42 @@
<?php
// Auth methods
return [
'email-password' => [
'name' => 'Email/Password',
'key' => 'usersAuthEmailPassword',
'icon' => '/images/users/email-password.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateSession',
'enabled' => true,
],
'anonymous' => [
'name' => 'Anonymous',
'key' => 'usersAuthAnonymous',
'icon' => '/images/users/anonymous.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateAnonymousSession',
'enabled' => true,
],
'invites' => [
'name' => 'Invites',
'key' => 'usersAuthInvites',
'icon' => '/images/users/invites.png',
'docs' => 'https://appwrite.io/docs/client/teams?sdk=web#teamsCreateMembership',
'enabled' => true,
],
'jwt' => [
'name' => 'JWT',
'key' => 'usersAuthJWT',
'icon' => '/images/users/jwt.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateJWT',
'enabled' => true,
],
'phone' => [
'name' => 'Phone',
'key' => 'usersAuthPhone',
'icon' => '/images/users/phone.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreatePhoneSession',
'docs' => '',
'enabled' => false,
],
];

View file

@ -5,6 +5,7 @@ use Utopia\Config\Config;
use Appwrite\Database\Database; use Appwrite\Database\Database;
$providers = Config::getParam('providers', []); $providers = Config::getParam('providers', []);
$auth = Config::getParam('auth', []);
$collections = [ $collections = [
'console' => [ 'console' => [
@ -45,7 +46,7 @@ $collections = [
'legalTaxId' => '', 'legalTaxId' => '',
'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], 'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], 'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
'authWhitelistDomains' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_DOMAINS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_DOMAINS', null)) : [], 'usersAuthLimit' => (App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
], ],
Database::SYSTEM_COLLECTION_COLLECTIONS => [ Database::SYSTEM_COLLECTION_COLLECTIONS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
@ -204,7 +205,7 @@ $collections = [
'key' => 'email', 'key' => 'email',
'type' => Database::SYSTEM_VAR_TYPE_EMAIL, 'type' => Database::SYSTEM_VAR_TYPE_EMAIL,
'default' => '', 'default' => '',
'required' => true, 'required' => false,
'array' => false, 'array' => false,
], ],
[ [
@ -222,7 +223,7 @@ $collections = [
'key' => 'password', 'key' => 'password',
'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '', 'default' => '',
'required' => true, 'required' => false,
'array' => false, 'array' => false,
], ],
[ [
@ -271,6 +272,16 @@ $collections = [
'required' => true, 'required' => true,
'array' => false, 'array' => false,
], ],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Sessions',
'key' => 'sessions',
'type' => Database::SYSTEM_VAR_TYPE_DOCUMENT,
'default' => [],
'required' => false,
'array' => true,
'list' => [Database::SYSTEM_COLLECTION_SESSIONS],
],
[ [
'$collection' => Database::SYSTEM_COLLECTION_RULES, '$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Tokens', 'label' => 'Tokens',
@ -293,11 +304,11 @@ $collections = [
], ],
], ],
], ],
Database::SYSTEM_COLLECTION_TOKENS => [ Database::SYSTEM_COLLECTION_SESSIONS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_TOKENS, '$id' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['*']], '$permissions' => ['read' => ['*']],
'name' => 'Token', 'name' => 'Session',
'structure' => true, 'structure' => true,
'rules' => [ 'rules' => [
[ [
@ -306,16 +317,34 @@ $collections = [
'key' => 'userId', 'key' => 'userId',
'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => null, 'default' => null,
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Provider',
'key' => 'provider',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Provider User Identifier',
'key' => 'providerUid',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false, 'required' => false,
'array' => false, 'array' => false,
], ],
[ [
'$collection' => Database::SYSTEM_COLLECTION_RULES, '$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Type', 'label' => 'Provider Token',
'key' => 'type', 'key' => 'providerToken',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC, 'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => null, 'default' => '',
'required' => true, 'required' => false,
'array' => false, 'array' => false,
], ],
[ [
@ -473,6 +502,69 @@ $collections = [
], ],
], ],
], ],
Database::SYSTEM_COLLECTION_TOKENS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['*']],
'name' => 'Token',
'structure' => true,
'rules' => [
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'User ID',
'key' => 'userId',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => null,
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Type',
'key' => 'type',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => null,
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Secret',
'key' => 'secret',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Expire',
'key' => 'expire',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => 0,
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'User Agent',
'key' => 'userAgent',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'IP',
'key' => 'ip',
'type' => Database::SYSTEM_VAR_TYPE_IP,
'default' => '',
'required' => true,
'array' => false,
],
],
],
Database::SYSTEM_COLLECTION_MEMBERSHIPS => [ Database::SYSTEM_COLLECTION_MEMBERSHIPS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_MEMBERSHIPS, '$id' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
@ -679,6 +771,14 @@ $collections = [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
], ],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Max users allowed',
'key' => 'usersAuthLimit',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => 0,
'required' => false,
],
[ [
'$collection' => Database::SYSTEM_COLLECTION_RULES, '$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Webhooks', 'label' => 'Webhooks',
@ -1172,6 +1272,24 @@ $collections = [
'required' => false, 'required' => false,
'array' => false, 'array' => false,
], ],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Updated Date',
'key' => 'updated',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => 0,
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Certificate ID',
'key' => 'certificateId',
'type' => Database::SYSTEM_VAR_TYPE_KEY,
'default' => '',
'required' => false,
'array' => false,
],
], ],
], ],
Database::SYSTEM_COLLECTION_FILES => [ Database::SYSTEM_COLLECTION_FILES => [
@ -1617,25 +1735,16 @@ foreach ($providers as $index => $provider) {
'array' => false, 'array' => false,
'filter' => ['encrypt'], 'filter' => ['encrypt'],
]; ];
}
$collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [ foreach ($auth as $index => $method) {
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES, '$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OAuth2 '.\ucfirst($index).' ID', 'label' => $method['name'] || '',
'key' => 'oauth2'.\ucfirst($index), 'key' => $method['key'] || '',
'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => '', 'default' => true,
'required' => false, 'required' => false,
'array' => false,
];
$collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OAuth2 '.\ucfirst($index).' Access Token',
'key' => 'oauth2'.\ucfirst($index).'AccessToken',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
]; ];
} }

View file

@ -1,138 +0,0 @@
<?php
use Utopia\App;
use Utopia\System\System;
/**
* List of Appwrite Cloud Functions supported environments
*/
$environments = [
'node-14.5' => [
'name' => 'Node.js',
'version' => '14.5',
'base' => 'node:14.5-alpine',
'image' => 'appwrite/env-node-14.5:1.0.0',
'build' => '/usr/src/code/docker/environments/node-14.5',
'logo' => 'node.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'node-15.5' => [
'name' => 'Node.js',
'version' => '15.5',
'base' => 'node:15.5-alpine',
'image' => 'appwrite/env-node-15.5:1.0.0',
'build' => '/usr/src/code/docker/environments/node-15.5',
'logo' => 'node.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'php-7.4' => [
'name' => 'PHP',
'version' => '7.4',
'base' => 'php:7.4-cli-alpine',
'image' => 'appwrite/env-php-7.4:1.0.0',
'build' => '/usr/src/code/docker/environments/php-7.4',
'logo' => 'php.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'php-8.0' => [
'name' => 'PHP',
'version' => '8.0',
'base' => 'php:8.0-cli-alpine',
'image' => 'appwrite/env-php-8.0:1.0.0',
'build' => '/usr/src/code/docker/environments/php-8.0',
'logo' => 'php.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'ruby-2.7' => [
'name' => 'Ruby',
'version' => '2.7',
'base' => 'ruby:2.7-alpine',
'image' => 'appwrite/env-ruby-2.7:1.0.2',
'build' => '/usr/src/code/docker/environments/ruby-2.7',
'logo' => 'ruby.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'ruby-3.0' => [
'name' => 'Ruby',
'version' => '3.0',
'base' => 'ruby:3.0-alpine',
'image' => 'appwrite/env-ruby-3.0:1.0.0',
'build' => '/usr/src/code/docker/environments/ruby-3.0',
'logo' => 'ruby.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'python-3.8' => [
'name' => 'Python',
'version' => '3.8',
'base' => 'python:3.8-alpine',
'image' => 'appwrite/env-python-3.8:1.0.0',
'build' => '/usr/src/code/docker/environments/python-3.8',
'logo' => 'python.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'deno-1.2' => [
'name' => 'Deno',
'version' => '1.2',
'base' => 'hayd/deno:alpine-1.2.0',
'image' => 'appwrite/env-deno-1.2:1.0.0',
'build' => '/usr/src/code/docker/environments/deno-1.2',
'logo' => 'deno.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'deno-1.5' => [
'name' => 'Deno',
'version' => '1.5',
'base' => 'hayd/deno:alpine-1.5.0',
'image' => 'appwrite/env-deno-1.5:1.0.0',
'build' => '/usr/src/code/docker/environments/deno-1.5',
'logo' => 'deno.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'deno-1.6' => [
'name' => 'Deno',
'version' => '1.6',
'base' => 'hayd/deno:alpine-1.6.0',
'image' => 'appwrite/env-deno-1.6:1.0.0',
'build' => '/usr/src/code/docker/environments/deno-1.6',
'logo' => 'deno.png',
'supports' => [System::X86, System::PPC, System::ARM],
],
'dart-2.10' => [
'name' => 'Dart',
'version' => '2.10',
'base' => 'google/dart:2.10',
'image' => 'appwrite/env-dart-2.10:1.0.0',
'build' => '/usr/src/code/docker/environments/dart-2.10',
'logo' => 'dart.png',
'supports' => [System::X86],
],
'dotnet-3.1' => [
'name' => '.NET',
'version' => '3.1',
'base' => 'mcr.microsoft.com/dotnet/runtime:3.1-alpine',
'image' => 'appwrite/env-dotnet-3.1:1.0.0',
'build' => '/usr/src/code/docker/environments/dotnet-3.1',
'logo' => 'dotnet.png',
'supports' => [System::X86, System::ARM],
],
'dotnet-5.0' => [
'name' => '.NET',
'version' => '5.0',
'base' => 'mcr.microsoft.com/dotnet/runtime:5.0-alpine',
'image' => 'appwrite/env-dotnet-5.0:1.0.0',
'build' => '/usr/src/code/docker/environments/dotnet-5.0',
'logo' => 'dotnet.png',
'supports' => [System::X86, System::ARM],
],
];
$allowList = empty(App::getEnv('_APP_FUNCTIONS_ENVS', null)) ? false : \explode(',', App::getEnv('_APP_FUNCTIONS_ENVS', null));
$environments = array_filter($environments, function ($environment, $key) use ($allowList) {
$isAllowed = $allowList && in_array($key, $allowList);
$isSupported = in_array(System::getArchEnum(), $environment["supports"]);
return $allowList ? ($isAllowed && $isSupported) : $isSupported;
}, ARRAY_FILTER_USE_BOTH);
return $environments;

View file

@ -84,19 +84,59 @@ return [
], ],
'database.documents.create' => [ 'database.documents.create' => [
'description' => 'This event triggers when a database document is created.', 'description' => 'This event triggers when a database document is created.',
'model' => Response::MODEL_ANY, 'model' => Response::MODEL_DOCUMENT,
'note' => '', 'note' => '',
], ],
'database.documents.update' => [ 'database.documents.update' => [
'description' => 'This event triggers when a database document is updated.', 'description' => 'This event triggers when a database document is updated.',
'model' => Response::MODEL_ANY, 'model' => Response::MODEL_DOCUMENT,
'note' => '', 'note' => '',
], ],
'database.documents.delete' => [ 'database.documents.delete' => [
'description' => 'This event triggers when a database document is deleted.', 'description' => 'This event triggers when a database document is deleted.',
'model' => Response::MODEL_ANY, 'model' => Response::MODEL_DOCUMENT,
'note' => '', 'note' => '',
], ],
'functions.create' => [
'description' => 'This event triggers when a function is created.',
'model' => Response::MODEL_FUNCTION,
'note' => 'version >= 0.7',
],
'functions.update' => [
'description' => 'This event triggers when a function is updated.',
'model' => Response::MODEL_FUNCTION,
'note' => 'version >= 0.7',
],
'functions.delete' => [
'description' => 'This event triggers when a function is deleted.',
'model' => Response::MODEL_ANY,
'note' => 'version >= 0.7',
],
'functions.tags.create' => [
'description' => 'This event triggers when a function tag is created.',
'model' => Response::MODEL_TAG,
'note' => 'version >= 0.7',
],
'functions.tags.update' => [
'description' => 'This event triggers when a function tag is updated.',
'model' => Response::MODEL_FUNCTION,
'note' => 'version >= 0.7',
],
'functions.tags.delete' => [
'description' => 'This event triggers when a function tag is deleted.',
'model' => Response::MODEL_ANY,
'note' => 'version >= 0.7',
],
'functions.executions.create' => [
'description' => 'This event triggers when a function execution is created.',
'model' => Response::MODEL_EXECUTION,
'note' => 'version >= 0.7',
],
'functions.executions.update' => [
'description' => 'This event triggers when a function execution is updated.',
'model' => Response::MODEL_EXECUTION,
'note' => 'version >= 0.7',
],
'storage.files.create' => [ 'storage.files.create' => [
'description' => 'This event triggers when a storage file is created.', 'description' => 'This event triggers when a storage file is created.',
'model' => Response::MODEL_FILE, 'model' => Response::MODEL_FILE,
@ -157,6 +197,11 @@ return [
'model' => Response::MODEL_MEMBERSHIP, 'model' => Response::MODEL_MEMBERSHIP,
'note' => 'version >= 0.7', 'note' => 'version >= 0.7',
], ],
'teams.memberships.update' => [
'description' => 'This event triggers when a team membership is updated.',
'model' => Response::MODEL_MEMBERSHIP,
'note' => 'version >= 0.8',
],
'teams.memberships.update.status' => [ 'teams.memberships.update.status' => [
'description' => 'This event triggers when a team memberships status is updated.', 'description' => 'This event triggers when a team memberships status is updated.',
'model' => Response::MODEL_MEMBERSHIP, 'model' => Response::MODEL_MEMBERSHIP,
@ -167,4 +212,4 @@ return [
'model' => Response::MODEL_MEMBERSHIP, 'model' => Response::MODEL_MEMBERSHIP,
'note' => 'version >= 0.7', 'note' => 'version >= 0.7',
], ],
]; ];

View file

@ -15,7 +15,7 @@ return [
[ [
'key' => 'web', 'key' => 'web',
'name' => 'Web', 'name' => 'Web',
'version' => '2.0.0', 'version' => '3.0.0',
'url' => 'https://github.com/appwrite/sdk-for-web', 'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite', 'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true, 'enabled' => true,
@ -62,7 +62,7 @@ return [
[ [
'key' => 'flutter', 'key' => 'flutter',
'name' => 'Flutter', 'name' => 'Flutter',
'version' => '0.5.0-dev.1', 'version' => '0.6.0',
'url' => 'https://github.com/appwrite/sdk-for-flutter', 'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite', 'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true, 'enabled' => true,
@ -151,7 +151,7 @@ return [
[ [
'key' => 'web', 'key' => 'web',
'name' => 'Console', 'name' => 'Console',
'version' => '1.0.0', 'version' => '2.0.0',
'url' => 'https://github.com/appwrite/sdk-for-console', 'url' => 'https://github.com/appwrite/sdk-for-console',
'package' => '', 'package' => '',
'enabled' => true, 'enabled' => true,
@ -178,7 +178,7 @@ return [
[ [
'key' => 'nodejs', 'key' => 'nodejs',
'name' => 'Node.js', 'name' => 'Node.js',
'version' => '2.1.0', 'version' => '2.2.0',
'url' => 'https://github.com/appwrite/sdk-for-node', 'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite', 'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true, 'enabled' => true,
@ -195,7 +195,7 @@ return [
[ [
'key' => 'deno', 'key' => 'deno',
'name' => 'Deno', 'name' => 'Deno',
'version' => '0.1.1', 'version' => '0.2.0',
'url' => 'https://github.com/appwrite/sdk-for-deno', 'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite', 'package' => 'https://deno.land/x/appwrite',
'enabled' => true, 'enabled' => true,
@ -212,7 +212,7 @@ return [
[ [
'key' => 'php', 'key' => 'php',
'name' => 'PHP', 'name' => 'PHP',
'version' => '2.0.0', 'version' => '2.1.0',
'url' => 'https://github.com/appwrite/sdk-for-php', 'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite', 'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true, 'enabled' => true,
@ -229,7 +229,7 @@ return [
[ [
'key' => 'python', 'key' => 'python',
'name' => 'Python', 'name' => 'Python',
'version' => '0.1.1', 'version' => '0.2.0',
'url' => 'https://github.com/appwrite/sdk-for-python', 'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/', 'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true, 'enabled' => true,
@ -246,7 +246,7 @@ return [
[ [
'key' => 'ruby', 'key' => 'ruby',
'name' => 'Ruby', 'name' => 'Ruby',
'version' => '2.0.2', 'version' => '2.1.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby', 'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite', 'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true, 'enabled' => true,
@ -297,7 +297,7 @@ return [
[ [
'key' => 'dotnet', 'key' => 'dotnet',
'name' => '.NET', 'name' => '.NET',
'version' => '0.2.0', 'version' => '0.3.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite', 'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true, 'enabled' => true,
@ -314,7 +314,7 @@ return [
[ [
'key' => 'dart', 'key' => 'dart',
'name' => 'Dart', 'name' => 'Dart',
'version' => '0.5.0-dev.1', 'version' => '0.6.0',
'url' => 'https://github.com/appwrite/sdk-for-dart', 'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite', 'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true, 'enabled' => true,
@ -331,7 +331,7 @@ return [
[ [
'key' => 'cli', 'key' => 'cli',
'name' => 'Command Line', 'name' => 'Command Line',
'version' => '0.8.0', 'version' => '0.9.0',
'url' => 'https://github.com/appwrite/sdk-for-cli', 'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://github.com/appwrite/sdk-for-cli',
'enabled' => true, 'enabled' => true,

View file

@ -6,6 +6,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.amazon.com/apps-and-games/services-and-apis', 'developers' => 'https://developer.amazon.com/apps-and-games/services-and-apis',
'icon' => 'icon-amazon', 'icon' => 'icon-amazon',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -15,6 +16,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.apple.com/', 'developers' => 'https://developer.apple.com/',
'icon' => 'icon-apple', 'icon' => 'icon-apple',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => 'apple.phtml', // Perperation for adding ability to customized OAuth UI forms, currently handled hardcoded. 'form' => 'apple.phtml', // Perperation for adding ability to customized OAuth UI forms, currently handled hardcoded.
'beta' => true, 'beta' => true,
'mock' => false, 'mock' => false,
@ -24,6 +26,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.atlassian.com/bitbucket', 'developers' => 'https://developer.atlassian.com/bitbucket',
'icon' => 'icon-bitbucket', 'icon' => 'icon-bitbucket',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -33,6 +36,7 @@ return [ // Ordered by ABC.
'developers' => 'https://dev.bitly.com/v4_documentation.html', 'developers' => 'https://dev.bitly.com/v4_documentation.html',
'icon' => 'icon-bitly', 'icon' => 'icon-bitly',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false 'mock' => false
@ -42,6 +46,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.box.com/reference/', 'developers' => 'https://developer.box.com/reference/',
'icon' => 'icon-box', 'icon' => 'icon-box',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false 'mock' => false
@ -51,6 +56,7 @@ return [ // Ordered by ABC.
'developers' => 'https://discordapp.com/developers/docs/topics/oauth2', 'developers' => 'https://discordapp.com/developers/docs/topics/oauth2',
'icon' => 'icon-discord', 'icon' => 'icon-discord',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -60,6 +66,7 @@ return [ // Ordered by ABC.
'developers' => 'https://www.dropbox.com/developers/documentation', 'developers' => 'https://www.dropbox.com/developers/documentation',
'icon' => 'icon-dropbox', 'icon' => 'icon-dropbox',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -69,6 +76,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developers.facebook.com/', 'developers' => 'https://developers.facebook.com/',
'icon' => 'icon-facebook', 'icon' => 'icon-facebook',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -78,6 +86,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.github.com/', 'developers' => 'https://developer.github.com/',
'icon' => 'icon-github-circled', 'icon' => 'icon-github-circled',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -87,6 +96,7 @@ return [ // Ordered by ABC.
'developers' => 'https://docs.gitlab.com/ee/api/', 'developers' => 'https://docs.gitlab.com/ee/api/',
'icon' => 'icon-gitlab', 'icon' => 'icon-gitlab',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -96,6 +106,7 @@ return [ // Ordered by ABC.
'developers' => 'https://support.google.com/googleapi/answer/6158849', 'developers' => 'https://support.google.com/googleapi/answer/6158849',
'icon' => 'icon-google', 'icon' => 'icon-google',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -105,6 +116,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.linkedin.com/', 'developers' => 'https://developer.linkedin.com/',
'icon' => 'icon-linkedin', 'icon' => 'icon-linkedin',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -114,6 +126,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.microsoft.com/en-us/', 'developers' => 'https://developer.microsoft.com/en-us/',
'icon' => 'icon-windows', 'icon' => 'icon-windows',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -123,15 +136,17 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.paypal.com/docs/api/overview/', 'developers' => 'https://developer.paypal.com/docs/api/overview/',
'icon' => 'icon-paypal', 'icon' => 'icon-paypal',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false 'mock' => false
], ],
'paypalSandbox' => [ 'paypalSandbox' => [
'name' => 'PayPal (Sandbox)', 'name' => 'PayPal',
'developers' => 'https://developer.paypal.com/docs/api/overview/', 'developers' => 'https://developer.paypal.com/docs/api/overview/',
'icon' => 'icon-paypal', 'icon' => 'icon-paypal',
'enabled' => true, 'enabled' => true,
'sandbox' => true,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false 'mock' => false
@ -141,6 +156,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.salesforce.com/docs/', 'developers' => 'https://developer.salesforce.com/docs/',
'icon' => 'icon-salesforce', 'icon' => 'icon-salesforce',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -150,6 +166,7 @@ return [ // Ordered by ABC.
'developers' => 'https://api.slack.com/', 'developers' => 'https://api.slack.com/',
'icon' => 'icon-slack', 'icon' => 'icon-slack',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -159,6 +176,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.spotify.com/documentation/general/guides/authorization-guide/', 'developers' => 'https://developer.spotify.com/documentation/general/guides/authorization-guide/',
'icon' => 'icon-spotify', 'icon' => 'icon-spotify',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -168,15 +186,17 @@ return [ // Ordered by ABC.
'developers' => 'https://developers.tradeshift.com/docs/api', 'developers' => 'https://developers.tradeshift.com/docs/api',
'icon' => 'icon-tradeshift', 'icon' => 'icon-tradeshift',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
], ],
'tradeshiftBox' => [ 'tradeshiftBox' => [
'name' => 'Tradeshift Sandbox', 'name' => 'Tradeshift',
'developers' => 'https://developers.tradeshift.com/docs/api', 'developers' => 'https://developers.tradeshift.com/docs/api',
'icon' => 'icon-tradeshiftbox', 'icon' => 'icon-tradeshiftbox',
'enabled' => true, 'enabled' => true,
'sandbox' => true,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -186,6 +206,7 @@ return [ // Ordered by ABC.
'developers' => 'https://dev.twitch.tv/docs/authentication', 'developers' => 'https://dev.twitch.tv/docs/authentication',
'icon' => 'icon-twitch', 'icon' => 'icon-twitch',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -195,6 +216,7 @@ return [ // Ordered by ABC.
'developers' => 'https://vk.com/dev', 'developers' => 'https://vk.com/dev',
'icon' => 'icon-vk', 'icon' => 'icon-vk',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -204,6 +226,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.yahoo.com/oauth2/guide/flows_authcode/', 'developers' => 'https://developer.yahoo.com/oauth2/guide/flows_authcode/',
'icon' => 'icon-yahoo', 'icon' => 'icon-yahoo',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -213,6 +236,7 @@ return [ // Ordered by ABC.
'developers' => 'https://tech.yandex.com/oauth/', 'developers' => 'https://tech.yandex.com/oauth/',
'icon' => 'icon-yandex', 'icon' => 'icon-yandex',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false, 'mock' => false,
@ -238,6 +262,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.wordpress.com/docs/oauth2/', 'developers' => 'https://developer.wordpress.com/docs/oauth2/',
'icon' => 'icon-wordpress', 'icon' => 'icon-wordpress',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => false 'mock' => false
@ -248,6 +273,7 @@ return [ // Ordered by ABC.
'developers' => 'https://appwrite.io', 'developers' => 'https://appwrite.io',
'icon' => 'icon-appwrite', 'icon' => 'icon-appwrite',
'enabled' => true, 'enabled' => true,
'sandbox' => false,
'form' => false, 'form' => false,
'beta' => false, 'beta' => false,
'mock' => true, 'mock' => true,

View file

@ -60,8 +60,6 @@ return [
'files.read', 'files.read',
'locale.read', 'locale.read',
'avatars.read', 'avatars.read',
'execution.read',
'execution.write',
], ],
], ],
Auth::USER_ROLE_MEMBER => [ Auth::USER_ROLE_MEMBER => [

15
app/config/runtimes.php Normal file
View file

@ -0,0 +1,15 @@
<?php
use Utopia\App;
use Appwrite\Runtimes\Runtimes;
/**
* List of Appwrite Cloud Functions supported runtimes
*/
$runtimes = new Runtimes();
$allowList = empty(App::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES'));
$runtimes = $runtimes->getAll(true, $allowList);
return $runtimes;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -14,6 +14,16 @@ return [
'default' => 'production', 'default' => 'production',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
],
[
'name' => '_APP_LOCALE',
'description' => 'Set your Appwrite\'s locale. By default, the locale is set to \'en\'.',
'introduction' => '',
'default' => 'en',
'required' => false,
'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_OPTIONS_ABUSE', 'name' => '_APP_OPTIONS_ABUSE',
@ -22,14 +32,16 @@ return [
'default' => 'enabled', 'default' => 'enabled',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_OPTIONS_FORCE_HTTPS', 'name' => '_APP_OPTIONS_FORCE_HTTPS',
'description' => 'Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the \'Strict-Transport-Security\' header to all HTTP responses. By default, set to \'disabled\'. To enable, set to \'enabled\'. This feature will work only when your ports are set to default 80 and 443.', 'description' => 'Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the \'Strict-Transport-Security\' header to all HTTP responses. By default, set to \'enabled\'. To disable, set to \'disabled\'. This feature will work only when your ports are set to default 80 and 443.',
'introduction' => '', 'introduction' => '',
'default' => 'enabled', 'default' => 'disabled',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_OPENSSL_KEY_V1', 'name' => '_APP_OPENSSL_KEY_V1',
@ -38,6 +50,7 @@ return [
'default' => 'your-secret-key', 'default' => 'your-secret-key',
'required' => true, 'required' => true,
'question' => 'Choose a secret API key, make sure to make a backup of your key in a secure location', 'question' => 'Choose a secret API key, make sure to make a backup of your key in a secure location',
'filter' => 'token'
], ],
[ [
'name' => '_APP_DOMAIN', 'name' => '_APP_DOMAIN',
@ -46,6 +59,7 @@ return [
'default' => 'localhost', 'default' => 'localhost',
'required' => true, 'required' => true,
'question' => 'Enter your Appwrite hostname', 'question' => 'Enter your Appwrite hostname',
'filter' => ''
], ],
[ [
'name' => '_APP_DOMAIN_TARGET', 'name' => '_APP_DOMAIN_TARGET',
@ -54,14 +68,25 @@ return [
'default' => 'localhost', 'default' => 'localhost',
'required' => true, 'required' => true,
'question' => 'Enter a DNS A record hostname to serve as a CNAME for your custom domains.\nYou can use the same value as used for the Appwrite hostname.', 'question' => 'Enter a DNS A record hostname to serve as a CNAME for your custom domains.\nYou can use the same value as used for the Appwrite hostname.',
'filter' => ''
],
[
'name' => '_APP_CONSOLE_WHITELIST_ROOT',
'description' => 'This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by invting them to your project. By default this option is enabled.',
'introduction' => '0.8.0',
'default' => 'enabled',
'required' => false,
'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_CONSOLE_WHITELIST_EMAILS', 'name' => '_APP_CONSOLE_WHITELIST_EMAILS',
'description' => 'This option allows you to limit creation of users to Appwrite console. This option is very useful for small teams or sole developers. To enable it, pass a list of allowed email addresses separated by a comma.', 'description' => 'This option allows you to limit creation of new users on the Appwrite console. This option is very useful for small teams or sole developers. To enable it, pass a list of allowed email addresses separated by a comma.',
'introduction' => '', 'introduction' => '',
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
// [ // [
// 'name' => '_APP_CONSOLE_WHITELIST_DOMAINS', // 'name' => '_APP_CONSOLE_WHITELIST_DOMAINS',
@ -78,6 +103,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SYSTEM_EMAIL_NAME', 'name' => '_APP_SYSTEM_EMAIL_NAME',
@ -86,6 +112,7 @@ return [
'default' => 'Appwrite', 'default' => 'Appwrite',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SYSTEM_EMAIL_ADDRESS', 'name' => '_APP_SYSTEM_EMAIL_ADDRESS',
@ -94,6 +121,7 @@ return [
'default' => 'team@appwrite.io', 'default' => 'team@appwrite.io',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SYSTEM_RESPONSE_FORMAT', 'name' => '_APP_SYSTEM_RESPONSE_FORMAT',
@ -102,6 +130,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', 'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS',
@ -110,6 +139,7 @@ return [
'default' => 'certs@appwrite.io', 'default' => 'certs@appwrite.io',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_USAGE_STATS', 'name' => '_APP_USAGE_STATS',
@ -118,7 +148,8 @@ return [
'default' => 'enabled', 'default' => 'enabled',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
], 'filter' => ''
]
], ],
], ],
[ [
@ -132,6 +163,7 @@ return [
'default' => 'redis', 'default' => 'redis',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_REDIS_PORT', 'name' => '_APP_REDIS_PORT',
@ -140,6 +172,7 @@ return [
'default' => '6379', 'default' => '6379',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_REDIS_USER', 'name' => '_APP_REDIS_USER',
@ -148,6 +181,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_REDIS_PASS', 'name' => '_APP_REDIS_PASS',
@ -156,6 +190,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
], ],
], ],
@ -170,6 +205,7 @@ return [
'default' => 'mariadb', 'default' => 'mariadb',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_DB_PORT', 'name' => '_APP_DB_PORT',
@ -178,6 +214,7 @@ return [
'default' => '3306', 'default' => '3306',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_DB_SCHEMA', 'name' => '_APP_DB_SCHEMA',
@ -186,6 +223,7 @@ return [
'default' => 'appwrite', 'default' => 'appwrite',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_DB_USER', 'name' => '_APP_DB_USER',
@ -194,6 +232,7 @@ return [
'default' => 'user', 'default' => 'user',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_DB_PASS', 'name' => '_APP_DB_PASS',
@ -202,6 +241,16 @@ return [
'default' => 'password', 'default' => 'password',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => 'password'
],
[
'name' => '_APP_DB_ROOT_PASS',
'description' => 'MariaDB server root password. Default value is: \'rootsecretpassword\'.',
'introduction' => '',
'default' => 'rootsecretpassword',
'required' => false,
'question' => '',
'filter' => 'password'
], ],
], ],
], ],
@ -216,6 +265,7 @@ return [
'default' => 'influxdb', 'default' => 'influxdb',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_INFLUXDB_PORT', 'name' => '_APP_INFLUXDB_PORT',
@ -224,6 +274,7 @@ return [
'default' => '8086', 'default' => '8086',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
], ],
], ],
@ -238,6 +289,7 @@ return [
'default' => 'telegraf', 'default' => 'telegraf',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_STATSD_PORT', 'name' => '_APP_STATSD_PORT',
@ -246,6 +298,7 @@ return [
'default' => '8125', 'default' => '8125',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
], ],
], ],
@ -260,6 +313,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SMTP_PORT', 'name' => '_APP_SMTP_PORT',
@ -268,6 +322,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SMTP_SECURE', 'name' => '_APP_SMTP_SECURE',
@ -276,6 +331,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SMTP_USERNAME', 'name' => '_APP_SMTP_USERNAME',
@ -284,6 +340,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_SMTP_PASSWORD', 'name' => '_APP_SMTP_PASSWORD',
@ -292,6 +349,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
], ],
], ],
@ -306,14 +364,16 @@ return [
'default' => '10000000', 'default' => '10000000',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_STORAGE_ANTIVIRUS', 'name' => '_APP_STORAGE_ANTIVIRUS',
'description' => 'This variable allows you to disable the internal anti-virus scans. This value is set to \'enabled\' by default, to cancel the scans set the value to \'disabled\'. When disabled, it\'s recommended to turn off the ClamAV container for better resource usage.', 'description' => 'This variable allows you to disable the internal anti-virus scans. This value is set to \'disabled\' by default, to enable the scans set the value to \'enabled\'. Before enabling, you must add the ClamAV service and depend on it on main Appwrite service.',
'introduction' => '', 'introduction' => '',
'default' => 'enabled', 'default' => 'disabled',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_STORAGE_ANTIVIRUS_HOST', 'name' => '_APP_STORAGE_ANTIVIRUS_HOST',
@ -322,6 +382,7 @@ return [
'default' => 'clamav', 'default' => 'clamav',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_STORAGE_ANTIVIRUS_PORT', 'name' => '_APP_STORAGE_ANTIVIRUS_PORT',
@ -330,6 +391,7 @@ return [
'default' => '3310', 'default' => '3310',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
], ],
], ],
@ -344,6 +406,7 @@ return [
'default' => '900', 'default' => '900',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_FUNCTIONS_CONTAINERS', 'name' => '_APP_FUNCTIONS_CONTAINERS',
@ -352,6 +415,7 @@ return [
'default' => '10', 'default' => '10',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_FUNCTIONS_CPUS', 'name' => '_APP_FUNCTIONS_CPUS',
@ -360,6 +424,7 @@ return [
'default' => '', 'default' => '',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_FUNCTIONS_MEMORY', 'name' => '_APP_FUNCTIONS_MEMORY',
@ -368,6 +433,7 @@ return [
'default' => '256', 'default' => '256',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_FUNCTIONS_MEMORY_SWAP', 'name' => '_APP_FUNCTIONS_MEMORY_SWAP',
@ -376,14 +442,25 @@ return [
'default' => '256', 'default' => '256',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_RUNTIMES',
'description' => "This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\n\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\n\nCurrently, supported environments are: " . \implode(', ', \array_keys(Config::getParam('runtimes'))),
'introduction' => '0.8.0',
'default' => 'node-15.5,deno-1.8,php-8.0,python-3.9,ruby-3.0,dotnet-5.0',
'required' => false,
'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_FUNCTIONS_ENVS', 'name' => '_APP_FUNCTIONS_ENVS',
'description' => 'This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\n\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\n\nCurrently, supported environments are: ' . \implode(', ', \array_keys(Config::getParam('providers'))), 'description' => 'Deprectated with 0.8.0, use \'_APP_FUNCTIONS_RUNTIMES\' instead!',
'introduction' => '0.7.0', 'introduction' => '0.7.0',
'default' => 'node-14.5,deno-1.6,php-7.4,python-3.8,ruby-3.0,dotnet-5.0', 'default' => 'node-14.5,deno-1.8,php-7.4,python-3.9,ruby-3.0,dotnet-5.0',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
], ],
[ [
@ -397,6 +474,7 @@ return [
'default' => '86400', 'default' => '86400',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION', 'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION',
@ -405,6 +483,7 @@ return [
'default' => '1209600', 'default' => '1209600',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT', 'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
@ -413,6 +492,7 @@ return [
'default' => '1209600', 'default' => '1209600',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
], ],
[ [
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE', 'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
@ -421,6 +501,7 @@ return [
'default' => '86400', 'default' => '86400',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
'filter' => ''
] ]
], ],
], ],

View file

@ -6,10 +6,10 @@ use Utopia\Exception;
use Utopia\Config\Config; use Utopia\Config\Config;
use Utopia\Validator\Assoc; use Utopia\Validator\Assoc;
use Utopia\Validator\Text; use Utopia\Validator\Text;
use Utopia\Validator\Email; use Appwrite\Network\Validator\Email;
use Utopia\Validator\WhiteList; use Utopia\Validator\WhiteList;
use Utopia\Validator\Host; use Appwrite\Network\Validator\Host;
use Utopia\Validator\URL; use Appwrite\Network\Validator\URL;
use Utopia\Audit\Audit; use Utopia\Audit\Audit;
use Utopia\Audit\Adapters\MySQL as AuditAdapter; use Utopia\Audit\Adapters\MySQL as AuditAdapter;
use Appwrite\Auth\Auth; use Appwrite\Auth\Auth;
@ -31,10 +31,11 @@ $oauthDefaultFailure = App::getEnv('_APP_HOME').'/auth/oauth2/failure';
App::post('/v1/account') App::post('/v1/account')
->desc('Create Account') ->desc('Create Account')
->groups(['api', 'account']) ->groups(['api', 'account', 'auth'])
->label('event', 'account.create') ->label('event', 'account.create')
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('auth.type', 'emailPassword')
->label('sdk.auth', [])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'create') ->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/account/create.md') ->label('sdk.description', '/docs/references/account/create.md')
@ -60,7 +61,6 @@ App::post('/v1/account')
if ('console' === $project->getId()) { if ('console' === $project->getId()) {
$whitlistEmails = $project->getAttribute('authWhitelistEmails'); $whitlistEmails = $project->getAttribute('authWhitelistEmails');
$whitlistIPs = $project->getAttribute('authWhitelistIPs'); $whitlistIPs = $project->getAttribute('authWhitelistIPs');
$whitlistDomains = $project->getAttribute('authWhitelistDomains');
if (!empty($whitlistEmails) && !\in_array($email, $whitlistEmails)) { if (!empty($whitlistEmails) && !\in_array($email, $whitlistEmails)) {
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);
@ -69,9 +69,21 @@ App::post('/v1/account')
if (!empty($whitlistIPs) && !\in_array($request->getIP(), $whitlistIPs)) { if (!empty($whitlistIPs) && !\in_array($request->getIP(), $whitlistIPs)) {
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);
} }
}
if (!empty($whitlistDomains) && !\in_array(\substr(\strrchr($email, '@'), 1), $whitlistDomains)) { $limit = $project->getAttribute('usersAuthLimit', 0);
throw new Exception('Console registration is restricted to specific domains. Contact your administrator for more information.', 401);
if ($limit !== 0) {
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
} }
} }
@ -109,7 +121,7 @@ App::post('/v1/account')
throw new Exception('Account already exists', 409); throw new Exception('Account already exists', 409);
} }
Authorization::enable(); Authorization::reset();
Authorization::unsetRole('role:'.Auth::USER_ROLE_GUEST); Authorization::unsetRole('role:'.Auth::USER_ROLE_GUEST);
Authorization::setRole('user:'.$user->getId()); Authorization::setRole('user:'.$user->getId());
@ -133,10 +145,11 @@ App::post('/v1/account')
App::post('/v1/account/sessions') App::post('/v1/account/sessions')
->desc('Create Account Session') ->desc('Create Account Session')
->groups(['api', 'account']) ->groups(['api', 'account', 'auth'])
->label('event', 'account.sessions.create') ->label('event', 'account.sessions.create')
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('auth.type', 'emailPassword')
->label('sdk.auth', [])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'createSession') ->label('sdk.method', 'createSession')
->label('sdk.description', '/docs/references/account/create-session.md') ->label('sdk.description', '/docs/references/account/create-session.md')
@ -190,10 +203,11 @@ App::post('/v1/account/sessions')
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$session = new Document(array_merge( $session = new Document(array_merge(
[ [
'$collection' => Database::SYSTEM_COLLECTION_TOKENS, '$collection' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]], '$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]],
'userId' => $profile->getId(), 'userId' => $profile->getId(),
'type' => Auth::TOKEN_TYPE_LOGIN, 'provider' => Auth::SESSION_PROVIDER_EMAIL,
'providerUid' => $email,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry, 'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'), 'userAgent' => $request->getUserAgent('UNKNOWN'),
@ -210,7 +224,7 @@ App::post('/v1/account/sessions')
throw new Exception('Failed saving session to DB', 500); throw new Exception('Failed saving session to DB', 500);
} }
$profile->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); $profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
$profile = $projectDB->updateDocument($profile->getArrayCopy()); $profile = $projectDB->updateDocument($profile->getArrayCopy());
@ -251,7 +265,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('error', __DIR__.'/../../views/general/error.phtml') ->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'createOAuth2Session') ->label('sdk.method', 'createOAuth2Session')
->label('sdk.description', '/docs/references/account/create-session-oauth2.md') ->label('sdk.description', '/docs/references/account/create-session-oauth2.md')
@ -441,7 +455,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
throw new Exception('Missing ID from OAuth2 provider', 400); throw new Exception('Missing ID from OAuth2 provider', 400);
} }
$current = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret); $current = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret);
if ($current) { if ($current) {
$projectDB->deleteDocument($current); //throw new Exception('User already logged in', 401); $projectDB->deleteDocument($current); //throw new Exception('User already logged in', 401);
@ -451,7 +465,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'limit' => 1, 'limit' => 1,
'filters' => [ 'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS, '$collection='.Database::SYSTEM_COLLECTION_USERS,
'oauth2'.\ucfirst($provider).'='.$oauth2ID, 'sessions.provider='.$provider,
'sessions.providerUid='.$oauth2ID
], ],
]) : $user; ]) : $user;
@ -467,7 +482,23 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
], ],
]); ]);
if (!$user || empty($user->getId())) { // Last option -> create user alone, generate random password if (!$user || empty($user->getId())) { // Last option -> create the user, generate random password
$limit = $project->getAttribute('usersAuthLimit', 0);
if ($limit !== 0) {
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
}
}
Authorization::disable(); Authorization::disable();
try { try {
@ -478,7 +509,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'emailVerification' => true, 'emailVerification' => true,
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth2 provider 'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth2 provider
'password' => Auth::passwordHash(Auth::passwordGenerator()), 'password' => Auth::passwordHash(Auth::passwordGenerator()),
'passwordUpdate' => \time(), 'passwordUpdate' => 0,
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'name' => $name, 'name' => $name,
@ -487,7 +518,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
throw new Exception('Account already exists', 409); throw new Exception('Account already exists', 409);
} }
Authorization::enable(); Authorization::reset();
if (false === $user) { if (false === $user) {
throw new Exception('Failed saving user to DB', 500); throw new Exception('Failed saving user to DB', 500);
@ -506,10 +537,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document(array_merge([ $session = new Document(array_merge([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS, '$collection' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]], '$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]],
'userId' => $user->getId(), 'userId' => $user->getId(),
'type' => Auth::TOKEN_TYPE_LOGIN, 'provider' => $provider,
'providerUid' => $oauth2ID,
'providerToken' => $accessToken,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry, 'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'), 'userAgent' => $request->getUserAgent('UNKNOWN'),
@ -517,11 +550,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
], $detector->getOS(), $detector->getClient(), $detector->getDevice())); ], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
$isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password'));
if ($isAnonymousUser) {
$user
->setAttribute('name', $oauth2->getUserName($accessToken))
->setAttribute('email', $oauth2->getUserEmail($accessToken))
;
}
$user $user
->setAttribute('oauth2'.\ucfirst($provider), $oauth2ID)
->setAttribute('oauth2'.\ucfirst($provider).'AccessToken', $accessToken)
->setAttribute('status', Auth::USER_STATUS_ACTIVATED) ->setAttribute('status', Auth::USER_STATUS_ACTIVATED)
->setAttribute('tokens', $session, Document::SET_TYPE_APPEND) ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)
; ;
Authorization::setRole('user:'.$user->getId()); Authorization::setRole('user:'.$user->getId());
@ -566,12 +606,156 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
; ;
}); });
App::post('/v1/account/sessions/anonymous')
->desc('Create Anonymous Session')
->groups(['api', 'account', 'auth'])
->label('event', 'account.sessions.create')
->label('scope', 'public')
->label('auth.type', 'anonymous')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createAnonymousSession')
->label('sdk.description', '/docs/references/account/create-session-anonymous.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION)
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->inject('request')
->inject('response')
->inject('locale')
->inject('user')
->inject('project')
->inject('projectDB')
->inject('geodb')
->inject('audits')
->action(function ($request, $response, $locale, $user, $project, $projectDB, $geodb, $audits) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
$protocol = $request->getProtocol();
if ('console' === $project->getId()) {
throw new Exception('Failed to create anonymous user.', 401);
}
if ($user->getId()) {
throw new Exception('Cannot create an anonymous user when logged in.', 401);
}
$limit = $project->getAttribute('usersAuthLimit', 0);
if ($limit !== 0) {
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
}
}
Authorization::disable();
try {
$user = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_USERS,
'$permissions' => [
'read' => ['*'],
'write' => ['user:{self}']
],
'email' => null,
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => null,
'passwordUpdate' => \time(),
'registration' => \time(),
'reset' => false,
'name' => null
]);
} catch (Exception $th) {
throw new Exception('Failed saving user to DB', 500);
}
Authorization::reset();
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
// Create session token
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$record = $geodb->get($request->getIP());
$secret = Auth::tokenGenerator();
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document(array_merge(
[
'$collection' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['user:' . $user['$id']], 'write' => ['user:' . $user['$id']]],
'userId' => $user->getId(),
'provider' => Auth::SESSION_PROVIDER_ANONYMOUS,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
],
$detector->getOS(),
$detector->getClient(),
$detector->getDevice()
));
$user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
Authorization::setRole('user:'.$user->getId());
$user = $projectDB->updateDocument($user->getArrayCopy());
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.sessions.create')
->setParam('resource', 'users/'.$user->getId())
;
if (!Config::getParam('domainVerification')) {
$response
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
;
}
$response
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
->setStatusCode(Response::STATUS_CODE_CREATED)
;
$session
->setAttribute('current', true)
->setAttribute('countryName', (isset($countries[$session->getAttribute('countryCode')])) ? $countries[$session->getAttribute('countryCode')] : $locale->getText('locale.country.unknown'))
;
$response->dynamic($session, Response::MODEL_SESSION);
});
App::post('/v1/account/jwt') App::post('/v1/account/jwt')
->desc('Create Account JWT') ->desc('Create Account JWT')
->groups(['api', 'account']) ->groups(['api', 'account', 'auth'])
->label('scope', 'account') ->label('scope', 'account')
->label('docs', false) // Hidden for now - private beta ->label('auth.type', 'jwt')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'createJWT') ->label('sdk.method', 'createJWT')
->label('sdk.description', '/docs/references/account/create-jwt.md') ->label('sdk.description', '/docs/references/account/create-jwt.md')
@ -583,16 +767,18 @@ App::post('/v1/account/jwt')
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */ /** @var Appwrite\Database\Document $user */
$tokens = $user->getAttribute('tokens', []); $sessions = $user->getAttribute('sessions', []);
$session = new Document(); $current = new Document();
foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */ foreach ($sessions as $session) {
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too /** @var Appwrite\Database\Document $session */
$session = $token;
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$current = $session;
} }
} }
if($session->isEmpty()) { if($current->isEmpty()) {
throw new Exception('No valid session found', 401); throw new Exception('No valid session found', 401);
} }
@ -606,7 +792,7 @@ App::post('/v1/account/jwt')
// 'scopes' => ['user'], // 'scopes' => ['user'],
// 'iss' => 'http://api.mysite.com', // 'iss' => 'http://api.mysite.com',
'userId' => $user->getId(), 'userId' => $user->getId(),
'sessionId' => $session->getId(), 'sessionId' => $current->getId(),
])]), Response::MODEL_JWT); ])]), Response::MODEL_JWT);
}); });
@ -614,7 +800,7 @@ App::get('/v1/account')
->desc('Get Account') ->desc('Get Account')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/account/get.md') ->label('sdk.description', '/docs/references/account/get.md')
@ -634,13 +820,13 @@ App::get('/v1/account/prefs')
->desc('Get Account Preferences') ->desc('Get Account Preferences')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'getPrefs') ->label('sdk.method', 'getPrefs')
->label('sdk.description', '/docs/references/account/get-prefs.md') ->label('sdk.description', '/docs/references/account/get-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.response.model', Response::MODEL_PREFERENCES)
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->action(function ($response, $user) { ->action(function ($response, $user) {
@ -649,14 +835,14 @@ App::get('/v1/account/prefs')
$prefs = $user->getAttribute('prefs', new \stdClass()); $prefs = $user->getAttribute('prefs', new \stdClass());
$response->dynamic(new Document($prefs), Response::MODEL_ANY); $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
}); });
App::get('/v1/account/sessions') App::get('/v1/account/sessions')
->desc('Get Account Sessions') ->desc('Get Account Sessions')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'getSessions') ->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/account/get-sessions.md') ->label('sdk.description', '/docs/references/account/get-sessions.md')
@ -671,22 +857,19 @@ App::get('/v1/account/sessions')
/** @var Appwrite\Database\Document $user */ /** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
$tokens = $user->getAttribute('tokens', []); $sessions = $user->getAttribute('sessions', []);
$sessions = [];
$countries = $locale->getText('countries'); $countries = $locale->getText('countries');
$current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret); $current = Auth::sessionVerify($sessions, Auth::$secret);
foreach ($tokens as $token) { /* @var $token Document */ foreach ($sessions as $key => $session) {
if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { /** @var Document $session */
continue;
}
$token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('countryCode'))])) $session->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))]))
? $countries[strtoupper($token->getAttribute('countryCode'))] ? $countries[strtoupper($session->getAttribute('countryCode'))]
: $locale->getText('locale.country.unknown')); : $locale->getText('locale.country.unknown'));
$token->setAttribute('current', ($current == $token->getId()) ? true : false); $session->setAttribute('current', ($current == $session->getId()) ? true : false);
$sessions[] = $token; $sessions[$key] = $session;
} }
$response->dynamic(new Document([ $response->dynamic(new Document([
@ -699,7 +882,7 @@ App::get('/v1/account/logs')
->desc('Get Account Logs') ->desc('Get Account Logs')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'getLogs') ->label('sdk.method', 'getLogs')
->label('sdk.description', '/docs/references/account/get-logs.md') ->label('sdk.description', '/docs/references/account/get-logs.md')
@ -776,7 +959,7 @@ App::patch('/v1/account/name')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('event', 'account.update.name') ->label('event', 'account.update.name')
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'updateName') ->label('sdk.method', 'updateName')
->label('sdk.description', '/docs/references/account/update-name.md') ->label('sdk.description', '/docs/references/account/update-name.md')
@ -816,7 +999,7 @@ App::patch('/v1/account/password')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('event', 'account.update.password') ->label('event', 'account.update.password')
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'updatePassword') ->label('sdk.method', 'updatePassword')
->label('sdk.description', '/docs/references/account/update-password.md') ->label('sdk.description', '/docs/references/account/update-password.md')
@ -824,7 +1007,7 @@ App::patch('/v1/account/password')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER) ->label('sdk.response.model', Response::MODEL_USER)
->param('password', '', new Password(), 'New user password. Must be between 6 to 32 chars.') ->param('password', '', new Password(), 'New user password. Must be between 6 to 32 chars.')
->param('oldPassword', '', new Password(), 'Old user password. Must be between 6 to 32 chars.') ->param('oldPassword', '', new Password(), 'Old user password. Must be between 6 to 32 chars.', true)
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('projectDB') ->inject('projectDB')
@ -835,12 +1018,14 @@ App::patch('/v1/account/password')
/** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
if (!Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user 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);
} }
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'password' => Auth::passwordHash($password), 'password' => Auth::passwordHash($password),
'passwordUpdate' => \time(),
])); ]));
if (false === $user) { if (false === $user) {
@ -861,7 +1046,7 @@ App::patch('/v1/account/email')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('event', 'account.update.email') ->label('event', 'account.update.email')
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'updateEmail') ->label('sdk.method', 'updateEmail')
->label('sdk.description', '/docs/references/account/update-email.md') ->label('sdk.description', '/docs/references/account/update-email.md')
@ -880,7 +1065,12 @@ App::patch('/v1/account/email')
/** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password $isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); // Check if request is from an anonymous account for converting
if (
!$isAnonymousUser &&
!Auth::passwordVerify($password, $user->getAttribute('password'))
) { // Double check user password
throw new Exception('Invalid credentials', 401); throw new Exception('Invalid credentials', 401);
} }
@ -898,10 +1088,14 @@ App::patch('/v1/account/email')
// TODO after this user needs to confirm mail again // TODO after this user needs to confirm mail again
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [ $user = $projectDB->updateDocument(\array_merge(
'email' => $email, $user->getArrayCopy(),
'emailVerification' => false, ($isAnonymousUser ? [ 'password' => Auth::passwordHash($password) ] : []),
])); [
'email' => $email,
'emailVerification' => false,
]
));
if (false === $user) { if (false === $user) {
throw new Exception('Failed saving user to DB', 500); throw new Exception('Failed saving user to DB', 500);
@ -921,13 +1115,13 @@ App::patch('/v1/account/prefs')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('event', 'account.update.prefs') ->label('event', 'account.update.prefs')
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'updatePrefs') ->label('sdk.method', 'updatePrefs')
->label('sdk.description', '/docs/references/account/update-prefs.md') ->label('sdk.description', '/docs/references/account/update-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.response.model', Response::MODEL_USER)
->param('prefs', [], new Assoc(), 'Prefs key-value JSON object.') ->param('prefs', [], new Assoc(), 'Prefs key-value JSON object.')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
@ -960,7 +1154,7 @@ App::delete('/v1/account')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('event', 'account.delete') ->label('event', 'account.delete')
->label('scope', 'account') ->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'delete') ->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/account/delete.md') ->label('sdk.description', '/docs/references/account/delete.md')
@ -1005,7 +1199,7 @@ App::delete('/v1/account')
; ;
$events $events
->setParam('payload', $response->output($user, Response::MODEL_USER)) ->setParam('eventData', $response->output($user, Response::MODEL_USER))
; ;
if (!Config::getParam('domainVerification')) { if (!Config::getParam('domainVerification')) {
@ -1026,7 +1220,7 @@ App::delete('/v1/account/sessions/:sessionId')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'account') ->label('scope', 'account')
->label('event', 'account.sessions.delete') ->label('event', 'account.sessions.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'deleteSession') ->label('sdk.method', 'deleteSession')
->label('sdk.description', '/docs/references/account/delete-session.md') ->label('sdk.description', '/docs/references/account/delete-session.md')
@ -1050,14 +1244,16 @@ App::delete('/v1/account/sessions/:sessionId')
$protocol = $request->getProtocol(); $protocol = $request->getProtocol();
$sessionId = ($sessionId === 'current') $sessionId = ($sessionId === 'current')
? Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret) ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
: $sessionId; : $sessionId;
$tokens = $user->getAttribute('tokens', []); $sessions = $user->getAttribute('sessions', []);
foreach ($tokens as $token) { /* @var $token Document */ foreach ($sessions as $session) {
if (($sessionId == $token->getId()) && Auth::TOKEN_TYPE_LOGIN == $token->getAttribute('type')) { /** @var Document $session */
if (!$projectDB->deleteDocument($token->getId())) {
if (($sessionId == $session->getId())) {
if (!$projectDB->deleteDocument($session->getId())) {
throw new Exception('Failed to remove token from DB', 500); throw new Exception('Failed to remove token from DB', 500);
} }
@ -1073,10 +1269,10 @@ App::delete('/v1/account/sessions/:sessionId')
; ;
} }
$token->setAttribute('current', false); $session->setAttribute('current', false);
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$token->setAttribute('current', true); $session->setAttribute('current', true);
$response $response
->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
@ -1085,7 +1281,7 @@ App::delete('/v1/account/sessions/:sessionId')
} }
$events $events
->setParam('payload', $response->output($token, Response::MODEL_SESSION)) ->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
; ;
return $response->noContent(); return $response->noContent();
@ -1100,7 +1296,7 @@ App::delete('/v1/account/sessions')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'account') ->label('scope', 'account')
->label('event', 'account.sessions.delete') ->label('event', 'account.sessions.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'deleteSessions') ->label('sdk.method', 'deleteSessions')
->label('sdk.description', '/docs/references/account/delete-sessions.md') ->label('sdk.description', '/docs/references/account/delete-sessions.md')
@ -1122,10 +1318,12 @@ App::delete('/v1/account/sessions')
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
$protocol = $request->getProtocol(); $protocol = $request->getProtocol();
$tokens = $user->getAttribute('tokens', []); $sessions = $user->getAttribute('sessions', []);
foreach ($tokens as $token) { /* @var $token Document */ foreach ($sessions as $session) {
if (!$projectDB->deleteDocument($token->getId())) { /** @var Document $session */
if (!$projectDB->deleteDocument($session->getId())) {
throw new Exception('Failed to remove token from DB', 500); throw new Exception('Failed to remove token from DB', 500);
} }
@ -1141,10 +1339,10 @@ App::delete('/v1/account/sessions')
; ;
} }
$token->setAttribute('current', false); $session->setAttribute('current', false);
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$token->setAttribute('current', true); $session->setAttribute('current', true);
$response $response
->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
@ -1153,9 +1351,9 @@ App::delete('/v1/account/sessions')
} }
$events $events
->setParam('payload', $response->output(new Document([ ->setParam('eventData', $response->output(new Document([
'sum' => count($tokens), 'sum' => count($sessions),
'sessions' => $tokens 'sessions' => $sessions
]), Response::MODEL_SESSION_LIST)) ]), Response::MODEL_SESSION_LIST))
; ;
@ -1167,7 +1365,7 @@ App::post('/v1/account/recovery')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'public') ->label('scope', 'public')
->label('event', 'account.recovery.create') ->label('event', 'account.recovery.create')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'createRecovery') ->label('sdk.method', 'createRecovery')
->label('sdk.description', '/docs/references/account/create-recovery.md') ->label('sdk.description', '/docs/references/account/create-recovery.md')
@ -1196,7 +1394,7 @@ App::post('/v1/account/recovery')
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles); $isAppUser = Auth::isAppUser(Authorization::$roles);
$profile = $projectDB->getCollectionFirst([ // Get user by email address $profile = $projectDB->getCollectionFirst([ // Get user by email address
@ -1278,7 +1476,7 @@ App::post('/v1/account/recovery')
; ;
$events $events
->setParam('payload', ->setParam('eventData',
$response->output($recovery->setAttribute('secret', $secret), $response->output($recovery->setAttribute('secret', $secret),
Response::MODEL_TOKEN Response::MODEL_TOKEN
)) ))
@ -1286,7 +1484,7 @@ App::post('/v1/account/recovery')
$recovery // Hide secret for clients, sp $recovery // Hide secret for clients, sp
->setAttribute('secret', ->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $secret : ''); ($isPrivilegedUser || $isAppUser) ? $secret : '');
$audits $audits
->setParam('userId', $profile->getId()) ->setParam('userId', $profile->getId())
@ -1305,7 +1503,7 @@ App::put('/v1/account/recovery')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'public') ->label('scope', 'public')
->label('event', 'account.recovery.update') ->label('event', 'account.recovery.update')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'updateRecovery') ->label('sdk.method', 'updateRecovery')
->label('sdk.description', '/docs/references/account/update-recovery.md') ->label('sdk.description', '/docs/references/account/update-recovery.md')
@ -1384,7 +1582,7 @@ App::post('/v1/account/verification')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'account') ->label('scope', 'account')
->label('event', 'account.verification.create') ->label('event', 'account.verification.create')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'createVerification') ->label('sdk.method', 'createVerification')
->label('sdk.description', '/docs/references/account/create-verification.md') ->label('sdk.description', '/docs/references/account/create-verification.md')
@ -1414,7 +1612,7 @@ App::post('/v1/account/verification')
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $mails */ /** @var Appwrite\Event\Event $mails */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles); $isAppUser = Auth::isAppUser(Authorization::$roles);
$verificationSecret = Auth::tokenGenerator(); $verificationSecret = Auth::tokenGenerator();
@ -1481,7 +1679,7 @@ App::post('/v1/account/verification')
; ;
$events $events
->setParam('payload', ->setParam('eventData',
$response->output($verification->setAttribute('secret', $verificationSecret), $response->output($verification->setAttribute('secret', $verificationSecret),
Response::MODEL_TOKEN Response::MODEL_TOKEN
)) ))
@ -1489,7 +1687,7 @@ App::post('/v1/account/verification')
$verification // Hide secret for clients, sp $verification // Hide secret for clients, sp
->setAttribute('secret', ->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $verificationSecret : ''); ($isPrivilegedUser || $isAppUser) ? $verificationSecret : '');
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
@ -1508,7 +1706,7 @@ App::put('/v1/account/verification')
->groups(['api', 'account']) ->groups(['api', 'account'])
->label('scope', 'public') ->label('scope', 'public')
->label('event', 'account.verification.update') ->label('event', 'account.verification.update')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account') ->label('sdk.namespace', 'account')
->label('sdk.method', 'updateVerification') ->label('sdk.method', 'updateVerification')
->label('sdk.description', '/docs/references/account/update-verification.md') ->label('sdk.description', '/docs/references/account/update-verification.md')

View file

@ -14,7 +14,7 @@ use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor; use Utopia\Validator\HexColor;
use Utopia\Validator\Range; use Utopia\Validator\Range;
use Utopia\Validator\Text; use Utopia\Validator\Text;
use Utopia\Validator\URL; use Appwrite\Network\Validator\URL;
use Utopia\Validator\WhiteList; use Utopia\Validator\WhiteList;
$avatarCallback = function ($type, $code, $width, $height, $quality, $response) { $avatarCallback = function ($type, $code, $width, $height, $quality, $response) {
@ -38,7 +38,7 @@ $avatarCallback = function ($type, $code, $width, $height, $quality, $response)
$output = 'png'; $output = 'png';
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache
$key = \md5('/v1/avatars/:type/:code-' . $code . $width . $height . $quality . $output); $key = \md5('/v1/avatars/'.$type.'/:code-' . $code . $width . $height . $quality . $output);
$path = $set[$code]; $path = $set[$code];
$type = 'png'; $type = 'png';
@ -83,7 +83,7 @@ App::get('/v1/avatars/credit-cards/:code')
->desc('Get Credit Card Icon') ->desc('Get Credit Card Icon')
->groups(['api', 'avatars']) ->groups(['api', 'avatars'])
->label('scope', 'avatars.read') ->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars') ->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getCreditCard') ->label('sdk.method', 'getCreditCard')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
@ -103,7 +103,7 @@ App::get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon') ->desc('Get Browser Icon')
->groups(['api', 'avatars']) ->groups(['api', 'avatars'])
->label('scope', 'avatars.read') ->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars') ->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getBrowser') ->label('sdk.method', 'getBrowser')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
@ -123,7 +123,7 @@ App::get('/v1/avatars/flags/:code')
->desc('Get Country Flag') ->desc('Get Country Flag')
->groups(['api', 'avatars']) ->groups(['api', 'avatars'])
->label('scope', 'avatars.read') ->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars') ->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFlag') ->label('sdk.method', 'getFlag')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
@ -143,7 +143,7 @@ App::get('/v1/avatars/image')
->desc('Get Image from URL') ->desc('Get Image from URL')
->groups(['api', 'avatars']) ->groups(['api', 'avatars'])
->label('scope', 'avatars.read') ->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars') ->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getImage') ->label('sdk.method', 'getImage')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
@ -212,7 +212,7 @@ App::get('/v1/avatars/favicon')
->desc('Get Favicon') ->desc('Get Favicon')
->groups(['api', 'avatars']) ->groups(['api', 'avatars'])
->label('scope', 'avatars.read') ->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars') ->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFavicon') ->label('sdk.method', 'getFavicon')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
@ -365,7 +365,7 @@ App::get('/v1/avatars/qr')
->desc('Get QR Code') ->desc('Get QR Code')
->groups(['api', 'avatars']) ->groups(['api', 'avatars'])
->label('scope', 'avatars.read') ->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars') ->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getQR') ->label('sdk.method', 'getQR')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
@ -408,7 +408,7 @@ App::get('/v1/avatars/initials')
->desc('Get User Initials') ->desc('Get User Initials')
->groups(['api', 'avatars']) ->groups(['api', 'avatars'])
->label('scope', 'avatars.read') ->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars') ->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getInitials') ->label('sdk.method', 'getInitials')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')

View file

@ -24,7 +24,7 @@ App::post('/v1/database/collections')
->label('event', 'database.collections.create') ->label('event', 'database.collections.create')
->label('scope', 'collections.write') ->label('scope', 'collections.write')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createCollection') ->label('sdk.method', 'createCollection')
->label('sdk.description', '/docs/references/database/create-collection.md') ->label('sdk.description', '/docs/references/database/create-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -96,7 +96,7 @@ App::get('/v1/database/collections')
->groups(['api', 'database']) ->groups(['api', 'database'])
->label('scope', 'collections.read') ->label('scope', 'collections.read')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'listCollections') ->label('sdk.method', 'listCollections')
->label('sdk.description', '/docs/references/database/list-collections.md') ->label('sdk.description', '/docs/references/database/list-collections.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -133,7 +133,7 @@ App::get('/v1/database/collections/:collectionId')
->groups(['api', 'database']) ->groups(['api', 'database'])
->label('scope', 'collections.read') ->label('scope', 'collections.read')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'getCollection') ->label('sdk.method', 'getCollection')
->label('sdk.description', '/docs/references/database/get-collection.md') ->label('sdk.description', '/docs/references/database/get-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -161,7 +161,7 @@ App::put('/v1/database/collections/:collectionId')
->label('scope', 'collections.write') ->label('scope', 'collections.write')
->label('event', 'database.collections.update') ->label('event', 'database.collections.update')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'updateCollection') ->label('sdk.method', 'updateCollection')
->label('sdk.description', '/docs/references/database/update-collection.md') ->label('sdk.description', '/docs/references/database/update-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -238,7 +238,7 @@ App::delete('/v1/database/collections/:collectionId')
->label('scope', 'collections.write') ->label('scope', 'collections.write')
->label('event', 'database.collections.delete') ->label('event', 'database.collections.delete')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'deleteCollection') ->label('sdk.method', 'deleteCollection')
->label('sdk.description', '/docs/references/database/delete-collection.md') ->label('sdk.description', '/docs/references/database/delete-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -271,7 +271,7 @@ App::delete('/v1/database/collections/:collectionId')
; ;
$events $events
->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION)) ->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION))
; ;
$audits $audits
@ -289,12 +289,12 @@ App::post('/v1/database/collections/:collectionId/documents')
->label('event', 'database.documents.create') ->label('event', 'database.documents.create')
->label('scope', 'documents.write') ->label('scope', 'documents.write')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.method', 'createDocument') ->label('sdk.method', 'createDocument')
->label('sdk.description', '/docs/references/database/create-document.md') ->label('sdk.description', '/docs/references/database/create-document.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).') ->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('data', [], new JSON(), 'Document data as JSON object.') ->param('data', [], new JSON(), 'Document data as JSON object.')
->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) ->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
@ -401,7 +401,7 @@ App::post('/v1/database/collections/:collectionId/documents')
$response $response
->setStatusCode(Response::STATUS_CODE_CREATED) ->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($data, Response::MODEL_ANY) ->dynamic($data, Response::MODEL_DOCUMENT)
; ;
}); });
@ -410,7 +410,7 @@ App::get('/v1/database/collections/:collectionId/documents')
->groups(['api', 'database']) ->groups(['api', 'database'])
->label('scope', 'documents.read') ->label('scope', 'documents.read')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.method', 'listDocuments') ->label('sdk.method', 'listDocuments')
->label('sdk.description', '/docs/references/database/list-documents.md') ->label('sdk.description', '/docs/references/database/list-documents.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -473,12 +473,12 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
->groups(['api', 'database']) ->groups(['api', 'database'])
->label('scope', 'documents.read') ->label('scope', 'documents.read')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.method', 'getDocument') ->label('sdk.method', 'getDocument')
->label('sdk.description', '/docs/references/database/get-document.md') ->label('sdk.description', '/docs/references/database/get-document.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).') ->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document unique ID.') ->param('documentId', null, new UID(), 'Document unique ID.')
->inject('response') ->inject('response')
@ -494,7 +494,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('No document found', 404); throw new Exception('No document found', 404);
} }
$response->dynamic($document, Response::MODEL_ANY); $response->dynamic($document, Response::MODEL_DOCUMENT);
}); });
App::patch('/v1/database/collections/:collectionId/documents/:documentId') App::patch('/v1/database/collections/:collectionId/documents/:documentId')
@ -503,12 +503,12 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->label('event', 'database.documents.update') ->label('event', 'database.documents.update')
->label('scope', 'documents.write') ->label('scope', 'documents.write')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.method', 'updateDocument') ->label('sdk.method', 'updateDocument')
->label('sdk.description', '/docs/references/database/update-document.md') ->label('sdk.description', '/docs/references/database/update-document.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).') ->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document unique ID.') ->param('documentId', null, new UID(), 'Document unique ID.')
->param('data', [], new JSON(), 'Document data as JSON object.') ->param('data', [], new JSON(), 'Document data as JSON object.')
@ -566,7 +566,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->setParam('data', $data->getArrayCopy()) ->setParam('data', $data->getArrayCopy())
; ;
$response->dynamic($data, Response::MODEL_ANY); $response->dynamic($data, Response::MODEL_DOCUMENT);
}); });
App::delete('/v1/database/collections/:collectionId/documents/:documentId') App::delete('/v1/database/collections/:collectionId/documents/:documentId')
@ -575,7 +575,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->label('scope', 'documents.write') ->label('scope', 'documents.write')
->label('event', 'database.documents.delete') ->label('event', 'database.documents.delete')
->label('sdk.namespace', 'database') ->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.method', 'deleteDocument') ->label('sdk.method', 'deleteDocument')
->label('sdk.description', '/docs/references/database/delete-document.md') ->label('sdk.description', '/docs/references/database/delete-document.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -614,9 +614,9 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
} }
$events $events
->setParam('payload', $response->output($document, Response::MODEL_ANY)) ->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT))
; ;
$audits $audits
->setParam('event', 'database.documents.delete') ->setParam('event', 'database.documents.delete')
->setParam('resource', 'database/document/'.$document->getId()) ->setParam('resource', 'database/document/'.$document->getId())

View file

@ -1,5 +1,7 @@
<?php <?php
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Database\Database; use Appwrite\Database\Database;
use Appwrite\Database\Document; use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization; use Appwrite\Database\Validator\Authorization;
@ -27,7 +29,8 @@ App::post('/v1/functions')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Create Function') ->desc('Create Function')
->label('scope', 'functions.write') ->label('scope', 'functions.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('event', 'functions.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'create') ->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/functions/create-function.md') ->label('sdk.description', '/docs/references/functions/create-function.md')
@ -36,7 +39,7 @@ App::post('/v1/functions')
->label('sdk.response.model', Response::MODEL_FUNCTION) ->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.') ->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.') ->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('env', '', new WhiteList(array_keys(Config::getParam('environments')), true), 'Execution enviornment.') ->param('env', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution enviornment.')
->param('vars', [], new Assoc(), 'Key-value JSON object.', true) ->param('vars', [], new Assoc(), 'Key-value JSON object.', true)
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true) ->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true) ->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
@ -80,7 +83,7 @@ App::get('/v1/functions')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('List Functions') ->desc('List Functions')
->label('scope', 'functions.read') ->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'list') ->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/functions/list-functions.md') ->label('sdk.description', '/docs/references/functions/list-functions.md')
@ -117,7 +120,7 @@ App::get('/v1/functions/:functionId')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Get Function') ->desc('Get Function')
->label('scope', 'functions.read') ->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/functions/get-function.md') ->label('sdk.description', '/docs/references/functions/get-function.md')
@ -144,7 +147,7 @@ App::get('/v1/functions/:functionId/usage')
->desc('Get Function Usage') ->desc('Get Function Usage')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->label('scope', 'functions.read') ->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_CONSOLE]) ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'getUsage') ->label('sdk.method', 'getUsage')
->param('functionId', '', new UID(), 'Function unique ID.') ->param('functionId', '', new UID(), 'Function unique ID.')
@ -265,7 +268,8 @@ App::put('/v1/functions/:functionId')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Update Function') ->desc('Update Function')
->label('scope', 'functions.write') ->label('scope', 'functions.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('event', 'functions.update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'update') ->label('sdk.method', 'update')
->label('sdk.description', '/docs/references/functions/update-function.md') ->label('sdk.description', '/docs/references/functions/update-function.md')
@ -317,6 +321,7 @@ App::put('/v1/functions/:functionId')
if ($next && $schedule !== $original) { if ($next && $schedule !== $original) {
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [ ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(), 'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(), 'functionId' => $function->getId(),
'executionId' => null, 'executionId' => null,
'trigger' => 'schedule', 'trigger' => 'schedule',
@ -330,7 +335,8 @@ App::patch('/v1/functions/:functionId/tag')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Update Function Tag') ->desc('Update Function Tag')
->label('scope', 'functions.write') ->label('scope', 'functions.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('event', 'functions.tags.update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateTag') ->label('sdk.method', 'updateTag')
->label('sdk.description', '/docs/references/functions/update-function-tag.md') ->label('sdk.description', '/docs/references/functions/update-function-tag.md')
@ -370,6 +376,7 @@ App::patch('/v1/functions/:functionId/tag')
if ($next) { // Init first schedule if ($next) { // Init first schedule
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [ ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(), 'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(), 'functionId' => $function->getId(),
'executionId' => null, 'executionId' => null,
'trigger' => 'schedule', 'trigger' => 'schedule',
@ -387,7 +394,8 @@ App::delete('/v1/functions/:functionId')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Delete Function') ->desc('Delete Function')
->label('scope', 'functions.write') ->label('scope', 'functions.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('event', 'functions.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'delete') ->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/functions/delete-function.md') ->label('sdk.description', '/docs/references/functions/delete-function.md')
@ -424,7 +432,8 @@ App::post('/v1/functions/:functionId/tags')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Create Tag') ->desc('Create Tag')
->label('scope', 'functions.write') ->label('scope', 'functions.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('event', 'functions.tags.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'createTag') ->label('sdk.method', 'createTag')
->label('sdk.description', '/docs/references/functions/create-tag.md') ->label('sdk.description', '/docs/references/functions/create-tag.md')
@ -518,7 +527,7 @@ App::get('/v1/functions/:functionId/tags')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('List Tags') ->desc('List Tags')
->label('scope', 'functions.read') ->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'listTags') ->label('sdk.method', 'listTags')
->label('sdk.description', '/docs/references/functions/list-tags.md') ->label('sdk.description', '/docs/references/functions/list-tags.md')
@ -563,7 +572,7 @@ App::get('/v1/functions/:functionId/tags/:tagId')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Get Tag') ->desc('Get Tag')
->label('scope', 'functions.read') ->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'getTag') ->label('sdk.method', 'getTag')
->label('sdk.description', '/docs/references/functions/get-tag.md') ->label('sdk.description', '/docs/references/functions/get-tag.md')
@ -601,7 +610,8 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Delete Tag') ->desc('Delete Tag')
->label('scope', 'functions.write') ->label('scope', 'functions.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('event', 'functions.tags.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'deleteTag') ->label('sdk.method', 'deleteTag')
->label('sdk.description', '/docs/references/functions/delete-tag.md') ->label('sdk.description', '/docs/references/functions/delete-tag.md')
@ -662,7 +672,8 @@ App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Create Execution') ->desc('Create Execution')
->label('scope', 'execution.write') ->label('scope', 'execution.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('event', 'functions.executions.create')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'createExecution') ->label('sdk.method', 'createExecution')
->label('sdk.description', '/docs/references/functions/create-execution.md') ->label('sdk.description', '/docs/references/functions/create-execution.md')
@ -672,14 +683,17 @@ App::post('/v1/functions/:functionId/executions')
->label('abuse-limit', 60) ->label('abuse-limit', 60)
->label('abuse-time', 60) ->label('abuse-time', 60)
->param('functionId', '', new UID(), 'Function unique ID.') ->param('functionId', '', new UID(), 'Function unique ID.')
->param('data', '', new Text(8192), 'String of custom data to send to function.', true)
// ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true) // ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true)
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('projectDB') ->inject('projectDB')
->action(function ($functionId, /*$async,*/ $response, $project, $projectDB) { ->inject('user')
->action(function ($functionId, $data, /*$async,*/ $response, $project, $projectDB, $user) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */ /** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Database\Document $user */
Authorization::disable(); Authorization::disable();
@ -712,7 +726,7 @@ App::post('/v1/functions/:functionId/executions')
$execution = $projectDB->createDocument([ $execution = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_EXECUTIONS, '$collection' => Database::SYSTEM_COLLECTION_EXECUTIONS,
'$permissions' => [ '$permissions' => [
'read' => $function->getPermissions()['execute'] ?? [], 'read' => (!empty($user->getId())) ? ['user:' . $user->getId()] : [],
'write' => [], 'write' => [],
], ],
'dateCreated' => time(), 'dateCreated' => time(),
@ -730,12 +744,37 @@ App::post('/v1/functions/:functionId/executions')
if (false === $execution) { if (false === $execution) {
throw new Exception('Failed saving execution to DB', 500); throw new Exception('Failed saving execution to DB', 500);
} }
$jwt = ''; // initialize
if (!empty($user->getId())) { // If userId exists, generate a JWT for function
$tokens = $user->getAttribute('tokens', []);
$session = new Document();
foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$session = $token;
}
}
if(!$session->isEmpty()) {
$jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
'sessionId' => $session->getId(),
]);
}
}
Resque::enqueue('v1-functions', 'FunctionsV1', [ Resque::enqueue('v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(), 'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(), 'functionId' => $function->getId(),
'executionId' => $execution->getId(), 'executionId' => $execution->getId(),
'trigger' => 'http', 'trigger' => 'http',
'data' => $data,
'userId' => $user->getId(),
'jwt' => $jwt,
]); ]);
$response $response
@ -748,7 +787,7 @@ App::get('/v1/functions/:functionId/executions')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('List Executions') ->desc('List Executions')
->label('scope', 'execution.read') ->label('scope', 'execution.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'listExecutions') ->label('sdk.method', 'listExecutions')
->label('sdk.description', '/docs/references/functions/list-executions.md') ->label('sdk.description', '/docs/references/functions/list-executions.md')
@ -766,7 +805,9 @@ App::get('/v1/functions/:functionId/executions')
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Database\Database $projectDB */
Authorization::disable();
$function = $projectDB->getDocument($functionId); $function = $projectDB->getDocument($functionId);
Authorization::reset();
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) { if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
@ -793,7 +834,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Get Execution') ->desc('Get Execution')
->label('scope', 'execution.read') ->label('scope', 'execution.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'getExecution') ->label('sdk.method', 'getExecution')
->label('sdk.description', '/docs/references/functions/get-execution.md') ->label('sdk.description', '/docs/references/functions/get-execution.md')
@ -808,7 +849,9 @@ App::get('/v1/functions/:functionId/executions/:executionId')
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Database\Database $projectDB */
Authorization::disable();
$function = $projectDB->getDocument($functionId); $function = $projectDB->getDocument($functionId);
Authorization::reset();
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) { if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);

View file

@ -11,7 +11,7 @@ App::get('/v1/health')
->desc('Get HTTP') ->desc('Get HTTP')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/health/get.md') ->label('sdk.description', '/docs/references/health/get.md')
@ -37,7 +37,7 @@ App::get('/v1/health/db')
->desc('Get DB') ->desc('Get DB')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getDB') ->label('sdk.method', 'getDB')
->label('sdk.description', '/docs/references/health/get-db.md') ->label('sdk.description', '/docs/references/health/get-db.md')
@ -56,7 +56,7 @@ App::get('/v1/health/cache')
->desc('Get Cache') ->desc('Get Cache')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getCache') ->label('sdk.method', 'getCache')
->label('sdk.description', '/docs/references/health/get-cache.md') ->label('sdk.description', '/docs/references/health/get-cache.md')
@ -74,7 +74,7 @@ App::get('/v1/health/time')
->desc('Get Time') ->desc('Get Time')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getTime') ->label('sdk.method', 'getTime')
->label('sdk.description', '/docs/references/health/get-time.md') ->label('sdk.description', '/docs/references/health/get-time.md')
@ -123,7 +123,7 @@ App::get('/v1/health/queue/webhooks')
->desc('Get Webhooks Queue') ->desc('Get Webhooks Queue')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueWebhooks') ->label('sdk.method', 'getQueueWebhooks')
->label('sdk.description', '/docs/references/health/get-queue-webhooks.md') ->label('sdk.description', '/docs/references/health/get-queue-webhooks.md')
@ -138,7 +138,7 @@ App::get('/v1/health/queue/tasks')
->desc('Get Tasks Queue') ->desc('Get Tasks Queue')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueTasks') ->label('sdk.method', 'getQueueTasks')
->label('sdk.description', '/docs/references/health/get-queue-tasks.md') ->label('sdk.description', '/docs/references/health/get-queue-tasks.md')
@ -153,7 +153,7 @@ App::get('/v1/health/queue/logs')
->desc('Get Logs Queue') ->desc('Get Logs Queue')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueLogs') ->label('sdk.method', 'getQueueLogs')
->label('sdk.description', '/docs/references/health/get-queue-logs.md') ->label('sdk.description', '/docs/references/health/get-queue-logs.md')
@ -168,7 +168,7 @@ App::get('/v1/health/queue/usage')
->desc('Get Usage Queue') ->desc('Get Usage Queue')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueUsage') ->label('sdk.method', 'getQueueUsage')
->label('sdk.description', '/docs/references/health/get-queue-usage.md') ->label('sdk.description', '/docs/references/health/get-queue-usage.md')
@ -183,7 +183,7 @@ App::get('/v1/health/queue/certificates')
->desc('Get Certificate Queue') ->desc('Get Certificate Queue')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueCertificates') ->label('sdk.method', 'getQueueCertificates')
->label('sdk.description', '/docs/references/health/get-queue-certificates.md') ->label('sdk.description', '/docs/references/health/get-queue-certificates.md')
@ -198,7 +198,7 @@ App::get('/v1/health/queue/functions')
->desc('Get Functions Queue') ->desc('Get Functions Queue')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueFunctions') ->label('sdk.method', 'getQueueFunctions')
->label('sdk.description', '/docs/references/health/get-queue-functions.md') ->label('sdk.description', '/docs/references/health/get-queue-functions.md')
@ -213,7 +213,7 @@ App::get('/v1/health/storage/local')
->desc('Get Local Storage') ->desc('Get Local Storage')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getStorageLocal') ->label('sdk.method', 'getStorageLocal')
->label('sdk.description', '/docs/references/health/get-storage-local.md') ->label('sdk.description', '/docs/references/health/get-storage-local.md')
@ -245,7 +245,7 @@ App::get('/v1/health/anti-virus')
->desc('Get Anti virus') ->desc('Get Anti virus')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'health.read') ->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health') ->label('sdk.namespace', 'health')
->label('sdk.method', 'getAntiVirus') ->label('sdk.method', 'getAntiVirus')
->label('sdk.description', '/docs/references/health/get-storage-anti-virus.md') ->label('sdk.description', '/docs/references/health/get-storage-anti-virus.md')
@ -272,8 +272,8 @@ App::get('/v1/health/anti-virus')
App::get('/v1/health/stats') // Currently only used internally App::get('/v1/health/stats') // Currently only used internally
->desc('Get System Stats') ->desc('Get System Stats')
->groups(['api', 'health']) ->groups(['api', 'health'])
->label('scope', 'god') ->label('scope', 'root')
// ->label('sdk.platform', [APP_PLATFORM_SERVER]) // ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
// ->label('sdk.namespace', 'health') // ->label('sdk.namespace', 'health')
// ->label('sdk.method', 'getStats') // ->label('sdk.method', 'getStats')
->label('docs', false) ->label('docs', false)

View file

@ -9,7 +9,7 @@ App::get('/v1/locale')
->desc('Get User Locale') ->desc('Get User Locale')
->groups(['api', 'locale']) ->groups(['api', 'locale'])
->label('scope', 'locale.read') ->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'locale') ->label('sdk.namespace', 'locale')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/locale/get-locale.md') ->label('sdk.description', '/docs/references/locale/get-locale.md')
@ -74,7 +74,7 @@ App::get('/v1/locale/countries')
->desc('List Countries') ->desc('List Countries')
->groups(['api', 'locale']) ->groups(['api', 'locale'])
->label('scope', 'locale.read') ->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'locale') ->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountries') ->label('sdk.method', 'getCountries')
->label('sdk.description', '/docs/references/locale/get-countries.md') ->label('sdk.description', '/docs/references/locale/get-countries.md')
@ -106,7 +106,7 @@ App::get('/v1/locale/countries/eu')
->desc('List EU Countries') ->desc('List EU Countries')
->groups(['api', 'locale']) ->groups(['api', 'locale'])
->label('scope', 'locale.read') ->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'locale') ->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountriesEU') ->label('sdk.method', 'getCountriesEU')
->label('sdk.description', '/docs/references/locale/get-countries-eu.md') ->label('sdk.description', '/docs/references/locale/get-countries-eu.md')
@ -141,7 +141,7 @@ App::get('/v1/locale/countries/phones')
->desc('List Countries Phone Codes') ->desc('List Countries Phone Codes')
->groups(['api', 'locale']) ->groups(['api', 'locale'])
->label('scope', 'locale.read') ->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'locale') ->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountriesPhones') ->label('sdk.method', 'getCountriesPhones')
->label('sdk.description', '/docs/references/locale/get-countries-phones.md') ->label('sdk.description', '/docs/references/locale/get-countries-phones.md')
@ -177,7 +177,7 @@ App::get('/v1/locale/continents')
->desc('List Continents') ->desc('List Continents')
->groups(['api', 'locale']) ->groups(['api', 'locale'])
->label('scope', 'locale.read') ->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'locale') ->label('sdk.namespace', 'locale')
->label('sdk.method', 'getContinents') ->label('sdk.method', 'getContinents')
->label('sdk.description', '/docs/references/locale/get-continents.md') ->label('sdk.description', '/docs/references/locale/get-continents.md')
@ -208,7 +208,7 @@ App::get('/v1/locale/currencies')
->desc('List Currencies') ->desc('List Currencies')
->groups(['api', 'locale']) ->groups(['api', 'locale'])
->label('scope', 'locale.read') ->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'locale') ->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCurrencies') ->label('sdk.method', 'getCurrencies')
->label('sdk.description', '/docs/references/locale/get-currencies.md') ->label('sdk.description', '/docs/references/locale/get-currencies.md')
@ -233,7 +233,7 @@ App::get('/v1/locale/languages')
->desc('List Languages') ->desc('List Languages')
->groups(['api', 'locale']) ->groups(['api', 'locale'])
->label('scope', 'locale.read') ->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'locale') ->label('sdk.namespace', 'locale')
->label('sdk.method', 'getLanguages') ->label('sdk.method', 'getLanguages')
->label('sdk.description', '/docs/references/locale/get-languages.md') ->label('sdk.description', '/docs/references/locale/get-languages.md')

View file

@ -6,8 +6,9 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean; use Utopia\Validator\Boolean;
use Utopia\Validator\Text; use Utopia\Validator\Text;
use Utopia\Validator\WhiteList; use Utopia\Validator\WhiteList;
use Utopia\Validator\URL; use Appwrite\Network\Validator\URL;
use Utopia\Validator\Range; use Utopia\Validator\Range;
use Utopia\Validator\Integer;
use Utopia\Config\Config; use Utopia\Config\Config;
use Utopia\Domains\Domain; use Utopia\Domains\Domain;
use Appwrite\Auth\Auth; use Appwrite\Auth\Auth;
@ -24,6 +25,7 @@ App::post('/v1/projects')
->desc('Create Project') ->desc('Create Project')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'create') ->label('sdk.method', 'create')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -96,6 +98,7 @@ App::get('/v1/projects')
->desc('List Projects') ->desc('List Projects')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'list') ->label('sdk.method', 'list')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -131,6 +134,7 @@ App::get('/v1/projects/:projectId')
->desc('Get Project') ->desc('Get Project')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -156,6 +160,7 @@ App::get('/v1/projects/:projectId/usage')
->desc('Get Project') ->desc('Get Project')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'getUsage') ->label('sdk.method', 'getUsage')
->param('projectId', '', new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
@ -356,6 +361,7 @@ App::patch('/v1/projects/:projectId')
->desc('Update Project') ->desc('Update Project')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'update') ->label('sdk.method', 'update')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -408,6 +414,7 @@ App::patch('/v1/projects/:projectId/oauth2')
->desc('Update Project OAuth2') ->desc('Update Project OAuth2')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateOAuth2') ->label('sdk.method', 'updateOAuth2')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -441,10 +448,85 @@ App::patch('/v1/projects/:projectId/oauth2')
$response->dynamic($project, Response::MODEL_PROJECT); $response->dynamic($project, Response::MODEL_PROJECT);
}); });
App::patch('/v1/projects/:projectId/auth/limit')
->desc('Update Project users limit')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateAuthLimit')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('limit', false, new Integer(true), 'Set the max number of users allowed in this project. Use 0 for unlimited.')
->inject('response')
->inject('consoleDB')
->action(function ($projectId, $limit, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS != $project->getCollection()) {
throw new Exception('Project not found', 404);
}
if (false === $consoleDB->updateDocument(
\array_merge($project->getArrayCopy(), [
'usersAuthLimit' => $limit,
]))
) {
throw new Exception('Failed saving project to DB', 500);
};
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/:method')
->desc('Update Project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateAuthStatus')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: '.implode(',', \array_keys(Config::getParam('auth'))), false)
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
->inject('response')
->inject('consoleDB')
->action(function ($projectId, $method, $status, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
$auth = Config::getParam('auth')[$method] ?? [];
$authKey = $auth['key'] ?? '';
$status = ($status === '1' || $status === 'true' || $status === 1 || $status === true);
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS != $project->getCollection()) {
throw new Exception('Project not found', 404);
}
if (false === $consoleDB->updateDocument(
\array_merge($project->getArrayCopy(), [
$authKey => $status,
]))
) {
throw new Exception('Failed saving project to DB', 500);
};
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::delete('/v1/projects/:projectId') App::delete('/v1/projects/:projectId')
->desc('Delete Project') ->desc('Delete Project')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'delete') ->label('sdk.method', 'delete')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -510,6 +592,7 @@ App::post('/v1/projects/:projectId/webhooks')
->desc('Create Webhook') ->desc('Create Webhook')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'createWebhook') ->label('sdk.method', 'createWebhook')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -572,6 +655,7 @@ App::get('/v1/projects/:projectId/webhooks')
->desc('List Webhooks') ->desc('List Webhooks')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'listWebhooks') ->label('sdk.method', 'listWebhooks')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -602,6 +686,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Get Webhook') ->desc('Get Webhook')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'getWebhook') ->label('sdk.method', 'getWebhook')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -634,6 +719,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Update Webhook') ->desc('Update Webhook')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateWebhook') ->label('sdk.method', 'updateWebhook')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -687,6 +773,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Delete Webhook') ->desc('Delete Webhook')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteWebhook') ->label('sdk.method', 'deleteWebhook')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -724,6 +811,7 @@ App::post('/v1/projects/:projectId/keys')
->desc('Create Key') ->desc('Create Key')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'createKey') ->label('sdk.method', 'createKey')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -777,6 +865,7 @@ App::get('/v1/projects/:projectId/keys')
->desc('List Keys') ->desc('List Keys')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'listKeys') ->label('sdk.method', 'listKeys')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -807,6 +896,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->desc('Get Key') ->desc('Get Key')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'getKey') ->label('sdk.method', 'getKey')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -836,6 +926,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->desc('Update Key') ->desc('Update Key')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateKey') ->label('sdk.method', 'updateKey')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -879,6 +970,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->desc('Delete Key') ->desc('Delete Key')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteKey') ->label('sdk.method', 'deleteKey')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -916,6 +1008,7 @@ App::post('/v1/projects/:projectId/tasks')
->desc('Create Task') ->desc('Create Task')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'createTask') ->label('sdk.method', 'createTask')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -996,6 +1089,7 @@ App::get('/v1/projects/:projectId/tasks')
->desc('List Tasks') ->desc('List Tasks')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'listTasks') ->label('sdk.method', 'listTasks')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1027,6 +1121,7 @@ App::get('/v1/projects/:projectId/tasks/:taskId')
->desc('Get Task') ->desc('Get Task')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'getTask') ->label('sdk.method', 'getTask')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1059,6 +1154,7 @@ App::put('/v1/projects/:projectId/tasks/:taskId')
->desc('Update Task') ->desc('Update Task')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateTask') ->label('sdk.method', 'updateTask')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1127,6 +1223,7 @@ App::delete('/v1/projects/:projectId/tasks/:taskId')
->desc('Delete Task') ->desc('Delete Task')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteTask') ->label('sdk.method', 'deleteTask')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -1164,6 +1261,7 @@ App::post('/v1/projects/:projectId/platforms')
->desc('Create Platform') ->desc('Create Platform')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'createPlatform') ->label('sdk.method', 'createPlatform')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -1224,6 +1322,7 @@ App::get('/v1/projects/:projectId/platforms')
->desc('List Platforms') ->desc('List Platforms')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'listPlatforms') ->label('sdk.method', 'listPlatforms')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1254,6 +1353,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->desc('Get Platform') ->desc('Get Platform')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'getPlatform') ->label('sdk.method', 'getPlatform')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1286,6 +1386,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->desc('Update Platform') ->desc('Update Platform')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'updatePlatform') ->label('sdk.method', 'updatePlatform')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1334,6 +1435,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->desc('Delete Platform') ->desc('Delete Platform')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'deletePlatform') ->label('sdk.method', 'deletePlatform')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -1371,6 +1473,7 @@ App::post('/v1/projects/:projectId/domains')
->desc('Create Domain') ->desc('Create Domain')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'createDomain') ->label('sdk.method', 'createDomain')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -1440,6 +1543,7 @@ App::get('/v1/projects/:projectId/domains')
->desc('List Domains') ->desc('List Domains')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'listDomains') ->label('sdk.method', 'listDomains')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1470,6 +1574,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
->desc('Get Domain') ->desc('Get Domain')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.read') ->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'getDomain') ->label('sdk.method', 'getDomain')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1502,6 +1607,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
->desc('Update Domain Verification Status') ->desc('Update Domain Verification Status')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateDomainVerification') ->label('sdk.method', 'updateDomainVerification')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1565,6 +1671,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
->desc('Delete Domain') ->desc('Delete Domain')
->groups(['api', 'projects']) ->groups(['api', 'projects'])
->label('scope', 'projects.write') ->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects') ->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteDomain') ->label('sdk.method', 'deleteDomain')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)

View file

@ -28,7 +28,7 @@ App::post('/v1/storage/files')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.write') ->label('scope', 'files.write')
->label('event', 'storage.files.create') ->label('event', 'storage.files.create')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'createFile') ->label('sdk.method', 'createFile')
->label('sdk.description', '/docs/references/storage/create-file.md') ->label('sdk.description', '/docs/references/storage/create-file.md')
@ -115,7 +115,7 @@ App::post('/v1/storage/files')
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag); $data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
if (!$device->write($path, $data)) { if (!$device->write($path, $data, $mimeType)) {
throw new Exception('Failed to save file', 500); throw new Exception('Failed to save file', 500);
} }
@ -167,7 +167,7 @@ App::get('/v1/storage/files')
->desc('List Files') ->desc('List Files')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.read') ->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'listFiles') ->label('sdk.method', 'listFiles')
->label('sdk.description', '/docs/references/storage/list-files.md') ->label('sdk.description', '/docs/references/storage/list-files.md')
@ -204,7 +204,7 @@ App::get('/v1/storage/files/:fileId')
->desc('Get File') ->desc('Get File')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.read') ->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFile') ->label('sdk.method', 'getFile')
->label('sdk.description', '/docs/references/storage/get-file.md') ->label('sdk.description', '/docs/references/storage/get-file.md')
@ -231,7 +231,7 @@ App::get('/v1/storage/files/:fileId/preview')
->desc('Get File Preview') ->desc('Get File Preview')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.read') ->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFilePreview') ->label('sdk.method', 'getFilePreview')
->label('sdk.description', '/docs/references/storage/get-file-preview.md') ->label('sdk.description', '/docs/references/storage/get-file-preview.md')
@ -242,13 +242,18 @@ App::get('/v1/storage/files/:fileId/preview')
->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true) ->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true) ->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true)
->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true)
->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true)
->param('borderRadius', 0, new Range(0, 4000), 'Preview image border radius in pixels. Pass an integer between 0 to 4000.', true)
->param('opacity', 1, new Range(0,1), 'Preview image opacity. Only works with images having an alpha channel (like png). Pass a number between 0 to 1.', true)
->param('rotation', 0, new Range(0,360), 'Preview image rotation in degrees. Pass an integer between 0 and 360.', true)
->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true) ->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true)
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('projectDB') ->inject('projectDB')
->action(function ($fileId, $width, $height, $quality, $background, $output, $request, $response, $project, $projectDB) { ->action(function ($fileId, $width, $height, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $projectDB) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */ /** @var Appwrite\Database\Document $project */
@ -273,7 +278,7 @@ App::get('/v1/storage/files/:fileId/preview')
$fileLogos = Config::getParam('storage-logos'); $fileLogos = Config::getParam('storage-logos');
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5($fileId.$width.$height.$quality.$background.$storage.$output); $key = \md5($fileId.$width.$height.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output);
$file = $projectDB->getDocument($fileId); $file = $projectDB->getDocument($fileId);
@ -293,7 +298,7 @@ App::get('/v1/storage/files/:fileId/preview')
$cipher = null; $cipher = null;
$background = (empty($background)) ? 'eceff1' : $background; $background = (empty($background)) ? 'eceff1' : $background;
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$key = \md5($path.$width.$height.$quality.$background.$storage.$output); $key = \md5($path.$width.$height.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output);
} }
$compressor = new GZIP(); $compressor = new GZIP();
@ -337,11 +342,28 @@ App::get('/v1/storage/files/:fileId/preview')
$image = new Image($source); $image = new Image($source);
$image->crop((int) $width, (int) $height); $image->crop((int) $width, (int) $height);
if (!empty($opacity) || $opacity==0) {
$image->setOpacity($opacity);
}
if (!empty($background)) { if (!empty($background)) {
$image->setBackground('#'.$background); $image->setBackground('#'.$background);
} }
if (!empty($borderWidth) ) {
$image->setBorder($borderWidth, '#'.$borderColor);
}
if (!empty($borderRadius)) {
$image->setBorderRadius($borderRadius);
}
if (!empty($rotation)) {
$image->setRotation($rotation);
}
$output = (empty($output)) ? $type : $output; $output = (empty($output)) ? $type : $output;
$data = $image->output($output, $quality); $data = $image->output($output, $quality);
@ -362,7 +384,7 @@ App::get('/v1/storage/files/:fileId/download')
->desc('Get File for Download') ->desc('Get File for Download')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.read') ->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileDownload') ->label('sdk.method', 'getFileDownload')
->label('sdk.description', '/docs/references/storage/get-file-download.md') ->label('sdk.description', '/docs/references/storage/get-file-download.md')
@ -420,7 +442,7 @@ App::get('/v1/storage/files/:fileId/view')
->desc('Get File for View') ->desc('Get File for View')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.read') ->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileView') ->label('sdk.method', 'getFileView')
->label('sdk.description', '/docs/references/storage/get-file-view.md') ->label('sdk.description', '/docs/references/storage/get-file-view.md')
@ -489,7 +511,7 @@ App::put('/v1/storage/files/:fileId')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.write') ->label('scope', 'files.write')
->label('event', 'storage.files.update') ->label('event', 'storage.files.update')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'updateFile') ->label('sdk.method', 'updateFile')
->label('sdk.description', '/docs/references/storage/update-file.md') ->label('sdk.description', '/docs/references/storage/update-file.md')
@ -538,7 +560,7 @@ App::delete('/v1/storage/files/:fileId')
->groups(['api', 'storage']) ->groups(['api', 'storage'])
->label('scope', 'files.write') ->label('scope', 'files.write')
->label('event', 'storage.files.delete') ->label('event', 'storage.files.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage') ->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteFile') ->label('sdk.method', 'deleteFile')
->label('sdk.description', '/docs/references/storage/delete-file.md') ->label('sdk.description', '/docs/references/storage/delete-file.md')
@ -581,7 +603,7 @@ App::delete('/v1/storage/files/:fileId')
; ;
$events $events
->setParam('payload', $response->output($file, Response::MODEL_FILE)) ->setParam('eventData', $response->output($file, Response::MODEL_FILE))
; ;
$response->noContent(); $response->noContent();
@ -590,8 +612,8 @@ App::delete('/v1/storage/files/:fileId')
// App::get('/v1/storage/files/:fileId/scan') // App::get('/v1/storage/files/:fileId/scan')
// ->desc('Scan Storage') // ->desc('Scan Storage')
// ->groups(['api', 'storage']) // ->groups(['api', 'storage'])
// ->label('scope', 'god') // ->label('scope', 'root')
// ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) // ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
// ->label('sdk.namespace', 'storage') // ->label('sdk.namespace', 'storage')
// ->label('sdk.method', 'getFileScan') // ->label('sdk.method', 'getFileScan')
// ->label('sdk.hide', true) // ->label('sdk.hide', true)

View file

@ -3,9 +3,9 @@
use Utopia\App; use Utopia\App;
use Utopia\Exception; use Utopia\Exception;
use Utopia\Config\Config; use Utopia\Config\Config;
use Utopia\Validator\Email; use Appwrite\Network\Validator\Email;
use Utopia\Validator\Text; use Utopia\Validator\Text;
use Utopia\Validator\Host; use Appwrite\Network\Validator\Host;
use Utopia\Validator\Range; use Utopia\Validator\Range;
use Utopia\Validator\ArrayList; use Utopia\Validator\ArrayList;
use Utopia\Validator\WhiteList; use Utopia\Validator\WhiteList;
@ -25,7 +25,7 @@ App::post('/v1/teams')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('event', 'teams.create') ->label('event', 'teams.create')
->label('scope', 'teams.write') ->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'create') ->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/teams/create-team.md') ->label('sdk.description', '/docs/references/teams/create-team.md')
@ -44,7 +44,7 @@ App::post('/v1/teams')
Authorization::disable(); Authorization::disable();
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles); $isAppUser = Auth::isAppUser(Authorization::$roles);
$team = $projectDB->createDocument([ $team = $projectDB->createDocument([
@ -54,7 +54,7 @@ App::post('/v1/teams')
'write' => ['team:{self}/owner'], 'write' => ['team:{self}/owner'],
], ],
'name' => $name, 'name' => $name,
'sum' => ($isPreviliggedUser || $isAppUser) ? 0 : 1, 'sum' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
'dateCreated' => \time(), 'dateCreated' => \time(),
]); ]);
@ -64,7 +64,7 @@ App::post('/v1/teams')
throw new Exception('Failed saving team to DB', 500); throw new Exception('Failed saving team to DB', 500);
} }
if (!$isPreviliggedUser && !$isAppUser) { // Don't add user on server mode if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode
$membership = new Document([ $membership = new Document([
'$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS, '$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
'$permissions' => [ '$permissions' => [
@ -100,7 +100,7 @@ App::get('/v1/teams')
->desc('List Teams') ->desc('List Teams')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('scope', 'teams.read') ->label('scope', 'teams.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'list') ->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/teams/list-teams.md') ->label('sdk.description', '/docs/references/teams/list-teams.md')
@ -137,7 +137,7 @@ App::get('/v1/teams/:teamId')
->desc('Get Team') ->desc('Get Team')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('scope', 'teams.read') ->label('scope', 'teams.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/teams/get-team.md') ->label('sdk.description', '/docs/references/teams/get-team.md')
@ -165,7 +165,7 @@ App::put('/v1/teams/:teamId')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('event', 'teams.update') ->label('event', 'teams.update')
->label('scope', 'teams.write') ->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'update') ->label('sdk.method', 'update')
->label('sdk.description', '/docs/references/teams/update-team.md') ->label('sdk.description', '/docs/references/teams/update-team.md')
@ -202,7 +202,7 @@ App::delete('/v1/teams/:teamId')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('event', 'teams.delete') ->label('event', 'teams.delete')
->label('scope', 'teams.write') ->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'delete') ->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/teams/delete-team.md') ->label('sdk.description', '/docs/references/teams/delete-team.md')
@ -243,7 +243,7 @@ App::delete('/v1/teams/:teamId')
} }
$events $events
->setParam('payload', $response->output($team, Response::MODEL_TEAM)) ->setParam('eventData', $response->output($team, Response::MODEL_TEAM))
; ;
$response->noContent(); $response->noContent();
@ -251,16 +251,18 @@ App::delete('/v1/teams/:teamId')
App::post('/v1/teams/:teamId/memberships') App::post('/v1/teams/:teamId/memberships')
->desc('Create Team Membership') ->desc('Create Team Membership')
->groups(['api', 'teams']) ->groups(['api', 'teams', 'auth'])
->label('event', 'teams.memberships.create') ->label('event', 'teams.memberships.create')
->label('scope', 'teams.write') ->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('auth.type', 'invites')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'createMembership') ->label('sdk.method', 'createMembership')
->label('sdk.description', '/docs/references/teams/create-team-membership.md') ->label('sdk.description', '/docs/references/teams/create-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP) ->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->label('abuse-limit', 10)
->param('teamId', '', new UID(), 'Team unique ID.') ->param('teamId', '', new UID(), 'Team unique ID.')
->param('email', '', new Email(), 'New team member email.') ->param('email', '', new Email(), 'New team member email.')
->param('name', '', new Text(128), 'New team member name. Max length: 128 chars.', true) ->param('name', '', new Text(128), 'New team member name. Max length: 128 chars.', true)
@ -281,7 +283,7 @@ App::post('/v1/teams/:teamId/memberships')
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $mails */ /** @var Appwrite\Event\Event $mails */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles); $isAppUser = Auth::isAppUser(Authorization::$roles);
$name = (empty($name)) ? $email : $name; $name = (empty($name)) ? $email : $name;
@ -292,7 +294,7 @@ App::post('/v1/teams/:teamId/memberships')
} }
$memberships = $projectDB->getCollection([ $memberships = $projectDB->getCollection([
'limit' => 50, 'limit' => 2000,
'offset' => 0, 'offset' => 0,
'filters' => [ 'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS, '$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,
@ -310,6 +312,22 @@ App::post('/v1/teams/:teamId/memberships')
if (empty($invitee)) { // Create new user if no user with same email found if (empty($invitee)) { // Create new user if no user with same email found
$limit = $project->getAttribute('usersAuthLimit', 0);
if ($limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
}
}
Authorization::disable(); Authorization::disable();
try { try {
@ -323,10 +341,16 @@ App::post('/v1/teams/:teamId/memberships')
'emailVerification' => false, 'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED, 'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash(Auth::passwordGenerator()), 'password' => Auth::passwordHash(Auth::passwordGenerator()),
'passwordUpdate' => \time(), /**
* Set the password update time to 0 for users created using
* team invite and OAuth to allow password updates without an
* old password
*/
'passwordUpdate' => 0,
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'name' => $name, 'name' => $name,
'sessions' => [],
'tokens' => [], 'tokens' => [],
], ['email' => $email]); ], ['email' => $email]);
} catch (Duplicate $th) { } catch (Duplicate $th) {
@ -352,7 +376,7 @@ App::post('/v1/teams/:teamId/memberships')
} }
} }
if (!$isOwner && !$isPreviliggedUser && !$isAppUser) { // Not owner, not admin, not app (server) 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);
} }
@ -368,12 +392,12 @@ App::post('/v1/teams/:teamId/memberships')
'teamId' => $team->getId(), 'teamId' => $team->getId(),
'roles' => $roles, 'roles' => $roles,
'invited' => \time(), 'invited' => \time(),
'joined' => ($isPreviliggedUser || $isAppUser) ? \time() : 0, 'joined' => ($isPrivilegedUser || $isAppUser) ? \time() : 0,
'confirm' => ($isPreviliggedUser || $isAppUser), 'confirm' => ($isPrivilegedUser || $isAppUser),
'secret' => Auth::hash($secret), 'secret' => Auth::hash($secret),
]); ]);
if ($isPreviliggedUser || $isAppUser) { // Allow admin to create membership if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
Authorization::disable(); Authorization::disable();
$membership = $projectDB->createDocument($membership->getArrayCopy()); $membership = $projectDB->createDocument($membership->getArrayCopy());
@ -400,7 +424,7 @@ App::post('/v1/teams/:teamId/memberships')
} }
$url = Template::parseURL($url); $url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['inviteId' => $membership->getId(), 'teamId' => $team->getId(), 'userId' => $invitee->getId(), 'secret' => $secret, 'teamId' => $teamId]); $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['membershipId' => $membership->getId(), 'teamId' => $team->getId(), 'userId' => $invitee->getId(), 'secret' => $secret, 'teamId' => $teamId]);
$url = Template::unParseURL($url); $url = Template::unParseURL($url);
$body = new Template(__DIR__.'/../../config/locale/templates/email-base.tpl'); $body = new Template(__DIR__.'/../../config/locale/templates/email-base.tpl');
@ -425,9 +449,9 @@ App::post('/v1/teams/:teamId/memberships')
->setParam('{{text-cta}}', '#ffffff') ->setParam('{{text-cta}}', '#ffffff')
; ;
if (!$isPreviliggedUser && !$isAppUser) { // No need in comfirmation when in admin or app mode if (!$isPrivilegedUser && !$isAppUser) { // No need of confirmation when in admin or app mode
$mails $mails
->setParam('event', 'teams.membership.create') ->setParam('event', 'teams.memberships.create')
->setParam('from', ($project->getId() === 'console') ? '' : \sprintf($locale->getText('account.emails.team'), $project->getAttribute('name'))) ->setParam('from', ($project->getId() === 'console') ? '' : \sprintf($locale->getText('account.emails.team'), $project->getAttribute('name')))
->setParam('recipient', $email) ->setParam('recipient', $email)
->setParam('name', $name) ->setParam('name', $name)
@ -439,7 +463,7 @@ App::post('/v1/teams/:teamId/memberships')
$audits $audits
->setParam('userId', $invitee->getId()) ->setParam('userId', $invitee->getId())
->setParam('event', 'teams.membership.create') ->setParam('event', 'teams.memberships.create')
->setParam('resource', 'teams/'.$teamId) ->setParam('resource', 'teams/'.$teamId)
; ;
@ -452,11 +476,74 @@ App::post('/v1/teams/:teamId/memberships')
; ;
}); });
App::patch('/v1/teams/:teamId/memberships/:membershipId')
->desc('Update Membership Roles')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.update')
->label('scope', 'teams.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'updateMembershipRoles')
->label('sdk.description', '/docs/references/teams/update-team-membership-roles.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.')
->inject('request')
->inject('response')
->inject('user')
->inject('projectDB')
->inject('audits')
->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $projectDB,$audits) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
$team = $projectDB->getDocument($teamId);
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
throw new Exception('Team not found', 404);
}
$membership = $projectDB->getDocument($membershipId);
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
throw new Exception('Membership not found', 404);
}
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$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);
}
// Update the roles
$membership->setAttribute('roles', $roles);
$membership = $projectDB->updateDocument($membership->getArrayCopy());
if (false === $membership) {
throw new Exception('Failed updating membership', 500);
}
$audits
->setParam('userId', $user->getId())
->setParam('event', 'teams.memberships.update')
->setParam('resource', 'teams/'.$teamId)
;
$response->dynamic(new Document($membership->getArrayCopy()), Response::MODEL_MEMBERSHIP);
});
App::get('/v1/teams/:teamId/memberships') App::get('/v1/teams/:teamId/memberships')
->desc('Get Team Memberships') ->desc('Get Team Memberships')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('scope', 'teams.read') ->label('scope', 'teams.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'getMemberships') ->label('sdk.method', 'getMemberships')
->label('sdk.description', '/docs/references/teams/get-team-members.md') ->label('sdk.description', '/docs/references/teams/get-team-members.md')
@ -505,12 +592,12 @@ App::get('/v1/teams/:teamId/memberships')
$response->dynamic(new Document(['sum' => $projectDB->getSum(), 'memberships' => $users]), Response::MODEL_MEMBERSHIP_LIST); $response->dynamic(new Document(['sum' => $projectDB->getSum(), 'memberships' => $users]), Response::MODEL_MEMBERSHIP_LIST);
}); });
App::patch('/v1/teams/:teamId/memberships/:inviteId/status') App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->desc('Update Team Membership Status') ->desc('Update Team Membership Status')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('event', 'teams.memberships.update.status') ->label('event', 'teams.memberships.update.status')
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'updateMembershipStatus') ->label('sdk.method', 'updateMembershipStatus')
->label('sdk.description', '/docs/references/teams/update-team-membership-status.md') ->label('sdk.description', '/docs/references/teams/update-team-membership-status.md')
@ -518,7 +605,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP) ->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team unique ID.') ->param('teamId', '', new UID(), 'Team unique ID.')
->param('inviteId', '', new UID(), 'Invite unique ID.') ->param('membershipId', '', new UID(), 'Membership ID.')
->param('userId', '', new UID(), 'User unique ID.') ->param('userId', '', new UID(), 'User unique ID.')
->param('secret', '', new Text(256), 'Secret key.') ->param('secret', '', new Text(256), 'Secret key.')
->inject('request') ->inject('request')
@ -527,7 +614,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
->inject('projectDB') ->inject('projectDB')
->inject('geodb') ->inject('geodb')
->inject('audits') ->inject('audits')
->action(function ($teamId, $inviteId, $userId, $secret, $request, $response, $user, $projectDB, $geodb, $audits) { ->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $projectDB, $geodb, $audits) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */ /** @var Appwrite\Database\Document $user */
@ -536,7 +623,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
$protocol = $request->getProtocol(); $protocol = $request->getProtocol();
$membership = $projectDB->getDocument($inviteId); $membership = $projectDB->getDocument($membershipId);
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) { if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
throw new Exception('Invite not found', 404); throw new Exception('Invite not found', 404);
@ -561,7 +648,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
} }
if ($userId != $membership->getAttribute('userId')) { if ($userId != $membership->getAttribute('userId')) {
throw new Exception('Invite not belong to current user ('.$user->getAttribute('email').')', 401); throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401);
} }
if (empty($user->getId())) { if (empty($user->getId())) {
@ -575,7 +662,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
} }
if ($membership->getAttribute('userId') !== $user->getId()) { if ($membership->getAttribute('userId') !== $user->getId()) {
throw new Exception('Invite not belong to current user ('.$user->getAttribute('email').')', 401); throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401);
} }
$membership // Attach user to team $membership // Attach user to team
@ -595,10 +682,11 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$session = new Document(array_merge([ $session = new Document(array_merge([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS, '$collection' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]], '$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]],
'userId' => $user->getId(), 'userId' => $user->getId(),
'type' => Auth::TOKEN_TYPE_LOGIN, 'provider' => Auth::SESSION_PROVIDER_EMAIL,
'providerUid' => $user->getAttribute('email'),
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry, 'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'), 'userAgent' => $request->getUserAgent('UNKNOWN'),
@ -606,7 +694,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
], $detector->getOS(), $detector->getClient(), $detector->getDevice())); ], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
$user->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
Authorization::setRole('user:'.$userId); Authorization::setRole('user:'.$userId);
@ -630,7 +718,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
->setParam('event', 'teams.membership.update') ->setParam('event', 'teams.memberships.update.status')
->setParam('resource', 'teams/'.$teamId) ->setParam('resource', 'teams/'.$teamId)
; ;
@ -651,30 +739,30 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
])), Response::MODEL_MEMBERSHIP); ])), Response::MODEL_MEMBERSHIP);
}); });
App::delete('/v1/teams/:teamId/memberships/:inviteId') App::delete('/v1/teams/:teamId/memberships/:membershipId')
->desc('Delete Team Membership') ->desc('Delete Team Membership')
->groups(['api', 'teams']) ->groups(['api', 'teams'])
->label('event', 'teams.memberships.delete') ->label('event', 'teams.memberships.delete')
->label('scope', 'teams.write') ->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams') ->label('sdk.namespace', 'teams')
->label('sdk.method', 'deleteMembership') ->label('sdk.method', 'deleteMembership')
->label('sdk.description', '/docs/references/teams/delete-team-membership.md') ->label('sdk.description', '/docs/references/teams/delete-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team unique ID.') ->param('teamId', '', new UID(), 'Team unique ID.')
->param('inviteId', '', new UID(), 'Invite unique ID.') ->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response') ->inject('response')
->inject('projectDB') ->inject('projectDB')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->action(function ($teamId, $inviteId, $response, $projectDB, $audits, $events) { ->action(function ($teamId, $membershipId, $response, $projectDB, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
$membership = $projectDB->getDocument($inviteId); $membership = $projectDB->getDocument($membershipId);
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) { if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
throw new Exception('Invite not found', 404); throw new Exception('Invite not found', 404);
@ -706,12 +794,12 @@ App::delete('/v1/teams/:teamId/memberships/:inviteId')
$audits $audits
->setParam('userId', $membership->getAttribute('userId')) ->setParam('userId', $membership->getAttribute('userId'))
->setParam('event', 'teams.membership.delete') ->setParam('event', 'teams.memberships.delete')
->setParam('resource', 'teams/'.$teamId) ->setParam('resource', 'teams/'.$teamId)
; ;
$events $events
->setParam('payload', $response->output($membership, Response::MODEL_MEMBERSHIP)) ->setParam('eventData', $response->output($membership, Response::MODEL_MEMBERSHIP))
; ;
$response->noContent(); $response->noContent();

View file

@ -2,9 +2,10 @@
use Utopia\App; use Utopia\App;
use Utopia\Exception; use Utopia\Exception;
use Utopia\Validator;
use Utopia\Validator\Assoc; use Utopia\Validator\Assoc;
use Utopia\Validator\WhiteList; use Utopia\Validator\WhiteList;
use Utopia\Validator\Email; use Appwrite\Network\Validator\Email;
use Utopia\Validator\Text; use Utopia\Validator\Text;
use Utopia\Validator\Range; use Utopia\Validator\Range;
use Utopia\Audit\Audit; use Utopia\Audit\Audit;
@ -23,7 +24,7 @@ App::post('/v1/users')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('event', 'users.create') ->label('event', 'users.create')
->label('scope', 'users.write') ->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'create') ->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/users/create-user.md') ->label('sdk.description', '/docs/references/users/create-user.md')
@ -81,7 +82,7 @@ App::get('/v1/users')
->desc('List Users') ->desc('List Users')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('scope', 'users.read') ->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'list') ->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/users/list-users.md') ->label('sdk.description', '/docs/references/users/list-users.md')
@ -118,7 +119,7 @@ App::get('/v1/users/:userId')
->desc('Get User') ->desc('Get User')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('scope', 'users.read') ->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/users/get-user.md') ->label('sdk.description', '/docs/references/users/get-user.md')
@ -145,13 +146,13 @@ App::get('/v1/users/:userId/prefs')
->desc('Get User Preferences') ->desc('Get User Preferences')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('scope', 'users.read') ->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'getPrefs') ->label('sdk.method', 'getPrefs')
->label('sdk.description', '/docs/references/users/get-user-prefs.md') ->label('sdk.description', '/docs/references/users/get-user-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.response.model', Response::MODEL_PREFERENCES)
->param('userId', '', new UID(), 'User unique ID.') ->param('userId', '', new UID(), 'User unique ID.')
->inject('response') ->inject('response')
->inject('projectDB') ->inject('projectDB')
@ -167,14 +168,14 @@ App::get('/v1/users/:userId/prefs')
$prefs = $user->getAttribute('prefs', new \stdClass()); $prefs = $user->getAttribute('prefs', new \stdClass());
$response->dynamic(new Document($prefs), Response::MODEL_ANY); $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
}); });
App::get('/v1/users/:userId/sessions') App::get('/v1/users/:userId/sessions')
->desc('Get User Sessions') ->desc('Get User Sessions')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('scope', 'users.read') ->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'getSessions') ->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/users/get-user-sessions.md') ->label('sdk.description', '/docs/references/users/get-user-sessions.md')
@ -196,21 +197,18 @@ App::get('/v1/users/:userId/sessions')
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$tokens = $user->getAttribute('tokens', []); $sessions = $user->getAttribute('sessions', []);
$sessions = [];
$countries = $locale->getText('countries'); $countries = $locale->getText('countries');
foreach ($tokens as $token) { /* @var $token Document */ foreach ($sessions as $key => $session) {
if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { /** @var Document $session */
continue;
}
$token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('countryCode'))])) $session->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))]))
? $countries[strtoupper($token->getAttribute('countryCode'))] ? $countries[strtoupper($session->getAttribute('countryCode'))]
: $locale->getText('locale.country.unknown')); : $locale->getText('locale.country.unknown'));
$token->setAttribute('current', false); $session->setAttribute('current', false);
$sessions[] = $token; $sessions[$key] = $session;
} }
$response->dynamic(new Document([ $response->dynamic(new Document([
@ -223,7 +221,7 @@ App::get('/v1/users/:userId/logs')
->desc('Get User Logs') ->desc('Get User Logs')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('scope', 'users.read') ->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'getLogs') ->label('sdk.method', 'getLogs')
->label('sdk.description', '/docs/references/users/get-user-logs.md') ->label('sdk.description', '/docs/references/users/get-user-logs.md')
@ -338,7 +336,7 @@ App::patch('/v1/users/:userId/status')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('event', 'users.update.status') ->label('event', 'users.update.status')
->label('scope', 'users.write') ->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'updateStatus') ->label('sdk.method', 'updateStatus')
->label('sdk.description', '/docs/references/users/update-user-status.md') ->label('sdk.description', '/docs/references/users/update-user-status.md')
@ -346,7 +344,7 @@ App::patch('/v1/users/:userId/status')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER) ->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User unique ID.') ->param('userId', '', new UID(), 'User unique ID.')
->param('status', '', new WhiteList([Auth::USER_STATUS_ACTIVATED, Auth::USER_STATUS_BLOCKED, Auth::USER_STATUS_UNACTIVATED], true), 'User Status code. To activate the user pass '.Auth::USER_STATUS_ACTIVATED.', to block the user pass '.Auth::USER_STATUS_BLOCKED.' and for disabling the user pass '.Auth::USER_STATUS_UNACTIVATED) ->param('status', '', new WhiteList([Auth::USER_STATUS_ACTIVATED, Auth::USER_STATUS_BLOCKED, Auth::USER_STATUS_UNACTIVATED], true, Validator::TYPE_INTEGER), 'User Status code. To activate the user pass '.Auth::USER_STATUS_ACTIVATED.', to block the user pass '.Auth::USER_STATUS_BLOCKED.' and for disabling the user pass '.Auth::USER_STATUS_UNACTIVATED)
->inject('response') ->inject('response')
->inject('projectDB') ->inject('projectDB')
->action(function ($userId, $status, $response, $projectDB) { ->action(function ($userId, $status, $response, $projectDB) {
@ -375,13 +373,13 @@ App::patch('/v1/users/:userId/prefs')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('event', 'users.update.prefs') ->label('event', 'users.update.prefs')
->label('scope', 'users.write') ->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'updatePrefs') ->label('sdk.method', 'updatePrefs')
->label('sdk.description', '/docs/references/users/update-user-prefs.md') ->label('sdk.description', '/docs/references/users/update-user-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.response.model', Response::MODEL_PREFERENCES)
->param('userId', '', new UID(), 'User unique ID.') ->param('userId', '', new UID(), 'User unique ID.')
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.') ->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->inject('response') ->inject('response')
@ -404,7 +402,7 @@ App::patch('/v1/users/:userId/prefs')
throw new Exception('Failed saving user to DB', 500); throw new Exception('Failed saving user to DB', 500);
} }
$response->dynamic(new Document($prefs), Response::MODEL_ANY); $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
}); });
App::delete('/v1/users/:userId/sessions/:sessionId') App::delete('/v1/users/:userId/sessions/:sessionId')
@ -412,7 +410,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('event', 'users.sessions.delete') ->label('event', 'users.sessions.delete')
->label('scope', 'users.write') ->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'deleteSession') ->label('sdk.method', 'deleteSession')
->label('sdk.description', '/docs/references/users/delete-user-session.md') ->label('sdk.description', '/docs/references/users/delete-user-session.md')
@ -434,16 +432,18 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$tokens = $user->getAttribute('tokens', []); $sessions = $user->getAttribute('sessions', []);
foreach ($tokens as $token) { /* @var $token Document */ foreach ($sessions as $session) {
if ($sessionId == $token->getId()) { /** @var Document $session */
if (!$projectDB->deleteDocument($token->getId())) {
if ($sessionId == $session->getId()) {
if (!$projectDB->deleteDocument($session->getId())) {
throw new Exception('Failed to remove token from DB', 500); throw new Exception('Failed to remove token from DB', 500);
} }
$events $events
->setParam('payload', $response->output($user, Response::MODEL_USER)) ->setParam('eventData', $response->output($user, Response::MODEL_USER))
; ;
} }
} }
@ -457,7 +457,7 @@ App::delete('/v1/users/:userId/sessions')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('event', 'users.sessions.delete') ->label('event', 'users.sessions.delete')
->label('scope', 'users.write') ->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'deleteSessions') ->label('sdk.method', 'deleteSessions')
->label('sdk.description', '/docs/references/users/delete-user-sessions.md') ->label('sdk.description', '/docs/references/users/delete-user-sessions.md')
@ -478,16 +478,18 @@ App::delete('/v1/users/:userId/sessions')
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$tokens = $user->getAttribute('tokens', []); $sessions = $user->getAttribute('sessions', []);
foreach ($tokens as $token) { /* @var $token Document */ foreach ($sessions as $session) {
if (!$projectDB->deleteDocument($token->getId())) { /** @var Document $session */
if (!$projectDB->deleteDocument($session->getId())) {
throw new Exception('Failed to remove token from DB', 500); throw new Exception('Failed to remove token from DB', 500);
} }
} }
$events $events
->setParam('payload', $response->output($user, Response::MODEL_USER)) ->setParam('eventData', $response->output($user, Response::MODEL_USER))
; ;
// TODO : Response filter implementation // TODO : Response filter implementation
@ -499,10 +501,10 @@ App::delete('/v1/users/:userId')
->groups(['api', 'users']) ->groups(['api', 'users'])
->label('event', 'users.delete') ->label('event', 'users.delete')
->label('scope', 'users.write') ->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users') ->label('sdk.namespace', 'users')
->label('sdk.method', 'deleteUser') ->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/users/delete-user.md') ->label('sdk.description', '/docs/references/users/delete.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', function () {return new UID();}, 'User unique ID.') ->param('userId', '', function () {return new UID();}, 'User unique ID.')
@ -547,7 +549,7 @@ App::delete('/v1/users/:userId')
; ;
$events $events
->setParam('payload', $response->output($user, Response::MODEL_USER)) ->setParam('eventData', $response->output($user, Response::MODEL_USER))
; ;
// TODO : Response filter implementation // TODO : Response filter implementation

View file

@ -14,24 +14,69 @@ use Appwrite\Database\Database;
use Appwrite\Database\Document; use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization; use Appwrite\Database\Validator\Authorization;
use Appwrite\Network\Validator\Origin; use Appwrite\Network\Validator\Origin;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Appwrite\Utopia\Response\Filters\V06; use Appwrite\Utopia\Response\Filters\V06;
use Appwrite\Utopia\Response\Filters\V07;
use Utopia\CLI\Console; use Utopia\CLI\Console;
Config::setParam('domainVerification', false); Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost'); Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $clients) { App::init(function ($utopia, $request, $response, $console, $project, $consoleDB, $user, $locale, $clients) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Database\Document $console */ /** @var Appwrite\Database\Document $console */
/** @var Appwrite\Database\Document $project */ /** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */ /** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var bool $mode */ /** @var bool $mode */
/** @var array $clients */ /** @var array $clients */
$domain = $request->getHostname();
$domains = Config::getParam('domains', []);
if (!array_key_exists($domain, $domains)) {
$domain = new Domain(!empty($domain) ? $domain : '');
if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) {
$domains[$domain->get()] = false;
Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.');
} else {
Authorization::disable();
$dbDomain = $consoleDB->getCollectionFirst([
'limit' => 1,
'offset' => 0,
'filters' => [
'$collection=' . Database::SYSTEM_COLLECTION_CERTIFICATES,
'domain=' . $domain->get(),
],
]);
if (empty($dbDomain)) {
$dbDomain = [
'$collection' => Database::SYSTEM_COLLECTION_CERTIFICATES,
'$permissions' => [
'read' => [],
'write' => [],
],
'domain' => $domain->get(),
];
$dbDomain = $consoleDB->createDocument($dbDomain);
Authorization::enable();
Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...'); // TODO move this to installation script
Resque::enqueue('v1-certificates', 'CertificatesV1', [
'document' => $dbDomain,
'domain' => $domain->get(),
'validateTarget' => false,
'validateCNAME' => false,
]);
}
$domains[$domain->get()] = true;
}
Config::setParam('domains', $domains);
}
$localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')); $localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
@ -41,7 +86,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
$route = $utopia->match($request); $route = $utopia->match($request);
if (!empty($route->getLabel('sdk.platform', [])) && empty($project->getId()) && ($route->getLabel('scope', '') !== 'public')) { if (!empty($route->getLabel('sdk.auth', [])) && empty($project->getId()) && ($route->getLabel('scope', '') !== 'public')) {
throw new Exception('Missing or unknown project ID', 400); throw new Exception('Missing or unknown project ID', 400);
} }
@ -92,6 +137,9 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
case version_compare ($responseFormat , '0.6.2', '<=') : case version_compare ($responseFormat , '0.6.2', '<=') :
Response::setFilter(new V06()); Response::setFilter(new V06());
break; break;
case version_compare ($responseFormat , '0.7.2', '<=') :
Response::setFilter(new V07());
break;
default: default:
Response::setFilter(null); Response::setFilter(null);
} }
@ -208,7 +256,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
} }
}, $user->getAttribute('memberships', [])); }, $user->getAttribute('memberships', []));
// TDOO Check if user is god // TDOO Check if user is root
if (!\in_array($scope, $scopes)) { if (!\in_array($scope, $scopes)) {
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS !== $project->getCollection()) { // Check if permission is denied because project is missing if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS !== $project->getCollection()) { // Check if permission is denied because project is missing
@ -226,7 +274,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
throw new Exception('Password reset is required', 412); throw new Exception('Password reset is required', 412);
} }
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'clients']); }, ['utopia', 'request', 'response', 'console', 'project', 'consoleDB', 'user', 'locale', 'clients']);
App::options(function ($request, $response) { App::options(function ($request, $response) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
@ -271,7 +319,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN'); $version = App::getEnv('_APP_VERSION', 'UNKNOWN');
switch ($error->getCode()) { switch ($error->getCode()) { // Don't show 500 errors!
case 400: // Error allowed publicly case 400: // Error allowed publicly
case 401: // Error allowed publicly case 401: // Error allowed publicly
case 402: // Error allowed publicly case 402: // Error allowed publicly
@ -280,6 +328,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
case 409: // Error allowed publicly case 409: // Error allowed publicly
case 412: // Error allowed publicly case 412: // Error allowed publicly
case 429: // Error allowed publicly case 429: // Error allowed publicly
case 501: // Error allowed publicly
$code = $error->getCode(); $code = $error->getCode();
$message = $error->getMessage(); $message = $error->getMessage();
break; break;
@ -423,4 +472,4 @@ include_once __DIR__ . '/shared/web.php';
foreach (Config::getParam('services', []) as $service) { foreach (Config::getParam('services', []) as $service) {
include_once $service['controller']; include_once $service['controller'];
} }

View file

@ -3,19 +3,19 @@
global $utopia, $request, $response; global $utopia, $request, $response;
use Appwrite\Database\Document; use Appwrite\Database\Document;
use Appwrite\Network\Validator\Host;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use Utopia\App; use Utopia\App;
use Utopia\Validator\Numeric; use Utopia\Validator\Numeric;
use Utopia\Validator\Text; use Utopia\Validator\Text;
use Utopia\Validator\ArrayList; use Utopia\Validator\ArrayList;
use Utopia\Validator\Host;
use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\File;
App::get('/v1/mock/tests/foo') App::get('/v1/mock/tests/foo')
->desc('Get Foo') ->desc('Get Foo')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'foo') ->label('sdk.namespace', 'foo')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', 'Mock a get request.') ->label('sdk.description', 'Mock a get request.')
@ -33,7 +33,7 @@ App::post('/v1/mock/tests/foo')
->desc('Post Foo') ->desc('Post Foo')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'foo') ->label('sdk.namespace', 'foo')
->label('sdk.method', 'post') ->label('sdk.method', 'post')
->label('sdk.description', 'Mock a post request.') ->label('sdk.description', 'Mock a post request.')
@ -51,7 +51,7 @@ App::patch('/v1/mock/tests/foo')
->desc('Patch Foo') ->desc('Patch Foo')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'foo') ->label('sdk.namespace', 'foo')
->label('sdk.method', 'patch') ->label('sdk.method', 'patch')
->label('sdk.description', 'Mock a patch request.') ->label('sdk.description', 'Mock a patch request.')
@ -69,7 +69,7 @@ App::put('/v1/mock/tests/foo')
->desc('Put Foo') ->desc('Put Foo')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'foo') ->label('sdk.namespace', 'foo')
->label('sdk.method', 'put') ->label('sdk.method', 'put')
->label('sdk.description', 'Mock a put request.') ->label('sdk.description', 'Mock a put request.')
@ -87,7 +87,7 @@ App::delete('/v1/mock/tests/foo')
->desc('Delete Foo') ->desc('Delete Foo')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'foo') ->label('sdk.namespace', 'foo')
->label('sdk.method', 'delete') ->label('sdk.method', 'delete')
->label('sdk.description', 'Mock a delete request.') ->label('sdk.description', 'Mock a delete request.')
@ -105,7 +105,7 @@ App::get('/v1/mock/tests/bar')
->desc('Get Bar') ->desc('Get Bar')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'bar') ->label('sdk.namespace', 'bar')
->label('sdk.method', 'get') ->label('sdk.method', 'get')
->label('sdk.description', 'Mock a get request.') ->label('sdk.description', 'Mock a get request.')
@ -123,7 +123,7 @@ App::post('/v1/mock/tests/bar')
->desc('Post Bar') ->desc('Post Bar')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'bar') ->label('sdk.namespace', 'bar')
->label('sdk.method', 'post') ->label('sdk.method', 'post')
->label('sdk.description', 'Mock a post request.') ->label('sdk.description', 'Mock a post request.')
@ -141,7 +141,7 @@ App::patch('/v1/mock/tests/bar')
->desc('Patch Bar') ->desc('Patch Bar')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'bar') ->label('sdk.namespace', 'bar')
->label('sdk.method', 'patch') ->label('sdk.method', 'patch')
->label('sdk.description', 'Mock a patch request.') ->label('sdk.description', 'Mock a patch request.')
@ -159,7 +159,7 @@ App::put('/v1/mock/tests/bar')
->desc('Put Bar') ->desc('Put Bar')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'bar') ->label('sdk.namespace', 'bar')
->label('sdk.method', 'put') ->label('sdk.method', 'put')
->label('sdk.description', 'Mock a put request.') ->label('sdk.description', 'Mock a put request.')
@ -177,7 +177,7 @@ App::delete('/v1/mock/tests/bar')
->desc('Delete Bar') ->desc('Delete Bar')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'bar') ->label('sdk.namespace', 'bar')
->label('sdk.method', 'delete') ->label('sdk.method', 'delete')
->label('sdk.description', 'Mock a delete request.') ->label('sdk.description', 'Mock a delete request.')
@ -195,7 +195,7 @@ App::post('/v1/mock/tests/general/upload')
->desc('Upload File') ->desc('Upload File')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'upload') ->label('sdk.method', 'upload')
->label('sdk.description', 'Mock a file upload request.') ->label('sdk.description', 'Mock a file upload request.')
@ -240,7 +240,7 @@ App::get('/v1/mock/tests/general/redirect')
->desc('Redirect') ->desc('Redirect')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'redirect') ->label('sdk.method', 'redirect')
->label('sdk.description', 'Mock a redirect request.') ->label('sdk.description', 'Mock a redirect request.')
@ -258,7 +258,7 @@ App::get('/v1/mock/tests/general/redirect/done')
->desc('Redirected') ->desc('Redirected')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'redirected') ->label('sdk.method', 'redirected')
->label('sdk.description', 'Mock a redirected request.') ->label('sdk.description', 'Mock a redirected request.')
@ -273,7 +273,7 @@ App::get('/v1/mock/tests/general/set-cookie')
->desc('Set Cookie') ->desc('Set Cookie')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'setCookie') ->label('sdk.method', 'setCookie')
->label('sdk.description', 'Mock a set cookie request.') ->label('sdk.description', 'Mock a set cookie request.')
@ -292,7 +292,7 @@ App::get('/v1/mock/tests/general/get-cookie')
->desc('Get Cookie') ->desc('Get Cookie')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'getCookie') ->label('sdk.method', 'getCookie')
->label('sdk.description', 'Mock a cookie response.') ->label('sdk.description', 'Mock a cookie response.')
@ -313,7 +313,7 @@ App::get('/v1/mock/tests/general/empty')
->desc('Empty Response') ->desc('Empty Response')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'empty') ->label('sdk.method', 'empty')
->label('sdk.description', 'Mock an empty response.') ->label('sdk.description', 'Mock an empty response.')
@ -331,7 +331,7 @@ App::get('/v1/mock/tests/general/400-error')
->desc('400 Error') ->desc('400 Error')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'error400') ->label('sdk.method', 'error400')
->label('sdk.description', 'Mock a 400 failed request.') ->label('sdk.description', 'Mock a 400 failed request.')
@ -347,7 +347,7 @@ App::get('/v1/mock/tests/general/500-error')
->desc('500 Error') ->desc('500 Error')
->groups(['mock']) ->groups(['mock'])
->label('scope', 'public') ->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general') ->label('sdk.namespace', 'general')
->label('sdk.method', 'error500') ->label('sdk.method', 'error500')
->label('sdk.description', 'Mock a 500 failed request.') ->label('sdk.description', 'Mock a 500 failed request.')
@ -391,7 +391,7 @@ App::get('/v1/mock/tests/general/oauth2')
->param('scope', '', new Text(100), 'OAuth2 scope list.') ->param('scope', '', new Text(100), 'OAuth2 scope list.')
->param('state', '', new Text(1024), 'OAuth2 state.') ->param('state', '', new Text(1024), 'OAuth2 state.')
->inject('response') ->inject('response')
->action(function ($clientId, $redirectURI, $scope, $state, $response) { ->action(function ($client_id, $redirectURI, $scope, $state, $response) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
$response->redirect($redirectURI.'?'.\http_build_query(['code' => 'abcdef', 'state' => $state])); $response->redirect($redirectURI.'?'.\http_build_query(['code' => 'abcdef', 'state' => $state]));
@ -408,14 +408,14 @@ App::get('/v1/mock/tests/general/oauth2/token')
->param('client_secret', '', new Text(100), 'OAuth2 scope list.') ->param('client_secret', '', new Text(100), 'OAuth2 scope list.')
->param('code', '', new Text(100), 'OAuth2 state.') ->param('code', '', new Text(100), 'OAuth2 state.')
->inject('response') ->inject('response')
->action(function ($clientId, $redirectURI, $clientSecret, $code, $response) { ->action(function ($client_id, $redirectURI, $client_secret, $code, $response) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
if ($clientId != '1') { if ($client_id != '1') {
throw new Exception('Invalid client ID'); throw new Exception('Invalid client ID');
} }
if ($clientSecret != '123456') { if ($client_secret != '123456') {
throw new Exception('Invalid client secret'); throw new Exception('Invalid client secret');
} }

View file

@ -61,12 +61,12 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
; ;
} }
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles); $isAppUser = Auth::isAppUser(Authorization::$roles);
if (($abuse->check() // Route is rate-limited if (($abuse->check() // Route is rate-limited
&& App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not diabled && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not diabled
&& (!$isAppUser && !$isPreviliggedUser)) // User is not an admin or API key && (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
{ {
throw new Exception('Too many requests', 429); throw new Exception('Too many requests', 429);
} }
@ -76,9 +76,10 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
*/ */
$events $events
->setParam('projectId', $project->getId()) ->setParam('projectId', $project->getId())
->setParam('webhooks', $project->getAttribute('webhooks', []))
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
->setParam('event', $route->getLabel('event', '')) ->setParam('event', $route->getLabel('event', ''))
->setParam('payload', []) ->setParam('eventData', [])
->setParam('functionId', null) ->setParam('functionId', null)
->setParam('executionId', null) ->setParam('executionId', null)
->setParam('trigger', 'event') ->setParam('trigger', 'event')
@ -110,6 +111,61 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes'], 'api'); }, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes'], 'api');
App::init(function ($utopia, $request, $response, $project, $user) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Registry\Registry $register */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $functions */
$route = $utopia->match($request);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
if($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
return;
}
switch ($route->getLabel('auth.type', '')) {
case 'emailPassword':
if($project->getAttribute('usersAuthEmailPassword', true) === false) {
throw new Exception('Email / Password authentication is disabled for this project', 501);
}
break;
case 'anonymous':
if($project->getAttribute('usersAuthAnonymous', true) === false) {
throw new Exception('Anonymous authentication is disabled for this project', 501);
}
break;
case 'invites':
if($project->getAttribute('usersAuthInvites', true) === false) {
throw new Exception('Invites authentication is disabled for this project', 501);
}
break;
case 'jwt':
if($project->getAttribute('usersAuthJWT', true) === false) {
throw new Exception('JWT authentication is disabled for this project', 501);
}
break;
default:
throw new Exception('Unsupported authentication route');
break;
}
}, ['utopia', 'request', 'response', 'project', 'user'], 'auth');
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $mode) { App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $mode) {
/** @var Utopia\App $utopia */ /** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
@ -123,8 +179,8 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
/** @var bool $mode */ /** @var bool $mode */
if (!empty($events->getParam('event'))) { if (!empty($events->getParam('event'))) {
if(empty($events->getParam('payload'))) { if(empty($events->getParam('eventData'))) {
$events->setParam('payload', $response->getPayload()); $events->setParam('eventData', $response->getPayload());
} }
$webhooks = clone $events; $webhooks = clone $events;

View file

@ -32,7 +32,7 @@ App::init(function ($utopia, $request, $response, $layout) {
['type' => 'developer', 'label' => 'Developer'], ['type' => 'developer', 'label' => 'Developer'],
['type' => 'admin', 'label' => 'Admin'], ['type' => 'admin', 'label' => 'Admin'],
]) ])
->setParam('environments', Config::getParam('environments')) ->setParam('runtimes', Config::getParam('runtimes'))
->setParam('mode', App::getMode()) ->setParam('mode', App::getMode())
; ;

View file

@ -323,7 +323,10 @@ App::get('/console/users')
$page = new View(__DIR__.'/../../views/console/users/index.phtml'); $page = new View(__DIR__.'/../../views/console/users/index.phtml');
$page->setParam('providers', Config::getParam('providers')); $page
->setParam('auth', Config::getParam('auth'))
->setParam('providers', Config::getParam('providers'))
;
$layout $layout
->setParam('title', APP_NAME.' - Users') ->setParam('title', APP_NAME.' - Users')
@ -370,7 +373,7 @@ App::get('/console/functions')
$page = new View(__DIR__.'/../../views/console/functions/index.phtml'); $page = new View(__DIR__.'/../../views/console/functions/index.phtml');
$page $page
->setParam('environments', Config::getParam('environments')) ->setParam('runtimes', Config::getParam('runtimes'))
; ;
$layout $layout

View file

@ -1,5 +1,6 @@
<?php <?php
use Appwrite\Database\Database;
use Appwrite\Specification\Format\OpenAPI3; use Appwrite\Specification\Format\OpenAPI3;
use Appwrite\Specification\Format\Swagger2; use Appwrite\Specification\Format\Swagger2;
use Appwrite\Specification\Specification; use Appwrite\Specification\Specification;
@ -42,10 +43,38 @@ App::get('/')
->label('permission', 'public') ->label('permission', 'public')
->label('scope', 'home') ->label('scope', 'home')
->inject('response') ->inject('response')
->action(function ($response) { ->inject('consoleDB')
->inject('project')
->action(function ($response, $consoleDB, $project) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Database\Document $project */
$response->redirect('/auth/signin'); $response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Expires', 0)
->addHeader('Pragma', 'no-cache')
;
if ('console' === $project->getId() || $project->isEmpty()) {
$whitlistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled');
if($whitlistRoot !== 'disabled') {
$consoleDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $consoleDB->getSum();
if($sum !== 0) {
return $response->redirect('/auth/signin');
}
}
}
$response->redirect('/auth/signup');
}); });
App::get('/auth/signin') App::get('/auth/signin')
@ -58,6 +87,10 @@ App::get('/auth/signin')
$page = new View(__DIR__.'/../../views/home/auth/signin.phtml'); $page = new View(__DIR__.'/../../views/home/auth/signin.phtml');
$page
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
;
$layout $layout
->setParam('title', 'Sign In - '.APP_NAME) ->setParam('title', 'Sign In - '.APP_NAME)
->setParam('body', $page); ->setParam('body', $page);
@ -72,6 +105,10 @@ App::get('/auth/signup')
/** @var Utopia\View $layout */ /** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/signup.phtml'); $page = new View(__DIR__.'/../../views/home/auth/signup.phtml');
$page
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
;
$layout $layout
->setParam('title', 'Sign Up - '.APP_NAME) ->setParam('title', 'Sign Up - '.APP_NAME)
->setParam('body', $page); ->setParam('body', $page);
@ -87,6 +124,10 @@ App::get('/auth/recovery')
$page = new View(__DIR__.'/../../views/home/auth/recovery.phtml'); $page = new View(__DIR__.'/../../views/home/auth/recovery.phtml');
$page
->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST'))))
;
$layout $layout
->setParam('title', 'Password Recovery - '.APP_NAME) ->setParam('title', 'Password Recovery - '.APP_NAME)
->setParam('body', $page); ->setParam('body', $page);
@ -214,6 +255,12 @@ App::get('/specs/:format')
'console' => APP_PLATFORM_CONSOLE, 'console' => APP_PLATFORM_CONSOLE,
]; ];
$authCounts = [
'client' => 1,
'server' => 2,
'console' => 1,
];
$routes = []; $routes = [];
$models = []; $models = [];
$services = []; $services = [];
@ -226,6 +273,12 @@ App::get('/specs/:format')
'description' => 'Your project ID', 'description' => 'Your project ID',
'in' => 'header', 'in' => 'header',
], ],
'JWT' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-JWT',
'description' => 'Your secret JSON Web Token',
'in' => 'header',
],
'Locale' => [ 'Locale' => [
'type' => 'apiKey', 'type' => 'apiKey',
'name' => 'X-Appwrite-Locale', 'name' => 'X-Appwrite-Locale',
@ -246,6 +299,12 @@ App::get('/specs/:format')
'description' => 'Your secret API key', 'description' => 'Your secret API key',
'in' => 'header', 'in' => 'header',
], ],
'JWT' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-JWT',
'description' => 'Your secret JSON Web Token',
'in' => 'header',
],
'Locale' => [ 'Locale' => [
'type' => 'apiKey', 'type' => 'apiKey',
'name' => 'X-Appwrite-Locale', 'name' => 'X-Appwrite-Locale',
@ -266,6 +325,12 @@ App::get('/specs/:format')
'description' => 'Your secret API key', 'description' => 'Your secret API key',
'in' => 'header', 'in' => 'header',
], ],
'JWT' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-JWT',
'description' => 'Your secret JSON Web Token',
'in' => 'header',
],
'Locale' => [ 'Locale' => [
'type' => 'apiKey', 'type' => 'apiKey',
'name' => 'X-Appwrite-Locale', 'name' => 'X-Appwrite-Locale',
@ -281,14 +346,32 @@ App::get('/specs/:format')
], ],
]; ];
$security = [
APP_PLATFORM_CLIENT => ['Project' => []],
APP_PLATFORM_SERVER => ['Project' => [], 'Key' => []],
APP_PLATFORM_CONSOLE => ['Project' => [], 'Key' => []],
];
foreach ($utopia->getRoutes() as $key => $method) { foreach ($utopia->getRoutes() as $key => $method) {
foreach ($method as $route) { /** @var \Utopia\Route $route */ foreach ($method as $route) { /** @var \Utopia\Route $route */
$routeSecurity = $route->getLabel('sdk.auth', []);
$sdkPlatofrms = [];
foreach ($routeSecurity as $value) {
switch ($value) {
case APP_AUTH_TYPE_SESSION:
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
break;
case APP_AUTH_TYPE_KEY:
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
break;
case APP_AUTH_TYPE_JWT:
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
break;
case APP_AUTH_TYPE_ADMIN:
$sdkPlatofrms[] = APP_PLATFORM_CONSOLE;
break;
}
}
if(empty($routeSecurity)) {
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
}
if (!$route->getLabel('docs', true)) { if (!$route->getLabel('docs', true)) {
continue; continue;
} }
@ -305,7 +388,7 @@ App::get('/specs/:format')
continue; continue;
} }
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $route->getLabel('sdk.platform', []))) { if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatofrms)) {
continue; continue;
} }
@ -342,11 +425,11 @@ App::get('/specs/:format')
switch ($format) { switch ($format) {
case 'swagger2': case 'swagger2':
$format = new Swagger2($utopia, $services, $routes, $models, $keys[$platform], $security[$platform]); $format = new Swagger2($utopia, $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0);
break; break;
case 'open-api3': case 'open-api3':
$format = new OpenAPI3($utopia, $services, $routes, $models, $keys[$platform], $security[$platform]); $format = new OpenAPI3($utopia, $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0);
break; break;
default: default:

View file

@ -12,6 +12,8 @@ use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse; use Swoole\Http\Response as SwooleResponse;
use Utopia\App; use Utopia\App;
use Utopia\CLI\Console; use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
// xdebug_start_trace('/tmp/trace'); // xdebug_start_trace('/tmp/trace');
@ -65,18 +67,6 @@ Files::load(__DIR__ . '/../public');
include __DIR__ . '/controllers/general.php'; include __DIR__ . '/controllers/general.php';
$domain = App::getEnv('_APP_DOMAIN', '');
Console::info('Issuing a TLS certificate for the master domain ('.$domain.') in 30 seconds.
Make sure your domain points to your server IP or restart your Appwrite server to try again.'); // TODO move this to installation script
ResqueScheduler::enqueueAt(\time() + 30, 'v1-certificates', 'CertificatesV1', [
'document' => [],
'domain' => $domain,
'validateTarget' => false,
'validateCNAME' => false,
]);
$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) { $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) {
$request = new Request($swooleRequest); $request = new Request($swooleRequest);
$response = new Response($swooleResponse); $response = new Response($swooleResponse);

View file

@ -39,8 +39,8 @@ const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
const APP_MODE_DEFAULT = 'default'; const APP_MODE_DEFAULT = 'default';
const APP_MODE_ADMIN = 'admin'; const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12; const APP_PAGING_LIMIT = 12;
const APP_CACHE_BUSTER = 145; const APP_CACHE_BUSTER = 146;
const APP_VERSION_STABLE = '0.7.2'; const APP_VERSION_STABLE = '0.8.0';
const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_FUNCTIONS = '/storage/functions';
const APP_STORAGE_CACHE = '/storage/cache'; const APP_STORAGE_CACHE = '/storage/cache';
@ -62,6 +62,11 @@ const DELETE_TYPE_EXECUTIONS = 'executions';
const DELETE_TYPE_AUDIT = 'audit'; const DELETE_TYPE_AUDIT = 'audit';
const DELETE_TYPE_ABUSE = 'abuse'; const DELETE_TYPE_ABUSE = 'abuse';
const DELETE_TYPE_CERTIFICATES = 'certificates'; const DELETE_TYPE_CERTIFICATES = 'certificates';
// Auth Types
const APP_AUTH_TYPE_SESSION = 'Session';
const APP_AUTH_TYPE_JWT = 'JWT';
const APP_AUTH_TYPE_KEY = 'Key';
const APP_AUTH_TYPE_ADMIN = 'Admin';
$register = new Registry(); $register = new Registry();
@ -71,10 +76,11 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
* ENV vars * ENV vars
*/ */
Config::load('events', __DIR__.'/config/events.php'); Config::load('events', __DIR__.'/config/events.php');
Config::load('auth', __DIR__.'/config/auth.php');
Config::load('providers', __DIR__.'/config/providers.php'); Config::load('providers', __DIR__.'/config/providers.php');
Config::load('platforms', __DIR__.'/config/platforms.php'); Config::load('platforms', __DIR__.'/config/platforms.php');
Config::load('collections', __DIR__.'/config/collections.php'); Config::load('collections', __DIR__.'/config/collections.php');
Config::load('environments', __DIR__.'/config/environments.php'); Config::load('runtimes', __DIR__.'/config/runtimes.php');
Config::load('roles', __DIR__.'/config/roles.php'); // User roles and scopes Config::load('roles', __DIR__.'/config/roles.php'); // User roles and scopes
Config::load('scopes', __DIR__.'/config/scopes.php'); // User roles and scopes Config::load('scopes', __DIR__.'/config/scopes.php'); // User roles and scopes
Config::load('services', __DIR__.'/config/services.php'); // List of services Config::load('services', __DIR__.'/config/services.php'); // List of services
@ -164,8 +170,9 @@ $register->set('influxdb', function () { // Register DB connection
if (empty($host) || empty($port)) { if (empty($host) || empty($port)) {
return; return;
} }
$driver = new InfluxDB\Driver\Curl("http://{$host}:{$port}");
$client = new InfluxDB\Client($host, $port, '', '', false, false, 5); $client = new InfluxDB\Client($host, $port, '', '', false, false, 5);
$client->setDriver($driver);
return $client; return $client;
}); });
@ -314,7 +321,7 @@ App::setResource('layout', function($locale) {
}, ['locale']); }, ['locale']);
App::setResource('locale', function() { App::setResource('locale', function() {
return new Locale('en'); return new Locale(App::getEnv('_APP_LOCALE', 'en'));
}); });
// Queues // Queues
@ -420,7 +427,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response
if (empty($user->getId()) // Check a document has been found in the DB if (empty($user->getId()) // Check a document has been found in the DB
|| Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document || Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document
|| !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)) { // Validate user has valid login token
$user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]);
} }

View file

@ -61,12 +61,12 @@ $cli
Console::log('🟢 Abuse protection is enabled'); Console::log('🟢 Abuse protection is enabled');
} }
$authWhitelistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', null);
$authWhitelistEmails = App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null); $authWhitelistEmails = App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null);
$authWhitelistIPs = App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null); $authWhitelistIPs = App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null);
$authWhitelistDomains = App::getEnv('_APP_CONSOLE_WHITELIST_DOMAINS', null);
if(empty($authWhitelistEmails) if(empty($authWhitelistRoot)
&& empty($authWhitelistDomains) && empty($authWhitelistEmails)
&& empty($authWhitelistIPs) && empty($authWhitelistIPs)
) { ) {
Console::log('🔴 Console access limits are disabled'); Console::log('🔴 Console access limits are disabled');

View file

@ -2,17 +2,24 @@
global $cli; global $cli;
use Appwrite\Auth\Auth;
use Appwrite\Docker\Compose; use Appwrite\Docker\Compose;
use Appwrite\Docker\Env; use Appwrite\Docker\Env;
use Utopia\Analytics\GoogleAnalytics; use Utopia\Analytics\GoogleAnalytics;
use Utopia\CLI\Console; use Utopia\CLI\Console;
use Utopia\Config\Config; use Utopia\Config\Config;
use Utopia\View; use Utopia\View;
use Utopia\Validator\Text;
$cli $cli
->task('install') ->task('install')
->desc('Install Appwrite') ->desc('Install Appwrite')
->action(function () { ->param('httpPort', '', new Text(4), 'Server HTTP port', true)
->param('httpsPort', '', new Text(4), 'Server HTTPS port', true)
->param('organization', 'appwrite', new Text(0), 'Docker Registry organization', true)
->param('image', 'appwrite', new Text(0), 'Main appwrite docker image', true)
->param('interactive','Y', new Text(1), 'Run an interactive session', true)
->action(function ($httpPort, $httpsPort, $organization, $image, $interactive) {
/** /**
* 1. Start - DONE * 1. Start - DONE
* 2. Check for older setup and get older version - DONE * 2. Check for older setup and get older version - DONE
@ -60,6 +67,9 @@ $cli
$data = @file_get_contents($path.'/docker-compose.yml'); $data = @file_get_contents($path.'/docker-compose.yml');
if($data !== false) { if($data !== false) {
$time = \time();
Console::info('Compose file found, creating backup: docker-compose.yml.'.$time.'.backup');
file_put_contents($path.'/docker-compose.yml.'.$time.'.backup',$data);
$compose = new Compose($data); $compose = new Compose($data);
$appwrite = $compose->getService('appwrite'); $appwrite = $compose->getService('appwrite');
$oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null; $oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null;
@ -85,6 +95,8 @@ $cli
$data = @file_get_contents($path.'/.env'); $data = @file_get_contents($path.'/.env');
if($data !== false) { // Fetch all env vars from previous .env file if($data !== false) { // Fetch all env vars from previous .env file
Console::info('Env file found, creating backup: .env.'.$time.'.backup');
file_put_contents($path.'/.env.'.$time.'.backup',$data);
$env = new Env($data); $env = new Env($data);
foreach ($env->list() as $key => $value) { foreach ($env->list() as $key => $value) {
@ -108,16 +120,36 @@ $cli
} }
} }
$httpPort = Console::confirm('Choose your server HTTP port: (default: '.$defaultHTTPPort.')'); if(empty($httpPort)) {
$httpPort = ($httpPort) ? $httpPort : $defaultHTTPPort; $httpPort = Console::confirm('Choose your server HTTP port: (default: '.$defaultHTTPPort.')');
$httpPort = ($httpPort) ? $httpPort : $defaultHTTPPort;
}
$httpsPort = Console::confirm('Choose your server HTTPS port: (default: '.$defaultHTTPSPort.')'); if(empty($httpsPort)) {
$httpsPort = ($httpsPort) ? $httpsPort : $defaultHTTPSPort; $httpsPort = Console::confirm('Choose your server HTTPS port: (default: '.$defaultHTTPSPort.')');
$httpsPort = ($httpsPort) ? $httpsPort : $defaultHTTPSPort;
}
$input = []; $input = [];
foreach($vars as $key => $var) { foreach($vars as $key => $var) {
if(!$var['required']) { if(!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) {
if($data && $var['default'] !== null) {
$input[$var['name']] = $var['default'];
continue;
}
if($var['filter'] === 'token') {
$input[$var['name']] = Auth::tokenGenerator();
continue;
}
if($var['filter'] === 'password') {
$input[$var['name']] = Auth::passwordGenerator();
continue;
}
}
if(!$var['required'] || !Console::isInteractive() || $interactive !== 'Y') {
$input[$var['name']] = $var['default']; $input[$var['name']] = $var['default'];
continue; continue;
} }
@ -136,6 +168,8 @@ $cli
->setParam('httpPort', $httpPort) ->setParam('httpPort', $httpPort)
->setParam('httpsPort', $httpsPort) ->setParam('httpsPort', $httpsPort)
->setParam('version', APP_VERSION_STABLE) ->setParam('version', APP_VERSION_STABLE)
->setParam('organization', $organization)
->setParam('image', $image)
; ;
$templateForEnv $templateForEnv

View file

@ -36,7 +36,7 @@ $cli
$projects = [$console]; $projects = [$console];
$count = 0; $count = 0;
$migration = new Version\V06($register->get('db')); //TODO: remove hardcoded version and move to dynamic migration $migration = new Version\V07($register->get('db')); //TODO: remove hardcoded version and move to dynamic migration
while ($sum > 0) { while ($sum > 0) {
foreach ($projects as $project) { foreach ($projects as $project) {

View file

@ -21,20 +21,6 @@ use Appwrite\SDK\Language\Swift;
$cli $cli
->task('sdks') ->task('sdks')
->action(function () { ->action(function () {
function getSSLPage($url)
{
$ch = \curl_init();
\curl_setopt($ch, CURLOPT_HEADER, false);
\curl_setopt($ch, CURLOPT_URL, $url);
\curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
\curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = \curl_exec($ch);
\curl_close($ch);
return $result;
}
$platforms = Config::getParam('platforms'); $platforms = Config::getParam('platforms');
$selected = \strtolower(Console::confirm('Choose SDK ("*" for all):')); $selected = \strtolower(Console::confirm('Choose SDK ("*" for all):'));
$version = Console::confirm('Choose an Appwrite version'); $version = Console::confirm('Choose an Appwrite version');
@ -42,7 +28,7 @@ $cli
$production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false; $production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false;
$message = ($git) ? Console::confirm('Please enter your commit message:') : ''; $message = ($git) ? Console::confirm('Please enter your commit message:') : '';
if(!in_array($version, ['0.6.x', '0.7.x'])) { if(!in_array($version, ['0.6.x', '0.7.x', '0.8.x'])) {
throw new Exception('Unknown version given'); throw new Exception('Unknown version given');
} }
@ -193,6 +179,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
->setExamples($examples) ->setExamples($examples)
->setTwitter(APP_SOCIAL_TWITTER_HANDLE) ->setTwitter(APP_SOCIAL_TWITTER_HANDLE)
->setDiscord(APP_SOCIAL_DISCORD_CHANNEL, APP_SOCIAL_DISCORD) ->setDiscord(APP_SOCIAL_DISCORD_CHANNEL, APP_SOCIAL_DISCORD)
->setDefaultHeaders([
'X-Appwrite-Response-Format' => '0.8.0',
])
; ;
try { try {

View file

@ -1,6 +1,6 @@
<?php <?php
$home = $this->getParam('home', ''); $home = $this->getParam('home', '');
$version = $this->getParam('version', '').'.'.APP_CACHE_BUSTER; $version = $this->getParam('version', '') . '.' . APP_CACHE_BUSTER;
?> ?>
<footer class="clear margin-top-large"> <footer class="clear margin-top-large">
<ul class="copyright pull-start"> <ul class="copyright pull-start">
@ -12,6 +12,14 @@ $version = $this->getParam('version', '').'.'.APP_CACHE_BUSTER;
data-analytics-label="GitHub Link" 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-circled"></i> GitHub</a>
</li> </li>
<li>
<a class="link-animation-enabled"
data-analytics
data-analytics-event="click"
data-analytics-category="console/footer"
data-analytics-label="Discord Link"
href="https://appwrite.io/discord" target="_blank" rel="noopener"><i class="icon-discord"></i> Discord</a>
</li>
<li> <li>
<a class="link-animation-enabled" <a class="link-animation-enabled"
data-analytics data-analytics

View file

@ -36,9 +36,6 @@ $maxCells = 10;
<div class="zone xl"> <div class="zone xl">
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}"> <ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/database/collection?id={{router.params.id}}&project={{router.params.project}}"> <li data-state="/console/database/collection?id={{router.params.id}}&project={{router.params.project}}">
<a data-ls-if="{{project-collection.rules.length}} > 0" data-ls-attrs="href=/console/database/document?collection={{router.params.id}}&project={{router.params.project}}&buster={{project-collection.dateUpdated}}" class="button fly round text-align-center">
<i class="icon-plus"></i>
</a>
<h2>Documents</h2> <h2>Documents</h2>
@ -135,7 +132,7 @@ $maxCells = 10;
</div> </div>
</div> </div>
<div class="clear text-align-center paging"> <div class="pull-end text-align-center paging">
<form <form
data-service="database.listDocuments" data-service="database.listDocuments"
data-event="submit" data-event="submit"
@ -166,6 +163,10 @@ $maxCells = 10;
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-documents.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-documents.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form> </form>
</div> </div>
<a data-ls-if="{{project-collection.rules.length}} > 0" data-ls-attrs="href=/console/database/document?collection={{router.params.id}}&project={{router.params.project}}&buster={{project-collection.dateUpdated}}" class="button">
Add Document
</a>
</div> </div>
</li> </li>
<li data-state="/console/database/collection/settings?id={{router.params.id}}&project={{router.params.project}}"> <li data-state="/console/database/collection/settings?id={{router.params.id}}&project={{router.params.project}}">
@ -520,7 +521,7 @@ $maxCells = 10;
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative"> <div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative">
<div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}"> <div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}">
<input type="radio" name="list" data-ls-attrs="value={{project.$id}},id={{project.$id}}" data-ls-bind="{{rule.list|firstElement}}" data-cast-to="array" required /> <input type="radio" data-ls-attrs="value={{project.$id}},id=[{{rule.$id}}].{{project.$id}},name=[{{rule.$id}}].list" data-ls-bind="{{rule.list|firstElement}}" data-cast-to="array" required />
<label data-ls-attrs="for={{project.$id}}"data-ls-bind="{{project.name}}"></label> <label data-ls-attrs="for={{project.$id}}"data-ls-bind="{{project.name}}"></label>
</div> </div>
</div> </div>

View file

@ -12,40 +12,6 @@
<li data-state="/console/database?project={{router.params.project}}"> <li data-state="/console/database?project={{router.params.project}}">
<h2>Collections</h2> <h2>Collections</h2>
<div data-ui-modal class="box modal close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>New Collection</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Database Collection"
data-service="database.createCollection"
data-event="submit"
data-scope="sdk"
data-success="alert,reset,redirect,trigger"
data-success-param-alert-text="Collection created successfully"
data-success-param-redirect-url="/console/database/collection/settings?id={{serviceData.$id}}&project={{router.params.project}}"
data-success-param-trigger-events="database.createCollection"
data-failure="alert"
data-failure-param-alert-text="Failed to create collection"
data-failure-param-alert-classname="error">
<label for="user-name">Name</label>
<input type="text" class="full-width" id="collection-name" name="name" required autocomplete="off" maxlength="128" />
<input type="hidden" id="collection-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
<input type="hidden" id="collection-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<div class="margin-top" <div class="margin-top"
data-service="database.listCollections" data-service="database.listCollections"
data-event="load,database.createCollection,database.updateCollection,database.deleteCollection" data-event="load,database.createCollection,database.updateCollection,database.deleteCollection"
@ -76,7 +42,7 @@
</ul> </ul>
</div> </div>
<div class="clear text-align-center paging"> <div class="pull-end text-align-center paging">
<form <form
data-service="database.listCollections" data-service="database.listCollections"
data-event="submit" data-event="submit"
@ -105,6 +71,41 @@
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-collections.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-collections.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form> </form>
</div> </div>
<div data-ui-modal class="modal close box sticky-footer" data-button-text="Add Collection">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>New Collection</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Database Collection"
data-service="database.createCollection"
data-event="submit"
data-scope="sdk"
data-success="alert,reset,redirect,trigger"
data-success-param-alert-text="Collection created successfully"
data-success-param-redirect-url="/console/database/collection/settings?id={{serviceData.$id}}&project={{router.params.project}}"
data-success-param-trigger-events="database.createCollection"
data-failure="alert"
data-failure-param-alert-text="Failed to create collection"
data-failure-param-alert-classname="error">
<label for="user-name">Name</label>
<input type="text" class="full-width" id="collection-name" name="name" required autocomplete="off" maxlength="128" />
<input type="hidden" id="collection-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
<input type="hidden" id="collection-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
</li> </li>
<!-- <li data-state="/console/database/usage?project={{router.params.project}}"> <!-- <li data-state="/console/database/usage?project={{router.params.project}}">
<h2>Usage</h2> <h2>Usage</h2>

View file

@ -46,28 +46,13 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
<div class="box margin-bottom-large"> <div class="box margin-bottom-large">
<div class="text-align-center"> <div class="text-align-center">
<img src="" data-ls-attrs="src=/images/environments/{{project-function.env|envLogo}}" alt="Function Env." class="avatar huge margin-top-negative-xxl" /> <img src="" data-ls-attrs="src=/images/runtimes/{{project-function.env|envLogo}}" alt="Function Env." class="avatar huge margin-top-negative-xxl" />
<p class="text-fade margin-bottom-small" data-ls-bind="{{project-function.env|envName}} {{project-function.env|envVersion}}"> <p class="text-fade margin-bottom-small" data-ls-bind="{{project-function.env|envName}} {{project-function.env|envVersion}}">
</p> </p>
<div data-ls-if="{{project-function.tag}} !== ''" class="margin-top">
<form data-ls-if="{{project-function.tag}} !== ''" name="functions.createExecution" class="margin-top" <button data-ls-ui-trigger="execute-now">Execute Now</button> &nbsp; <a data-ls-attrs="href=/console/functions/function/logs?id={{router.params.id}}&project={{router.params.project}}" class="button reverse" style="vertical-align: top;">View Logs</a>
data-analytics </div>
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Function Execution"
data-service="functions.createExecution"
data-event="submit"
data-param-function-id="{{router.params.id}}"
data-success="alert,trigger"
data-success-param-alert-text="Function executed successfully"
data-success-param-trigger-events="functions.createExecution"
data-failure="alert"
data-failure-param-alert-text="Failed to execute function"
data-failure-param-alert-classname="error">
<button style="vertical-align: top;">Execute Now</button> &nbsp; <a data-ls-attrs="href=/console/functions/function/logs?id={{router.params.id}}&project={{router.params.project}}" class="button reverse" style="vertical-align: top;">View Logs</a>
</form>
</div> </div>
</div> </div>
@ -575,6 +560,31 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
</div> </div>
</div> </div>
<div data-ui-modal class="modal close box sticky-footer" data-button-hide="on" data-open-event="execute-now">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1 class="margin-bottom">Execute Function</h1>
<form data-ls-if="{{project-function.tag}} !== ''" name="functions.createExecution" class="margin-top"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Function Execution"
data-service="functions.createExecution"
data-event="submit"
data-param-function-id="{{router.params.id}}"
data-success="alert,trigger"
data-success-param-alert-text="Function executed successfully"
data-success-param-trigger-events="functions.createExecution"
data-failure="alert"
data-failure-param-alert-text="Failed to execute function"
data-failure-param-alert-classname="error">
<label for="execution-data">Custom Data</label>
<textarea id="execution-data" name="data" autocomplete="off" class="margin-bottom" placeholder="Data string (optional)"></textarea>
<button type="submit" style="vertical-align: top;">Execute Now</button>
</form>
</div>
<div data-ui-modal class="modal close box sticky-footer" data-button-hide="on" data-open-event="deploy-tag"> <div data-ui-modal class="modal close box sticky-footer" data-button-hide="on" data-open-event="deploy-tag">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button> <button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>

View file

@ -1,5 +1,5 @@
<?php <?php
$environments = $this->getParam('environments', []); $runtimes = $this->getParam('runtimes', []);
?> ?>
<div class="cover"> <div class="cover">
<h1 class="zone xl margin-bottom-large"> <h1 class="zone xl margin-bottom-large">
@ -40,7 +40,7 @@ $environments = $this->getParam('environments', []);
<ul data-ls-loop="project-functions.functions" data-ls-as="function" class="list"> <ul data-ls-loop="project-functions.functions" data-ls-as="function" class="list">
<li class="clear"> <li class="clear">
<div class="pull-start margin-end avatar-container"> <div class="pull-start margin-end avatar-container">
<img src="" data-ls-attrs="src=/images/environments/{{function.env|envLogo}}?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Function Env." class="avatar" loading="lazy" width="60" height="60" /> <img src="" data-ls-attrs="src=/images/runtimes/{{function.env|envLogo}}?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Function Env." class="avatar" loading="lazy" width="60" height="60" />
</div> </div>
<a data-ls-attrs="href=/console/functions/function?id={{function.$id}}&project={{router.params.project}}" class="button pull-end">Settings</a> <a data-ls-attrs="href=/console/functions/function?id={{function.$id}}&project={{router.params.project}}" class="button pull-end">Settings</a>
@ -109,10 +109,10 @@ $environments = $this->getParam('environments', []);
<label for="name">Name</label> <label for="name">Name</label>
<input type="text" id="name" name="name" required autocomplete="off" class="margin-bottom" maxlength="128" /> <input type="text" id="name" name="name" required autocomplete="off" class="margin-bottom" maxlength="128" />
<label for="env">Environment</label> <label for="env">Runtimes</label>
<select name="env" id="env" required class="margin-bottom-xl"> <select name="env" id="env" required class="margin-bottom-xl">
<?php foreach($environments as $key => $environment): ?> <?php foreach($runtimes as $key => $runtime): ?>
<option value="<?php echo $this->escape($key); ?>"><?php echo $this->escape($environment['name']); ?> <?php echo $this->escape($environment['version']); ?></option> <option value="<?php echo $this->escape($key); ?>"><?php echo $this->escape($runtime['name']); ?> <?php echo $this->escape($runtime['version']); ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>

View file

@ -93,7 +93,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
<div class="chart-metric"> <div class="chart-metric">
<div class="value margin-bottom-small"><span class="sum" data-ls-bind="{{usage.requests.total|statsTotal}}">N/A</span></div> <div class="value margin-bottom-small"><span class="sum" data-ls-bind="{{usage.requests.total|statsTotal}}">N/A</span></div>
<div class="metric margin-bottom-small">Requests <span class="tooltip" data-tooltip="Total number of API requests last 30 days"><i class="icon-info-circled"></i></span></div> <div class="metric margin-bottom-small">Requests <span class="tooltip" data-tooltip="Total number of API requests"><i class="icon-info-circled"></i></span></div>
</div> </div>
</div> </div>
<div class="col span-3"> <div class="col span-3">

View file

@ -415,7 +415,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
data-failure-param-alert-classname="error"> data-failure-param-alert-classname="error">
<input name="teamId" data-ls-attrs="id=leave-teamId-{{member.$id}}" type="hidden" data-ls-bind="{{console-project.teamId}}"> <input name="teamId" data-ls-attrs="id=leave-teamId-{{member.$id}}" type="hidden" data-ls-bind="{{console-project.teamId}}">
<input name="inviteId" data-ls-attrs="id=leave-inviteId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}"> <input name="membershipId" data-ls-attrs="id=leave-membershipId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}">
<button class="danger">Leave</button> <button class="danger">Leave</button>
</form> </form>
@ -437,7 +437,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
data-failure-param-alert-classname="error"> data-failure-param-alert-classname="error">
<input name="teamId" data-ls-attrs="id=resend-teamId-{{member.$id}}" type="hidden" data-ls-bind="{{console-project.teamId}}"> <input name="teamId" data-ls-attrs="id=resend-teamId-{{member.$id}}" type="hidden" data-ls-bind="{{console-project.teamId}}">
<input name="inviteId" data-ls-attrs="id=resend-inviteId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}"> <input name="membershipId" data-ls-attrs="id=resend-membershipId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}">
<button class="reverse">Resend</button> <button class="reverse">Resend</button>
</form> </form>

View file

@ -17,48 +17,6 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<li data-state="/console/database?project={{router.params.project}}"> <li data-state="/console/database?project={{router.params.project}}">
<h2 class="margin-bottom">Files</h2> <h2 class="margin-bottom">Files</h2>
<div data-ui-modal class="box modal sticky-footer close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<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"
data-service="storage.createFile"
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-failure="alert"
data-failure-param-alert-text="Failed to upload file"
data-failure-param-alert-classname="error">
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="integer" value="1">
<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(['*'])); ?>" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add * 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 * for wildcard access</div>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
<form class="box padding-small margin-bottom search" <form class="box padding-small margin-bottom search"
data-service="storage.listFiles" data-service="storage.listFiles"
data-event="submit" data-event="submit"
@ -215,7 +173,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
</div> </div>
</div> </div>
<div class="clear text-align-center paging"> <div class="pull-end text-align-center paging">
<form <form
data-service="storage.listFiles" data-service="storage.listFiles"
data-event="submit" data-event="submit"
@ -244,6 +202,48 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<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-files.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form> </form>
</div> </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"
data-service="storage.createFile"
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-failure="alert"
data-failure-param-alert-text="Failed to upload file"
data-failure-param-alert-classname="error">
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="integer" value="1">
<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(['*'])); ?>" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add * 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 * for wildcard access</div>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
</div> </div>
</li> </li>
</ul> </ul>

View file

@ -1,5 +1,6 @@
<?php <?php
$providers = $this->getParam('providers', []); $providers = $this->getParam('providers', []);
$auth = $this->getParam('auth', []);
?> ?>
<div class="cover"> <div class="cover">
@ -17,41 +18,6 @@ $providers = $this->getParam('providers', []);
<h2>Users</h2> <h2>Users</h2>
<div data-ui-modal class="box modal close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create User</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create User"
data-service="users.create"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created user successfully"
data-success-param-trigger-events="users.create"
data-failure="alert"
data-failure-param-alert-text="Failed to create user"
data-failure-param-alert-classname="error">
<label for="user-name">Name</label>
<input type="text" class="full-width" id="user-name" name="name" required autocomplete="off" maxlength="128" />
<label for="user-email">Email</label>
<input type="email" class="full-width" id="user-email" name="email" required autocomplete="off" />
<label for="user-password">Password</label>
<input type="password" class="full-width" id="user-password" name="password" required pattern=".{6,}" title="Six or more characters" autocomplete="off" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<form class="box padding-small margin-bottom search" <form class="box padding-small margin-bottom search"
data-service="users.list" data-service="users.list"
data-event="submit" data-event="submit"
@ -111,7 +77,7 @@ $providers = $this->getParam('providers', []);
<td data-title="Name: "> <td data-title="Name: ">
<a data-ls-attrs="href=/console/users/user?id={{user.$id}}&project={{router.params.project}}"> <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}}"></span>
<span data-ls-if="{{user.name}} === ''">-----</span> <span data-ls-if="{{user.name|escape}} === ''">-----</span>
</a> </a>
</td> </td>
<td data-title="Email: "> <td data-title="Email: ">
@ -137,7 +103,7 @@ $providers = $this->getParam('providers', []);
</div> </div>
</div> </div>
<div class="clear text-align-center paging"> <div class="pull-end text-align-center paging">
<form <form
data-service="users.list" data-service="users.list"
data-event="submit" data-event="submit"
@ -166,41 +132,47 @@ $providers = $this->getParam('providers', []);
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-users.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-users.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form> </form>
</div> </div>
<div data-ui-modal class="box modal close" data-button-text="Add User">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create User</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create User"
data-service="users.create"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created user successfully"
data-success-param-trigger-events="users.create"
data-failure="alert"
data-failure-param-alert-text="Failed to create user"
data-failure-param-alert-classname="error">
<label for="user-name">Name</label>
<input type="text" class="full-width" id="user-name" name="name" required autocomplete="off" maxlength="128" />
<label for="user-email">Email</label>
<input type="email" class="full-width" id="user-email" name="email" required autocomplete="off" />
<label for="user-password">Password</label>
<input type="password" class="full-width" id="user-password" name="password" required pattern=".{6,}" title="Six or more characters" autocomplete="off" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
</div> </div>
</li> </li>
<li data-state="/console/users/teams?project={{router.params.project}}"> <li data-state="/console/users/teams?project={{router.params.project}}">
<h2>Teams</h2> <h2>Teams</h2>
<div data-ui-modal class="box modal close" data-button-text="" data-button-class="fly round" data-button-icon="icon-plus">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create Team</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Team"
data-service="teams.create"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created team successfully"
data-success-param-trigger-events="filter-teams-changed,teams.create"
data-failure="alert"
data-failure-param-alert-text="Failed to create team"
data-failure-param-alert-classname="error">
<label for="team-name">Name</label>
<input type="text" class="full-width" id="team-name" name="name" required autocomplete="off" maxlength="128" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<form class="box padding-small margin-bottom search" <form class="box padding-small margin-bottom search"
data-service="teams.list" data-service="teams.list"
data-event="submit" data-event="submit"
@ -239,7 +211,7 @@ $providers = $this->getParam('providers', []);
</div> </div>
<div data-ls-if="0 != {{project-teams.sum}}"> <div data-ls-if="0 != {{project-teams.sum}}">
<div class="margin-bottom-small margin-end-small text-align-end text-size-small"><span data-ls-bind="{{project-teams.sum}}"></span> results found</div> <div class="margin-bottom-small margin-end-small text-align-end text-size-small"><span data-ls-bind="{{project-teams.sum}}"></span> results found</div>
<div class="box margin-bottom"> <div class="box margin-bottom">
<table class="vertical"> <table class="vertical">
@ -267,7 +239,7 @@ $providers = $this->getParam('providers', []);
</div> </div>
</div> </div>
<div class="clear text-align-center paging"> <div class="pull-end text-align-center paging">
<form <form
data-service="teams.list" data-service="teams.list"
data-event="submit" data-event="submit"
@ -296,13 +268,131 @@ $providers = $this->getParam('providers', []);
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-teams.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-teams.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form> </form>
</div> </div>
<div data-ui-modal class="box modal close" data-button-text="Add Team">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create Team</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Team"
data-service="teams.create"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created team successfully"
data-success-param-trigger-events="filter-teams-changed,teams.create"
data-failure="alert"
data-failure-param-alert-text="Failed to create team"
data-failure-param-alert-classname="error">
<label for="team-name">Name</label>
<input type="text" class="full-width" id="team-name" name="name" required autocomplete="off" maxlength="128" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
</div> </div>
</li> </li>
<li data-state="/console/users/providers?project={{router.params.project}}"> <li data-state="/console/users/providers?project={{router.params.project}}">
<h2>OAuth2 Providers</h2> <p data-ls-if="{{console-project.usersAuthLimit}} == 0" class="text-fade text-size-small margin-bottom pull-end">Unlimited Users <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Set Limit</a></p>
<p data-ls-if="{{console-project.usersAuthLimit}} != 0" class="text-fade text-size-small margin-bottom pull-end"><span data-ls-bind="{{console-project.usersAuthLimit|statsTotal}}"></span> Users allowed <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Change Limit</a></p>
<h2>Settings</h2>
<div class="margin-bottom margin-top-large" <div data-ui-modal class="modal close" data-button-alias="none" data-open-event="project-update-auth-users-limit">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Max Allowed Users</h1>
<form data-debug="1"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Users Limit"
data-service="projects.updateAuthLimit"
data-scope="console"
data-event="submit"
data-param-project-id="{{router.params.project}}"
data-success="alert,trigger"
data-success-param-alert-text="Updated project users limit successfully"
data-success-param-trigger-events="projects.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update project users limit"
data-failure-param-alert-classname="error">
<input name="limit" id="users-limit" type="number" data-ls-bind="{{console-project.usersAuthLimit}}" data-cast-to="numeric" min="0" />
<div class="info row thin margin-bottom margin-top">
<div class="col span-1">
<i class="icon-info-circled text-sign"></i>
</div>
<div class="col span-11">This limit will prevent new users from signing up for your project, no matter what auth method has been used. You will still be able to create users and team memberships from your Appwrite console. For an unlimited amount of users, set the limit to 0.</div>
</div>
<button>Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<p class="text-fade margin-bottom">Choose auth methods you wish to use.</p>
<ul class="tiles cell-3 margin-bottom-small">
<?php foreach($auth as $index => $method):
$key = $method['key'] ?? '';
$name = $method['name'] ?? '';
$icon = $method['icon'] ?? '';
$docs = $method['docs'] ?? '';
$enabled = $method['enabled'] ?? false;
?>
<li class="">
<div class="box padding-small margin-bottom clear">
<?php if($enabled): ?>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Auth Status (<?php echo $this->escape($name); ?>)"
data-service="projects.updateAuthStatus"
data-scope="console"
data-event="change"
data-param-project-id="{{router.params.project}}"
data-success="alert,trigger"
data-success-param-alert-text="Updated project auth status successfully"
data-success-param-trigger-events="projects.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update project auth status settings"
data-failure-param-alert-classname="error">
<input name="method" id="<?php echo $this->escape($key); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($index); ?>">
<input name="status" type="hidden" data-forms-switch data-ls-bind="{{console-project.<?php echo $this->escape($key); ?>}}" data-cast-to="boolean" class="pull-end" />
</form>
<?php endif; ?>
<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>
<?php if($docs): ?>
<p class="margin-bottom-no text-one-liner text-size-small">
<a href="<?php echo $this->escape($docs); ?>" target="_blank" rel="noopener">Docs<i class="icon-link-ext"></i></a>
</p>
<?php endif; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
<h3>OAuth2 Providers</h3>
<div class="margin-bottom margin-top"
data-service="projects.get" data-service="projects.get"
data-event="load,projects.create,projects.update,projects.deleteProject" data-event="load,projects.create,projects.update,projects.deleteProject"
data-name="console-project" data-name="console-project"
@ -312,6 +402,7 @@ $providers = $this->getParam('providers', []);
<?php foreach ($providers as $provider => $data): <?php foreach ($providers as $provider => $data):
if (isset($data['enabled']) && !$data['enabled']) { continue; } if (isset($data['enabled']) && !$data['enabled']) { continue; }
if (isset($data['mock']) && $data['mock']) { continue; } if (isset($data['mock']) && $data['mock']) { continue; }
$sandbox = $data['sandbox'] ?? false;
$form = $data['form'] ?? false; $form = $data['form'] ?? false;
$name = $data['name'] ?? 'Unknown'; $name = $data['name'] ?? 'Unknown';
$beta = $data['beta'] ?? false; $beta = $data['beta'] ?? false;
@ -320,9 +411,10 @@ $providers = $this->getParam('providers', []);
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>"> <div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button> <button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1><?php echo $this->escape($name); ?> OAuth2 Settings</h1> <h1><?php echo $this->escape($name); ?> <?php if($sandbox): ?>Sandbox<?php endif; ?> OAuth2 Settings</h1>
<form <form
autocomplete="off"
data-analytics data-analytics
data-analytics-activity data-analytics-activity
data-analytics-event="submit" data-analytics-event="submit"
@ -338,6 +430,10 @@ $providers = $this->getParam('providers', []);
data-failure="alert" data-failure="alert"
data-failure-param-alert-text="Failed to update project OAuth2 settings" data-failure-param-alert-text="Failed to update project OAuth2 settings"
data-failure-param-alert-classname="error"> data-failure-param-alert-classname="error">
<input style="display: none" type="text" /> <?php /** Disabling Chrone Autofill @see https://stackoverflow.com/a/15917221/2299554 */ ?>
<input style="display: none" type="password" /> <?php /** Disabling Chrone Autofill @see https://stackoverflow.com/a/15917221/2299554 */ ?>
<input name="provider" id="provider<?php echo $this->escape(ucfirst($provider)); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($provider); ?>"> <input name="provider" id="provider<?php echo $this->escape(ucfirst($provider)); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($provider); ?>">
<?php if(!$form): ?> <?php if(!$form): ?>
@ -383,10 +479,10 @@ $providers = $this->getParam('providers', []);
<button class="switch pull-end" data-ls-ui-trigger="provider-update-<?php echo $this->escape($provider); ?>"></button> <button class="switch pull-end" data-ls-ui-trigger="provider-update-<?php echo $this->escape($provider); ?>"></button>
</span> </span>
<img src="/images/oauth2/<?php echo $this->escape(strtolower($provider)); ?>.png?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="<?php echo $this->escape(ucfirst($provider)); ?> Logo" class="pull-start provider margin-end" /> <img src="/images/users/oauth2/<?php echo $this->escape(strtolower($provider)); ?>.png?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="<?php echo $this->escape(ucfirst($provider)); ?> Logo" class="pull-start provider margin-end" />
<span class="text-size-small"> <span class="text-size-small">
<?php echo $this->escape($name); ?> <?php if($beta): ?>(beta)<?php endif; ?> <?php echo $this->escape($name); ?> <?php if($sandbox): ?><span class="text-size-xs text-fade">sandbox</span><?php endif; ?> <?php if($beta): ?><span class="text-size-xs text-fade">beta</span><?php endif; ?>
</span> </span>
<p class="margin-bottom-no text-one-liner text-size-small"> <p class="margin-bottom-no text-one-liner text-size-small">

View file

@ -105,7 +105,7 @@
data-failure-param-alert-classname="error"> data-failure-param-alert-classname="error">
<input name="teamId" data-ls-attrs="id={{member.$id}}" type="hidden" data-ls-bind="{{router.params.id}}"> <input name="teamId" data-ls-attrs="id={{member.$id}}" type="hidden" data-ls-bind="{{router.params.id}}">
<input name="inviteId" data-ls-attrs="id=leave-inviteId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}"> <input name="membershipId" data-ls-attrs="id=leave-membershipId-{{member.$id}}" type="hidden" data-ls-bind="{{member.$id}}">
<button class="danger">Remove</button> <button class="danger">Remove</button>
</form> </form>

View file

@ -9,7 +9,7 @@
data-scope="console" data-scope="console"
data-event="submit" data-event="submit"
data-param-team-id="{{router.params.teamId}}" data-param-team-id="{{router.params.teamId}}"
data-param-invite-id="{{router.params.inviteId}}" data-param-invite-id="{{router.params.membershipId}}"
data-param-user-id="{{router.params.userId}}" data-param-user-id="{{router.params.userId}}"
data-param-secret="{{router.params.secret}}" data-param-secret="{{router.params.secret}}"
data-success="redirect,alert,trigger" data-success="redirect,alert,trigger"

View file

@ -1,3 +1,6 @@
<?php
$smtpEnabled = $this->getParam('smtpEnabled', false);
?>
<div class="zone medium"> <div class="zone medium">
<h1 class="zone xl margin-bottom-large margin-top"> <h1 class="zone xl margin-bottom-large margin-top">
Password Recovery Password Recovery
@ -25,7 +28,13 @@
<input name="url" type="hidden" data-ls-bind="{{env.ENDPOINT}}/auth/recovery/reset" /> <input name="url" type="hidden" data-ls-bind="{{env.ENDPOINT}}/auth/recovery/reset" />
<button type="submit" class="btn btn-primary"><i class="fa fa-sign-in"></i> Recover</button> <?php if(!$smtpEnabled): ?>
<div class="box note padding-tiny warning margin-bottom text-align-center">
<i class="icon-warning"></i> SMTP connection is disabled. <a href="https://appwrite.io/docs/email-delivery" target="_blank" rel="noopener">Learn more <i class="icon-link-ext"></i></a>
</div>
<?php endif; ?>
<button type="submit" class="btn btn-primary"<?php if(!$smtpEnabled): ?> disabled<?php endif; ?>><i class="fa fa-sign-in"></i> Recover</button>
</form> </form>
</div> </div>

View file

@ -1,3 +1,6 @@
<?php
$root = ($this->getParam('root') !== 'disabled');
?>
<div class="zone medium" <div class="zone medium"
data-service="account.get" data-service="account.get"
data-name="account" data-name="account"
@ -43,7 +46,7 @@
<br /> <br />
<div class="text-line-high-large text-align-center"> <div class="text-line-high-large text-align-center">
<a href="/auth/recovery">Forgot password?</a> or don't have an account? <b><a href="/auth/signup">Sign up now</a></b> <a href="/auth/recovery">Forgot password?</a><?php if(!$root): ?> or don't have an account? <b><a href="/auth/signup">Sign up now</a></b><?php endif; ?>
</div> </div>
</div> </div>

View file

@ -1,4 +1,14 @@
<div class="zone medium signup"> <?php
$root = ($this->getParam('root') !== 'disabled');
?>
<div class="zone medium signup"
data-service="account.get"
data-name="account"
data-scope="console"
data-event="load"
data-success="redirect"
data-success-param-redirect-url="/console"
data-success-param-trigger-events="account.get">
<h1 class="zone xl margin-bottom-large margin-top"> <h1 class="zone xl margin-bottom-large margin-top">
Sign Up Sign Up
</h1> </h1>
@ -9,6 +19,7 @@
<form name="account.create" <form name="account.create"
data-analytics data-analytics
data-newsletter
data-analytics-activity data-analytics-activity
data-analytics-event="submit" data-analytics-event="submit"
data-analytics-category="home" data-analytics-category="home"
@ -23,6 +34,10 @@
data-failure-param-alert-text="Registration Failed. Please try again later" data-failure-param-alert-text="Registration Failed. Please try again later"
data-failure-param-alert-classname="error"> data-failure-param-alert-classname="error">
<?php if($root): ?>
<p>Please create your root account</p>
<?php endif; ?>
<label>Name</label> <label>Name</label>
<input name="name" type="text" autocomplete="name" placeholder="" required maxlength="128"> <input name="name" type="text" autocomplete="name" placeholder="" required maxlength="128">
@ -39,11 +54,20 @@
By signing up, you agree to the <a data-ls-attrs="href={{env.HOME}}/policy/terms" tabindex="-1" target="_blank" rel="noopener">Terms and Conditions</a> and <a data-ls-attrs="href={{env.HOME}}/policy/privacy" target="_blank" tabindex="-1" rel="noopener">Privacy Policy</a> By signing up, you agree to the <a data-ls-attrs="href={{env.HOME}}/policy/terms" tabindex="-1" target="_blank" rel="noopener">Terms and Conditions</a> and <a data-ls-attrs="href={{env.HOME}}/policy/privacy" target="_blank" tabindex="-1" rel="noopener">Privacy Policy</a>
</div> </div>
<div class="newsletter margin-top margin-bottom-large">
<div class="pull-start margin-end-small margin-bottom">
<input id="newsletter" type="checkbox" />
</div>
Sign up for our <a data-ls-attrs="href={{env.HOME}}/updates" tabindex="-1" target="_blank" rel="noopener">monthly newsletter</a> <span class="text-fade">(optional)</span>.
</div>
<button type="submit">Sign Up</button> <button type="submit">Sign Up</button>
</form> </form>
</div> </div>
<?php if(!$root): ?>
<div class="zone medium text-align-center"> <div class="zone medium text-align-center">
<a href="/auth/signin">Already have an account?</a> <a href="/auth/signin">Already have an account?</a>
</div> </div>
<?PHP endif; ?>

View file

@ -3,6 +3,8 @@
$httpPort = $this->getParam('httpPort', ''); $httpPort = $this->getParam('httpPort', '');
$httpsPort = $this->getParam('httpsPort', ''); $httpsPort = $this->getParam('httpsPort', '');
$version = $this->getParam('version', ''); $version = $this->getParam('version', '');
$organization = $this->getParam('organization', '');
$image = $this->getParam('image', '');
?>version: '3' ?>version: '3'
services: services:
@ -32,7 +34,7 @@ services:
- appwrite - appwrite
appwrite: appwrite:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
container_name: appwrite container_name: appwrite
restart: unless-stopped restart: unless-stopped
networks: networks:
@ -52,10 +54,12 @@ services:
depends_on: depends_on:
- mariadb - mariadb
- redis - redis
- clamav # - clamav
- influxdb - influxdb
environment: environment:
- _APP_ENV - _APP_ENV
- _APP_LOCALE
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS - _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_WHITELIST_IPS - _APP_CONSOLE_WHITELIST_IPS
- _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_NAME
@ -93,10 +97,10 @@ services:
- _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP - _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_ENVS - _APP_FUNCTIONS_RUNTIMES
appwrite-worker-usage: appwrite-worker-usage:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-usage entrypoint: worker-usage
container_name: appwrite-worker-usage container_name: appwrite-worker-usage
restart: unless-stopped restart: unless-stopped
@ -115,7 +119,7 @@ services:
- _APP_STATSD_PORT - _APP_STATSD_PORT
appwrite-worker-audits: appwrite-worker-audits:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-audits entrypoint: worker-audits
container_name: appwrite-worker-audits container_name: appwrite-worker-audits
restart: unless-stopped restart: unless-stopped
@ -137,7 +141,7 @@ services:
- _APP_DB_PASS - _APP_DB_PASS
appwrite-worker-webhooks: appwrite-worker-webhooks:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-webhooks entrypoint: worker-webhooks
container_name: appwrite-worker-webhooks container_name: appwrite-worker-webhooks
restart: unless-stopped restart: unless-stopped
@ -153,14 +157,9 @@ services:
- _APP_REDIS_PORT - _APP_REDIS_PORT
- _APP_REDIS_USER - _APP_REDIS_USER
- _APP_REDIS_PASS - _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-worker-tasks: appwrite-worker-tasks:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-tasks entrypoint: worker-tasks
container_name: appwrite-worker-tasks container_name: appwrite-worker-tasks
restart: unless-stopped restart: unless-stopped
@ -171,6 +170,7 @@ services:
- mariadb - mariadb
environment: environment:
- _APP_ENV - _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_REDIS_HOST - _APP_REDIS_HOST
- _APP_REDIS_PORT - _APP_REDIS_PORT
@ -183,7 +183,7 @@ services:
- _APP_DB_PASS - _APP_DB_PASS
appwrite-worker-deletes: appwrite-worker-deletes:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-deletes entrypoint: worker-deletes
container_name: appwrite-worker-deletes container_name: appwrite-worker-deletes
restart: unless-stopped restart: unless-stopped
@ -209,7 +209,7 @@ services:
- _APP_DB_PASS - _APP_DB_PASS
appwrite-worker-certificates: appwrite-worker-certificates:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-certificates entrypoint: worker-certificates
container_name: appwrite-worker-certificates container_name: appwrite-worker-certificates
restart: unless-stopped restart: unless-stopped
@ -236,7 +236,7 @@ services:
- _APP_DB_PASS - _APP_DB_PASS
appwrite-worker-functions: appwrite-worker-functions:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-functions entrypoint: worker-functions
container_name: appwrite-worker-functions container_name: appwrite-worker-functions
restart: unless-stopped restart: unless-stopped
@ -251,6 +251,7 @@ services:
- /tmp:/tmp:rw - /tmp:/tmp:rw
environment: environment:
- _APP_ENV - _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST - _APP_REDIS_HOST
- _APP_REDIS_PORT - _APP_REDIS_PORT
- _APP_REDIS_USER - _APP_REDIS_USER
@ -265,10 +266,11 @@ services:
- _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP - _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_RUNTIMES
- _APP_USAGE_STATS - _APP_USAGE_STATS
appwrite-worker-mails: appwrite-worker-mails:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-mails entrypoint: worker-mails
container_name: appwrite-worker-mails container_name: appwrite-worker-mails
restart: unless-stopped restart: unless-stopped
@ -291,7 +293,7 @@ services:
- _APP_SMTP_PASSWORD - _APP_SMTP_PASSWORD
appwrite-maintenance: appwrite-maintenance:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: maintenance entrypoint: maintenance
container_name: appwrite-maintenance container_name: appwrite-maintenance
restart: unless-stopped restart: unless-stopped
@ -312,7 +314,7 @@ services:
appwrite-schedule: appwrite-schedule:
image: appwrite/appwrite:<?php echo $version."\n"; ?> image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: schedule entrypoint: schedule
container_name: appwrite-schedule container_name: appwrite-schedule
restart: unless-stopped restart: unless-stopped
@ -336,7 +338,7 @@ services:
volumes: volumes:
- appwrite-mariadb:/var/lib/mysql:rw - appwrite-mariadb:/var/lib/mysql:rw
environment: environment:
- MYSQL_ROOT_PASSWORD=rootsecretpassword - MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
- MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_DATABASE=${_APP_DB_SCHEMA}
- MYSQL_USER=${_APP_DB_USER} - MYSQL_USER=${_APP_DB_USER}
- MYSQL_PASSWORD=${_APP_DB_PASS} - MYSQL_PASSWORD=${_APP_DB_PASS}
@ -351,17 +353,17 @@ services:
volumes: volumes:
- appwrite-redis:/data:rw - appwrite-redis:/data:rw
clamav: # clamav:
image: appwrite/clamav:1.2.0 # image: appwrite/clamav:1.2.0
container_name: appwrite-clamav # container_name: appwrite-clamav
restart: unless-stopped # restart: unless-stopped
networks: # networks:
- appwrite # - appwrite
volumes: # volumes:
- appwrite-uploads:/storage/uploads # - appwrite-uploads:/storage/uploads
influxdb: influxdb:
image: influxdb:1.8-alpine image: appwrite/influxdb:1.0.0
container_name: appwrite-influxdb container_name: appwrite-influxdb
restart: unless-stopped restart: unless-stopped
networks: networks:

View file

@ -11,7 +11,7 @@ $analytics = $this->getParam('analytics', 'UA-26264668-9');
$mode = $this->getParam('mode', ''); $mode = $this->getParam('mode', '');
$canonical = $this->getParam('canonical', ''); $canonical = $this->getParam('canonical', '');
$locale = $this->getParam('locale', null); $locale = $this->getParam('locale', null);
$environments = $this->getParam('environments', null); $runtimes = $this->getParam('runtimes', null);
if(!empty($platforms)) { if(!empty($platforms)) {
$platforms = array_map(function($platform) { $platforms = array_map(function($platform) {
@ -78,7 +78,7 @@ if(!empty($platforms)) {
SETUP: '<?php echo $this->escape($this->getParam('setup')); ?>', SETUP: '<?php echo $this->escape($this->getParam('setup')); ?>',
API: '/v1', API: '/v1',
PROJECT: 'console', PROJECT: 'console',
ENVIRONMENTS: <?php echo json_encode($environments); ?>, RUNTIMES: <?php echo json_encode($runtimes); ?>,
PLATFORMS: <?php echo json_encode($platforms); ?>, PLATFORMS: <?php echo json_encode($platforms); ?>,
LOCALE: '<?php echo $this->escape($locale->getText('settings.locale')); ?>', LOCALE: '<?php echo $this->escape($locale->getText('settings.locale')); ?>',
PREFIX: '<?php echo $this->escape($this->getParam('prefix')); ?>', PREFIX: '<?php echo $this->escape($this->getParam('prefix')); ?>',

View file

@ -112,13 +112,21 @@ class DeletesV1
protected function deleteUser(Document $document, $projectId) protected function deleteUser(Document $document, $projectId)
{ {
$tokens = $document->getAttribute('tokens', []); $tokens = $document->getAttribute('tokens', []);
foreach ($tokens as $token) { foreach ($tokens as $token) {
if (!$this->getProjectDB($projectId)->deleteDocument($token->getId())) { if (!$this->getProjectDB($projectId)->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB'); throw new Exception('Failed to remove token from DB');
} }
} }
$sessions = $document->getAttribute('sessions', []);
foreach ($sessions as $session) {
if (!$this->getProjectDB($projectId)->deleteDocument($session->getId())) {
throw new Exception('Failed to remove session from DB');
}
}
// Delete Memberships // Delete Memberships
$this->deleteByGroup([ $this->deleteByGroup([
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS, '$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,

View file

@ -14,21 +14,19 @@ use Utopia\Config\Config;
require_once __DIR__.'/../init.php'; require_once __DIR__.'/../init.php';
Runtime::enableCoroutine(0);
Console::title('Functions V1 Worker'); Console::title('Functions V1 Worker');
Runtime::setHookFlags(SWOOLE_HOOK_ALL);
Console::success(APP_NAME.' functions worker v1 has started'); Console::success(APP_NAME.' functions worker v1 has started');
$environments = Config::getParam('environments'); $runtimes = Config::getParam('runtimes');
/** /**
* Warmup Docker Images * Warmup Docker Images
*/ */
$warmupStart = \microtime(true); $warmupStart = \microtime(true);
Co\run(function() use ($environments) { // Warmup: make sure images are ready to run fast 🚀 Co\run(function() use ($runtimes) { // Warmup: make sure images are ready to run fast 🚀
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
$dockerUser = App::getEnv('DOCKERHUB_PULL_USERNAME', null); $dockerUser = App::getEnv('DOCKERHUB_PULL_USERNAME', null);
$dockerPass = App::getEnv('DOCKERHUB_PULL_PASSWORD', null); $dockerPass = App::getEnv('DOCKERHUB_PULL_PASSWORD', null);
@ -41,14 +39,14 @@ Co\run(function() use ($environments) { // Warmup: make sure images are ready t
Console::log('Docker Login'. $stdout.$stderr); Console::log('Docker Login'. $stdout.$stderr);
} }
foreach($environments as $environment) { foreach($runtimes as $runtime) {
go(function() use ($environment) { go(function() use ($runtime) {
$stdout = ''; $stdout = '';
$stderr = ''; $stderr = '';
Console::info('Warming up '.$environment['name'].' '.$environment['version'].' environment...'); Console::info('Warming up '.$runtime['name'].' '.$runtime['version'].' environment...');
Console::execute('docker pull '.$environment['image'], '', $stdout, $stderr); Console::execute('docker pull '.$runtime['image'], '', $stdout, $stderr);
if(!empty($stdout)) { if(!empty($stdout)) {
Console::log($stdout); Console::log($stdout);
@ -143,11 +141,15 @@ class FunctionsV1
$projectId = $this->args['projectId'] ?? ''; $projectId = $this->args['projectId'] ?? '';
$functionId = $this->args['functionId'] ?? ''; $functionId = $this->args['functionId'] ?? '';
$webhooks = $this->args['webhooks'] ?? [];
$executionId = $this->args['executionId'] ?? ''; $executionId = $this->args['executionId'] ?? '';
$trigger = $this->args['trigger'] ?? ''; $trigger = $this->args['trigger'] ?? '';
$event = $this->args['event'] ?? ''; $event = $this->args['event'] ?? '';
$scheduleOriginal = $this->args['scheduleOriginal'] ?? ''; $scheduleOriginal = $this->args['scheduleOriginal'] ?? '';
$payload = (!empty($this->args['payload'])) ? json_encode($this->args['payload']) : ''; $eventData = (!empty($this->args['eventData'])) ? json_encode($this->args['eventData']) : '';
$data = $this->args['data'] ?? '';
$userId = $this->args['userId'] ?? '';
$jwt = $this->args['jwt'] ?? '';
$database = new Database(); $database = new Database();
$database->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); $database->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
@ -195,7 +197,7 @@ class FunctionsV1
Console::success('Triggered function: '.$event); Console::success('Triggered function: '.$event);
$this->execute('event', $projectId, '', $database, $function, $event, $payload); $this->execute('event', $projectId, '', $database, $function, $event, $eventData, $data, $webhooks, $userId, $jwt);
} }
} }
break; break;
@ -245,14 +247,14 @@ class FunctionsV1
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [ ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $projectId, 'projectId' => $projectId,
'webhooks' => $webhooks,
'functionId' => $function->getId(), 'functionId' => $function->getId(),
'executionId' => null, 'executionId' => null,
'trigger' => 'schedule', 'trigger' => 'schedule',
'scheduleOriginal' => $function->getAttribute('schedule', ''), 'scheduleOriginal' => $function->getAttribute('schedule', ''),
]); // Async task rescheduale ]); // Async task rescheduale
$this->execute($trigger, $projectId, $executionId, $database, $function); $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$eventData*/'', $data, $webhooks, $userId, $jwt);
break; break;
case 'http': case 'http':
@ -264,7 +266,7 @@ class FunctionsV1
throw new Exception('Function not found ('.$functionId.')'); throw new Exception('Function not found ('.$functionId.')');
} }
$this->execute($trigger, $projectId, $executionId, $database, $function); $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$eventData*/'', $data, $webhooks, $userId, $jwt);
break; break;
default: default:
@ -282,15 +284,19 @@ class FunctionsV1
* @param Database $database * @param Database $database
* @param Database $function * @param Database $function
* @param string $event * @param string $event
* @param string $payload * @param string $eventData
* @param string $data
* @param array $webhooks
* @param string $userId
* @param string $jwt
* *
* @return void * @return void
*/ */
public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $payload = ''): void public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $eventData = '', string $data = '', array $webhooks = [], string $userId = '', string $jwt = ''): void
{ {
global $list; global $list;
$environments = Config::getParam('environments'); $runtimes = Config::getParam('runtimes');
Authorization::disable(); Authorization::disable();
$tag = $database->getDocument($function->getAttribute('tag', '')); $tag = $database->getDocument($function->getAttribute('tag', ''));
@ -324,11 +330,11 @@ class FunctionsV1
Authorization::reset(); Authorization::reset();
$environment = (isset($environments[$function->getAttribute('env', '')])) $runtime = (isset($runtimes[$function->getAttribute('env', '')]))
? $environments[$function->getAttribute('env', '')] ? $runtimes[$function->getAttribute('env', '')]
: null; : null;
if(\is_null($environment)) { if(\is_null($runtime)) {
throw new Exception('Environment "'.$function->getAttribute('env', '').' is not supported'); throw new Exception('Environment "'.$function->getAttribute('env', '').' is not supported');
} }
@ -337,10 +343,14 @@ class FunctionsV1
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''), 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
'APPWRITE_FUNCTION_TAG' => $tag->getId(), 'APPWRITE_FUNCTION_TAG' => $tag->getId(),
'APPWRITE_FUNCTION_TRIGGER' => $trigger, 'APPWRITE_FUNCTION_TRIGGER' => $trigger,
'APPWRITE_FUNCTION_ENV_NAME' => $environment['name'], 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
'APPWRITE_FUNCTION_ENV_VERSION' => $environment['version'], 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
'APPWRITE_FUNCTION_EVENT' => $event, 'APPWRITE_FUNCTION_EVENT' => $event,
'APPWRITE_FUNCTION_EVENT_PAYLOAD' => $payload, 'APPWRITE_FUNCTION_EVENT_DATA' => $eventData,
'APPWRITE_FUNCTION_DATA' => $data,
'APPWRITE_FUNCTION_USER_ID' => $userId,
'APPWRITE_FUNCTION_JWT' => $jwt,
'APPWRITE_FUNCTION_PROJECT_ID' => $projectId,
]); ]);
\array_walk($vars, function (&$value, $key) { \array_walk($vars, function (&$value, $key) {
@ -412,7 +422,7 @@ class FunctionsV1
" --volume {$tagPathTargetDir}:/tmp:rw". " --volume {$tagPathTargetDir}:/tmp:rw".
" --workdir /usr/local/src". " --workdir /usr/local/src".
" ".\implode(" ", $vars). " ".\implode(" ", $vars).
" {$environment['image']}". " {$runtime['image']}".
" sh -c 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz && tail -f /dev/null'" " sh -c 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz && tail -f /dev/null'"
, '', $stdout, $stderr, 30); , '', $stdout, $stderr, 30);
@ -469,6 +479,27 @@ class FunctionsV1
throw new Exception('Failed saving execution to DB', 500); throw new Exception('Failed saving execution to DB', 500);
} }
$executionUpdate = new Event('v1-webhooks', 'WebhooksV1');
$executionUpdate
->setParam('projectId', $projectId)
->setParam('userId', $userId)
->setParam('webhooks', $webhooks)
->setParam('event', 'functions.executions.update')
->setParam('eventData', [
'$id' => $execution['$id'],
'functionId' => $execution['functionId'],
'dateCreated' => $execution['dateCreated'],
'trigger' => $execution['trigger'],
'status' => $execution['status'],
'exitCode' => $execution['exitCode'],
'stdout' => $execution['stdout'],
'stderr' => $execution['stderr'],
'time' => $execution['time']
]);
$executionUpdate->trigger();
$usage = new Event('v1-usage', 'UsageV1'); $usage = new Event('v1-usage', 'UsageV1');
$usage $usage

View file

@ -29,7 +29,6 @@ class MailsV1
return; return;
} }
$event = $this->args['event'];
$from = $this->args['from']; $from = $this->args['from'];
$recipient = $this->args['recipient']; $recipient = $this->args['recipient'];
$name = $this->args['name']; $name = $this->args['name'];

View file

@ -26,20 +26,20 @@ class UsageV1
$statsd = $register->get('statsd', true); $statsd = $register->get('statsd', true);
$projectId = $this->args['projectId']; $projectId = $this->args['projectId'] ?? '';
$storage = $this->args['storage']; $storage = $this->args['storage'] ?? 0;
$networkRequestSize = $this->args['networkRequestSize']; $networkRequestSize = $this->args['networkRequestSize'] ?? 0;
$networkResponseSize = $this->args['networkResponseSize']; $networkResponseSize = $this->args['networkResponseSize'] ?? 0;
$httpMethod = $this->args['httpMethod']; $httpMethod = $this->args['httpMethod'] ?? '';
$httpRequest = $this->args['httpRequest']; $httpRequest = $this->args['httpRequest'] ?? 0;
$functionId = $this->args['functionId']; $functionId = $this->args['functionId'];
$functionExecution = $this->args['functionExecution']; $functionExecution = $this->args['functionExecution'] ?? 0;
$functionExecutionTime = $this->args['functionExecutionTime']; $functionExecutionTime = $this->args['functionExecutionTime'] ?? 0;
$functionStatus = $this->args['functionStatus']; $functionStatus = $this->args['functionStatus'] ?? '';
$tags = ",project={$projectId},version=".App::getEnv('_APP_VERSION', 'UNKNOWN').''; $tags = ",project={$projectId},version=".App::getEnv('_APP_VERSION', 'UNKNOWN').'';

View file

@ -2,11 +2,6 @@
use Utopia\App; use Utopia\App;
use Utopia\CLI\Console; use Utopia\CLI\Console;
use Utopia\Config\Config;
use Appwrite\Database\Database;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Database\Validator\Authorization;
require_once __DIR__.'/../init.php'; require_once __DIR__.'/../init.php';
@ -24,34 +19,16 @@ class WebhooksV1
public function perform() public function perform()
{ {
global $register;
$consoleDB = new Database();
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$consoleDB->setNamespace('app_console'); // Main DB
$consoleDB->setMocks(Config::getParam('collections', []));
$errors = []; $errors = [];
// Event // Event
$projectId = $this->args['projectId'] ?? ''; $projectId = $this->args['projectId'] ?? '';
$webhooks = $this->args['webhooks'] ?? [];
$userId = $this->args['userId'] ?? ''; $userId = $this->args['userId'] ?? '';
$event = $this->args['event'] ?? ''; $event = $this->args['event'] ?? '';
$payload = \json_encode($this->args['payload']); $eventData = \json_encode($this->args['eventData']);
// Webhook foreach ($webhooks as $webhook) {
Authorization::disable();
$project = $consoleDB->getDocument($projectId);
Authorization::reset();
if (\is_null($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS !== $project->getCollection()) {
throw new Exception('Project Not Found');
}
foreach ($project->getAttribute('webhooks', []) as $webhook) {
if (!(isset($webhook['events']) && \is_array($webhook['events']) && \in_array($event, $webhook['events']))) { if (!(isset($webhook['events']) && \is_array($webhook['events']) && \in_array($event, $webhook['events']))) {
continue; continue;
} }
@ -67,7 +44,7 @@ class WebhooksV1
$ch = \curl_init($url); $ch = \curl_init($url);
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
\curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); \curl_setopt($ch, CURLOPT_POSTFIELDS, $eventData);
\curl_setopt($ch, CURLOPT_HEADER, 0); \curl_setopt($ch, CURLOPT_HEADER, 0);
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); \curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(APP_USERAGENT, \curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(APP_USERAGENT,
@ -79,7 +56,7 @@ class WebhooksV1
CURLOPT_HTTPHEADER, CURLOPT_HTTPHEADER,
[ [
'Content-Type: application/json', 'Content-Type: application/json',
'Content-Length: '.\strlen($payload), 'Content-Length: '.\strlen($eventData),
'X-'.APP_NAME.'-Webhook-Id: '.$id, 'X-'.APP_NAME.'-Webhook-Id: '.$id,
'X-'.APP_NAME.'-Webhook-Event: '.$event, 'X-'.APP_NAME.'-Webhook-Event: '.$event,
'X-'.APP_NAME.'-Webhook-Name: '.$name, 'X-'.APP_NAME.'-Webhook-Name: '.$name,

View file

@ -21,7 +21,7 @@
} }
}, },
"require": { "require": {
"php": ">=7.4.0", "php": ">=8.0.0",
"ext-curl": "*", "ext-curl": "*",
"ext-imagick": "*", "ext-imagick": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
@ -35,52 +35,44 @@
"ext-zlib": "*", "ext-zlib": "*",
"ext-sockets": "*", "ext-sockets": "*",
"appwrite/php-clamav": "1.0.*", "appwrite/php-clamav": "1.1.*",
"appwrite/php-runtimes": "0.2.*",
"utopia-php/framework": "0.12.*", "utopia-php/framework": "0.14.*",
"utopia-php/abuse": "0.4.*", "utopia-php/abuse": "0.4.*",
"utopia-php/analytics": "0.2.*", "utopia-php/analytics": "0.2.*",
"utopia-php/audit": "0.5.*", "utopia-php/audit": "0.5.*",
"utopia-php/cache": "0.2.*", "utopia-php/cache": "0.2.*",
"utopia-php/cli": "0.10.0", "utopia-php/cli": "0.11.*",
"utopia-php/config": "0.2.*", "utopia-php/config": "0.2.*",
"utopia-php/locale": "0.3.*", "utopia-php/locale": "0.3.*",
"utopia-php/registry": "0.2.*", "utopia-php/registry": "0.4.*",
"utopia-php/preloader": "0.2.*", "utopia-php/preloader": "0.2.*",
"utopia-php/domains": "0.2.*", "utopia-php/domains": "1.1.*",
"utopia-php/swoole": "0.2.*", "utopia-php/swoole": "0.2.*",
"utopia-php/system": "0.4.*", "utopia-php/storage": "0.5.*",
"utopia-php/storage": "0.4.*", "utopia-php/image": "0.2.*",
"utopia-php/image": "0.1.*",
"resque/php-resque": "1.3.6", "resque/php-resque": "1.3.6",
"matomo/device-detector": "4.1.0", "matomo/device-detector": "4.2.2",
"dragonmantank/cron-expression": "3.1.0", "dragonmantank/cron-expression": "3.1.0",
"influxdb/influxdb-php": "1.15.2", "influxdb/influxdb-php": "1.15.2",
"phpmailer/phpmailer": "6.4.1", "phpmailer/phpmailer": "6.4.1",
"chillerlan/php-qrcode": "4.3.0", "chillerlan/php-qrcode": "4.3.0",
"adhocore/jwt": "1.1.2", "adhocore/jwt": "1.1.2",
"slickdeals/statsd": "~3.0" "slickdeals/statsd": "3.0.2"
}, },
"require-dev": { "require-dev": {
"appwrite/sdk-generator": "0.9.0", "appwrite/sdk-generator": "0.10.1",
"phpunit/phpunit": "9.4.2", "swoole/ide-helper": "4.6.6",
"swoole/ide-helper": "4.5.5", "phpunit/phpunit": "9.5.4",
"vimeo/psalm": "4.1.1" "vimeo/psalm": "4.7.2"
}, },
"repositories": [
{
"type": "git",
"url": "https://github.com/appwrite/sdk-generator"
}
],
"provide": { "provide": {
"ext-phpiredis": "*" "ext-phpiredis": "*"
}, },
"minimum-stability": "dev",
"config": { "config": {
"platform": { "platform": {
"php": "7.4" "php": "8.0"
} }
} }
} }

993
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -71,10 +71,14 @@ services:
depends_on: depends_on:
- mariadb - mariadb
- redis - redis
- clamav # - clamav
- influxdb - influxdb
environment: environment:
- _APP_ENV - _APP_ENV
- _APP_LOCALE
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_WHITELIST_IPS
- _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS - _APP_SYSTEM_EMAIL_ADDRESS
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
@ -110,7 +114,7 @@ services:
- _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP - _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_ENVS - _APP_FUNCTIONS_RUNTIMES
appwrite-worker-usage: appwrite-worker-usage:
entrypoint: worker-usage entrypoint: worker-usage
@ -180,11 +184,6 @@ services:
- _APP_REDIS_PORT - _APP_REDIS_PORT
- _APP_REDIS_USER - _APP_REDIS_USER
- _APP_REDIS_PASS - _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-worker-tasks: appwrite-worker-tasks:
entrypoint: worker-tasks entrypoint: worker-tasks
@ -201,6 +200,7 @@ services:
- mariadb - mariadb
environment: environment:
- _APP_ENV - _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_REDIS_HOST - _APP_REDIS_HOST
- _APP_REDIS_PORT - _APP_REDIS_PORT
@ -282,12 +282,12 @@ services:
- /tmp:/tmp:rw - /tmp:/tmp:rw
- ./app:/usr/src/code/app - ./app:/usr/src/code/app
- ./src:/usr/src/code/src - ./src:/usr/src/code/src
- ./docker:/usr/src/code/docker
depends_on: depends_on:
- redis - redis
- mariadb - mariadb
environment: environment:
- _APP_ENV - _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST - _APP_REDIS_HOST
- _APP_REDIS_PORT - _APP_REDIS_PORT
- _APP_REDIS_USER - _APP_REDIS_USER
@ -299,6 +299,7 @@ services:
- _APP_DB_PASS - _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT - _APP_FUNCTIONS_TIMEOUT
- _APP_FUNCTIONS_CONTAINERS - _APP_FUNCTIONS_CONTAINERS
- _APP_FUNCTIONS_RUNTIMES
- _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP - _APP_FUNCTIONS_MEMORY_SWAP
@ -413,16 +414,16 @@ services:
volumes: volumes:
- appwrite-redis:/data:rw - appwrite-redis:/data:rw
clamav: # clamav:
image: appwrite/clamav:1.2.0 # image: appwrite/clamav:1.2.0
container_name: appwrite-clamav # container_name: appwrite-clamav
networks: # networks:
- appwrite # - appwrite
volumes: # volumes:
- appwrite-uploads:/storage/uploads # - appwrite-uploads:/storage/uploads
influxdb: influxdb:
image: influxdb:1.8-alpine image: appwrite/influxdb:1.0.0
container_name: appwrite-influxdb container_name: appwrite-influxdb
networks: networks:
- appwrite - appwrite
@ -430,7 +431,7 @@ services:
- appwrite-influxdb:/var/lib/influxdb:rw - appwrite-influxdb:/var/lib/influxdb:rw
telegraf: telegraf:
image: appwrite/telegraf:1.0.0 image: appwrite/telegraf:1.1.0
container_name: appwrite-telegraf container_name: appwrite-telegraf
networks: networks:
- appwrite - appwrite
@ -449,16 +450,16 @@ services:
# Webgrind - A nice UI for exploring and debugging code-level stuff # Webgrind - A nice UI for exploring and debugging code-level stuff
maildev: # used mainly for dev tests maildev: # used mainly for dev tests
image: djfarrelly/maildev image: appwrite/mailcatcher:1.0.0
container_name: appwrite-maildev container_name: appwrite-mailcatcher
ports: ports:
- '9503:80' - '9503:1080'
networks: networks:
- appwrite - appwrite
request-catcher: # used mainly for dev tests request-catcher: # used mainly for dev tests
image: smarterdm/http-request-catcher image: appwrite/requestcatcher:1.0.0
container_name: appwrite-request-catcher container_name: appwrite-requestcatcher
ports: ports:
- '9504:5000' - '9504:5000'
networks: networks:
@ -466,6 +467,7 @@ services:
adminer: adminer:
image: adminer image: adminer
container_name: appwrite-adminer
restart: always restart: always
ports: ports:
- 9505:8080 - 9505:8080

View file

@ -1,13 +0,0 @@
# Appwrite Functions Environments
Docker based enviornments for Appwrite Functions. You can use this Docker images to locally tests your functions by executing them on your local desktop or server.
All the supported enviornments are based on Docker Alpine images.
## Build
Build envs for all supported cloud functions (multicore builds)
```bash
bash ./docker/environments/build.sh
```

View file

@ -1,40 +0,0 @@
echo 'Starting build...'
echo 'Deno 1.2...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-deno-1.2:1.0.0 ./docker/environments/deno-1.2/ --push
echo 'Deno 1.5...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-deno-1.5:1.0.0 ./docker/environments/deno-1.5/ --push
echo 'Deno 1.6...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-deno-1.6:1.0.0 ./docker/environments/deno-1.6/ --push
echo 'Node 14.5...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le -t appwrite/env-node-14.5:1.0.0 ./docker/environments/node-14.5/ --push
echo 'Node 15.5...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le -t appwrite/env-node-15.5:1.0.0 ./docker/environments/node-15.5/ --push
echo 'PHP 7.4...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-php-7.4:1.0.0 ./docker/environments/php-7.4/ --push
echo 'PHP 8.0...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-php-8.0:1.0.0 ./docker/environments/php-8.0/ --push
echo 'Python 3.8...'
docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-python-3.8:1.0.0 ./docker/environments/python-3.8/ --push
echo 'Ruby 2.7...'
docker buildx build --platform linux/amd64,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-ruby-2.7:1.0.2 ./docker/environments/ruby-2.7/ --push
echo 'Ruby 3.0...'
docker buildx build --platform linux/amd64,linux/arm64,linux/386,linux/ppc64le -t appwrite/env-ruby-3.0:1.0.0 ./docker/environments/ruby-3.0/ --push
echo 'Dart 2.10...'
docker buildx build --platform linux/amd64 -t appwrite/env-dart-2.10:1.0.0 ./docker/environments/dart-2.10/ --push
echo '.NET 3.1...'
docker buildx build --platform linux/amd64,linux/arm64 -t appwrite/env-dotnet-3.1:1.0.0 ./docker/environments/dotnet-3.1/ --push
echo '.NET 5.0...'
docker buildx build --platform linux/amd64,linux/arm64 -t appwrite/env-dotnet-5.0:1.0.0 ./docker/environments/dotnet-5.0/ --push

View file

@ -1,9 +0,0 @@
FROM google/dart:2.10
LABEL maintainer="team@appwrite.io"
RUN apt-get update -y && apt-get install -y tar
WORKDIR /usr/local/src/
ENV PUB_CACHE=/usr/local/src/.appwrite

View file

@ -1,11 +0,0 @@
FROM hayd/deno:alpine-1.2.0
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/
ENV DENO_DIR=/usr/local/src/.appwrite

View file

@ -1,11 +0,0 @@
FROM hayd/deno:alpine-1.5.0
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/
ENV DENO_DIR=/usr/local/src/.appwrite

View file

@ -1,11 +0,0 @@
FROM hayd/deno:alpine-1.6.2
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/
ENV DENO_DIR=/usr/local/src/.appwrite

View file

@ -1,7 +0,0 @@
FROM mcr.microsoft.com/dotnet/runtime:3.1-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
WORKDIR /usr/local/src/

View file

@ -1,7 +0,0 @@
FROM mcr.microsoft.com/dotnet/runtime:5.0-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
WORKDIR /usr/local/src/

View file

@ -1,9 +0,0 @@
FROM node:14.5-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/

View file

@ -1,9 +0,0 @@
FROM node:15.5-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/

View file

@ -1,9 +0,0 @@
FROM php:7.4-cli-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/

View file

@ -1,9 +0,0 @@
FROM php:8.0-cli-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/

View file

@ -1,11 +0,0 @@
FROM python:3.8-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/
ENV PYTHONPATH "${PYTHONPATH}:/usr/local/src/.appwrite"

View file

@ -1,12 +0,0 @@
FROM ruby:2.7-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/
ENV GEM_PATH=/usr/local/src/.appwrite
ENV GEM_SPEC_CACHE=/usr/local/src/.appwrite/specs

View file

@ -1,12 +0,0 @@
FROM ruby:3.0-alpine
LABEL maintainer="team@appwrite.io"
RUN apk add tar
RUN mkdir /usr/local/src
WORKDIR /usr/local/src/
ENV GEM_PATH=/usr/local/src/.appwrite
ENV GEM_SPEC_CACHE=/usr/local/src/.appwrite/specs

View file

@ -0,0 +1,15 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
let promise = sdk.projects.updateAuthLimit('[PROJECT_ID]', '');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,15 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
let promise = sdk.projects.updateAuthStatus('[PROJECT_ID]', 'email-password', false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.createAnonymousSession();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.createJWT();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,21 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.createOAuth2Session(
provider: 'amazon',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,22 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.createRecovery(
email: 'email@example.com',
url: 'https://example.com',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,22 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.createSession(
email: 'email@example.com',
password: 'password',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,21 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.createVerification(
url: 'https://example.com',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,22 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.create(
email: 'email@example.com',
password: 'password',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,21 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.deleteSession(
sessionId: '[SESSION_ID]',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.deleteSessions();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.delete();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.getLogs();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.getPrefs();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.getSessions();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,19 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.get();
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,22 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.updateEmail(
email: 'email@example.com',
password: 'password',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,21 @@
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Account account = Account(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
Future result = account.updateName(
name: '[NAME]',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

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