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=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_ADDRESS=team@appwrite.io
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io
@ -16,7 +20,7 @@ _APP_DB_PORT=3306
_APP_DB_SCHEMA=appwrite
_APP_DB_USER=user
_APP_DB_PASS=password
_APP_STORAGE_ANTIVIRUS=enabled
_APP_STORAGE_ANTIVIRUS=disabled
_APP_STORAGE_ANTIVIRUS_HOST=clamav
_APP_STORAGE_ANTIVIRUS_PORT=3310
_APP_INFLUXDB_HOST=influxdb
@ -24,7 +28,7 @@ _APP_INFLUXDB_PORT=8086
_APP_STATSD_HOST=telegraf
_APP_STATSD_PORT=8125
_APP_SMTP_HOST=maildev
_APP_SMTP_PORT=25
_APP_SMTP_PORT=1025
_APP_SMTP_SECURE=
_APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD=

View file

@ -2,6 +2,7 @@ dist: xenial
arch:
- amd64
- arm64
os: linux
@ -10,9 +11,6 @@ language: shell
notifications:
email:
- team@appwrite.io
services:
- docker
before_install:
- curl -fsSL https://get.docker.com | sh
@ -27,6 +25,9 @@ before_install:
- docker --version
- docker buildx create --use
- 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:
- 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
@ -31,6 +78,7 @@
- Force adding a security email on 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
- Custom data strings, userId, and JWT available for cloud functions #967
## Upgrades

View file

@ -278,13 +278,9 @@ Before running the command, make sure you have proper write permissions to the A
```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 .
```
**Build Functions Envs**
**Build Functions Runtimes**
Build envs for all supported cloud functions (multicore builds)
```bash
bash ./docker/environments/build.sh
```
The Runtimes for all supported cloud functions (multicore builds) can be found at the [appwrite/php-runtimes](https://github.com/appwrite/php-runtimes) repository.
## Tests

View file

@ -12,12 +12,13 @@ RUN composer update --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`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 \
PHP_SWOOLE_VERSION=v4.5.8 \
PHP_MAXMINDDB_VERSION=v1.10.0 \
PHP_XDEBUG_VERSION=sdebug_2_9-beta
ENV PHP_REDIS_VERSION=5.3.4 \
PHP_SWOOLE_VERSION=v4.6.6 \
PHP_IMAGICK_VERSION=master \
PHP_YAML_VERSION=2.2.1 \
PHP_MAXMINDDB_VERSION=v1.10.1
RUN \
apk add --no-cache --virtual .deps \
@ -29,38 +30,52 @@ RUN \
git \
zlib-dev \
brotli-dev \
yaml-dev \
imagemagick \
imagemagick-dev \
libmaxminddb-dev
RUN docker-php-ext-install sockets
RUN \
# 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 && \
git checkout $PHP_REDIS_VERSION && \
phpize && \
./configure && \
make && make install && \
cd .. && \
## 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 && \
git checkout $PHP_SWOOLE_VERSION && \
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 && \
cd .. && \
## 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 && \
git checkout $PHP_MAXMINDDB_VERSION && \
cd ext && \
phpize && \
./configure && \
make && make install && \
cd ../..
FROM php:7.4-cli-alpine as final
FROM php:8.0-cli-alpine as final
LABEL maintainer="team@appwrite.io"
@ -68,10 +83,18 @@ ARG VERSION=dev
ENV _APP_SERVER=swoole \
_APP_ENV=production \
_APP_LOCALE=en \
_APP_DOMAIN=localhost \
_APP_DOMAIN_TARGET=localhost \
_APP_HOME=https://appwrite.io \
_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_FORCE_HTTPS=disabled \
_APP_OPENSSL_KEY_V1=your-secret-key \
@ -123,16 +146,14 @@ RUN \
curl-dev \
&& apk add --no-cache \
libstdc++ \
certbot \
brotli-dev \
yaml-dev \
imagemagick \
imagemagick-dev \
libmaxminddb-dev \
certbot \
docker-cli \
docker-compose \
libmaxminddb \
libmaxminddb-dev \
&& pecl install imagick yaml \
&& docker-php-ext-enable imagick yaml \
&& docker-php-ext-install sockets opcache pdo_mysql \
&& apk del .deps \
&& rm -rf /var/cache/apk/*
@ -140,9 +161,11 @@ RUN \
WORKDIR /usr/src/code
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-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-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/swoole.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
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-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
COPY ./app /usr/src/code/app
@ -190,11 +213,15 @@ RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
# Enable Extensions
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=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 "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.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

View file

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

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;
$providers = Config::getParam('providers', []);
$auth = Config::getParam('auth', []);
$collections = [
'console' => [
@ -45,7 +46,7 @@ $collections = [
'legalTaxId' => '',
'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)) : [],
'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 => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
@ -204,7 +205,7 @@ $collections = [
'key' => 'email',
'type' => Database::SYSTEM_VAR_TYPE_EMAIL,
'default' => '',
'required' => true,
'required' => false,
'array' => false,
],
[
@ -222,7 +223,7 @@ $collections = [
'key' => 'password',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => true,
'required' => false,
'array' => false,
],
[
@ -271,6 +272,16 @@ $collections = [
'required' => true,
'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,
'label' => 'Tokens',
@ -293,11 +304,11 @@ $collections = [
],
],
],
Database::SYSTEM_COLLECTION_TOKENS => [
Database::SYSTEM_COLLECTION_SESSIONS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_TOKENS,
'$id' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['*']],
'name' => 'Token',
'name' => 'Session',
'structure' => true,
'rules' => [
[
@ -306,16 +317,34 @@ $collections = [
'key' => 'userId',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'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,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Type',
'key' => 'type',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => null,
'required' => true,
'label' => 'Provider Token',
'key' => 'providerToken',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => 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 => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
@ -679,6 +771,14 @@ $collections = [
'default' => '',
'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,
'label' => 'Webhooks',
@ -1172,6 +1272,24 @@ $collections = [
'required' => 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 => [
@ -1617,25 +1735,16 @@ foreach ($providers as $index => $provider) {
'array' => false,
'filter' => ['encrypt'],
];
}
$collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [
foreach ($auth as $index => $method) {
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OAuth2 '.\ucfirst($index).' ID',
'key' => 'oauth2'.\ucfirst($index),
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'label' => $method['name'] || '',
'key' => $method['key'] || '',
'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => true,
'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' => [
'description' => 'This event triggers when a database document is created.',
'model' => Response::MODEL_ANY,
'model' => Response::MODEL_DOCUMENT,
'note' => '',
],
'database.documents.update' => [
'description' => 'This event triggers when a database document is updated.',
'model' => Response::MODEL_ANY,
'model' => Response::MODEL_DOCUMENT,
'note' => '',
],
'database.documents.delete' => [
'description' => 'This event triggers when a database document is deleted.',
'model' => Response::MODEL_ANY,
'model' => Response::MODEL_DOCUMENT,
'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' => [
'description' => 'This event triggers when a storage file is created.',
'model' => Response::MODEL_FILE,
@ -157,6 +197,11 @@ return [
'model' => Response::MODEL_MEMBERSHIP,
'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' => [
'description' => 'This event triggers when a team memberships status is updated.',
'model' => Response::MODEL_MEMBERSHIP,
@ -167,4 +212,4 @@ return [
'model' => Response::MODEL_MEMBERSHIP,
'note' => 'version >= 0.7',
],
];
];

View file

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

View file

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

View file

@ -60,8 +60,6 @@ return [
'files.read',
'locale.read',
'avatars.read',
'execution.read',
'execution.write',
],
],
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',
'required' => false,
'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',
@ -22,14 +32,16 @@ return [
'default' => 'enabled',
'required' => false,
'question' => '',
'filter' => ''
],
[
'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' => '',
'default' => 'enabled',
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_OPENSSL_KEY_V1',
@ -38,6 +50,7 @@ return [
'default' => 'your-secret-key',
'required' => true,
'question' => 'Choose a secret API key, make sure to make a backup of your key in a secure location',
'filter' => 'token'
],
[
'name' => '_APP_DOMAIN',
@ -46,6 +59,7 @@ return [
'default' => 'localhost',
'required' => true,
'question' => 'Enter your Appwrite hostname',
'filter' => ''
],
[
'name' => '_APP_DOMAIN_TARGET',
@ -54,14 +68,25 @@ return [
'default' => 'localhost',
'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.',
'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',
'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' => '',
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
// [
// 'name' => '_APP_CONSOLE_WHITELIST_DOMAINS',
@ -78,6 +103,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SYSTEM_EMAIL_NAME',
@ -86,6 +112,7 @@ return [
'default' => 'Appwrite',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SYSTEM_EMAIL_ADDRESS',
@ -94,6 +121,7 @@ return [
'default' => 'team@appwrite.io',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SYSTEM_RESPONSE_FORMAT',
@ -102,6 +130,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS',
@ -110,6 +139,7 @@ return [
'default' => 'certs@appwrite.io',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_USAGE_STATS',
@ -118,7 +148,8 @@ return [
'default' => 'enabled',
'required' => false,
'question' => '',
],
'filter' => ''
]
],
],
[
@ -132,6 +163,7 @@ return [
'default' => 'redis',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_REDIS_PORT',
@ -140,6 +172,7 @@ return [
'default' => '6379',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_REDIS_USER',
@ -148,6 +181,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_REDIS_PASS',
@ -156,6 +190,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
@ -170,6 +205,7 @@ return [
'default' => 'mariadb',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_PORT',
@ -178,6 +214,7 @@ return [
'default' => '3306',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_SCHEMA',
@ -186,6 +223,7 @@ return [
'default' => 'appwrite',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_USER',
@ -194,6 +232,7 @@ return [
'default' => 'user',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_PASS',
@ -202,6 +241,16 @@ return [
'default' => 'password',
'required' => false,
'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',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_INFLUXDB_PORT',
@ -224,6 +274,7 @@ return [
'default' => '8086',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
@ -238,6 +289,7 @@ return [
'default' => 'telegraf',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_STATSD_PORT',
@ -246,6 +298,7 @@ return [
'default' => '8125',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
@ -260,6 +313,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SMTP_PORT',
@ -268,6 +322,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SMTP_SECURE',
@ -276,6 +331,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SMTP_USERNAME',
@ -284,6 +340,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SMTP_PASSWORD',
@ -292,6 +349,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
@ -306,14 +364,16 @@ return [
'default' => '10000000',
'required' => false,
'question' => '',
'filter' => ''
],
[
'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' => '',
'default' => 'enabled',
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_HOST',
@ -322,6 +382,7 @@ return [
'default' => 'clamav',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_PORT',
@ -330,6 +391,7 @@ return [
'default' => '3310',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
@ -344,6 +406,7 @@ return [
'default' => '900',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_CONTAINERS',
@ -352,6 +415,7 @@ return [
'default' => '10',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_CPUS',
@ -360,6 +424,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
@ -368,6 +433,7 @@ return [
'default' => '256',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_MEMORY_SWAP',
@ -376,14 +442,25 @@ return [
'default' => '256',
'required' => false,
'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',
'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',
'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,
'question' => '',
'filter' => ''
],
],
[
@ -397,6 +474,7 @@ return [
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION',
@ -405,6 +483,7 @@ return [
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
@ -413,6 +492,7 @@ return [
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
@ -421,6 +501,7 @@ return [
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
]
],
],

View file

@ -6,10 +6,10 @@ use Utopia\Exception;
use Utopia\Config\Config;
use Utopia\Validator\Assoc;
use Utopia\Validator\Text;
use Utopia\Validator\Email;
use Appwrite\Network\Validator\Email;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Host;
use Utopia\Validator\URL;
use Appwrite\Network\Validator\Host;
use Appwrite\Network\Validator\URL;
use Utopia\Audit\Audit;
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
use Appwrite\Auth\Auth;
@ -31,10 +31,11 @@ $oauthDefaultFailure = App::getEnv('_APP_HOME').'/auth/oauth2/failure';
App::post('/v1/account')
->desc('Create Account')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('event', 'account.create')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('auth.type', 'emailPassword')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/account/create.md')
@ -60,7 +61,6 @@ App::post('/v1/account')
if ('console' === $project->getId()) {
$whitlistEmails = $project->getAttribute('authWhitelistEmails');
$whitlistIPs = $project->getAttribute('authWhitelistIPs');
$whitlistDomains = $project->getAttribute('authWhitelistDomains');
if (!empty($whitlistEmails) && !\in_array($email, $whitlistEmails)) {
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)) {
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)) {
throw new Exception('Console registration is restricted to specific domains. Contact your administrator for more information.', 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);
}
}
@ -109,7 +121,7 @@ App::post('/v1/account')
throw new Exception('Account already exists', 409);
}
Authorization::enable();
Authorization::reset();
Authorization::unsetRole('role:'.Auth::USER_ROLE_GUEST);
Authorization::setRole('user:'.$user->getId());
@ -133,10 +145,11 @@ App::post('/v1/account')
App::post('/v1/account/sessions')
->desc('Create Account Session')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('event', 'account.sessions.create')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('auth.type', 'emailPassword')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createSession')
->label('sdk.description', '/docs/references/account/create-session.md')
@ -190,10 +203,11 @@ App::post('/v1/account/sessions')
$secret = Auth::tokenGenerator();
$session = new Document(array_merge(
[
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$collection' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$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
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
@ -210,7 +224,7 @@ App::post('/v1/account/sessions')
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());
@ -251,7 +265,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
->groups(['api', 'account'])
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createOAuth2Session')
->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);
}
$current = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
$current = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret);
if ($current) {
$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,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'oauth2'.\ucfirst($provider).'='.$oauth2ID,
'sessions.provider='.$provider,
'sessions.providerUid='.$oauth2ID
],
]) : $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();
try {
@ -478,7 +509,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'emailVerification' => true,
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth2 provider
'password' => Auth::passwordHash(Auth::passwordGenerator()),
'passwordUpdate' => \time(),
'passwordUpdate' => 0,
'registration' => \time(),
'reset' => false,
'name' => $name,
@ -487,7 +518,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
throw new Exception('Account already exists', 409);
}
Authorization::enable();
Authorization::reset();
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
@ -506,10 +537,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$secret = Auth::tokenGenerator();
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document(array_merge([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$collection' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]],
'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
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
@ -517,11 +550,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
], $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
->setAttribute('oauth2'.\ucfirst($provider), $oauth2ID)
->setAttribute('oauth2'.\ucfirst($provider).'AccessToken', $accessToken)
->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());
@ -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')
->desc('Create Account JWT')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('scope', 'account')
->label('docs', false) // Hidden for now - private beta
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('auth.type', 'jwt')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createJWT')
->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\Database\Document $user */
$tokens = $user->getAttribute('tokens', []);
$session = new Document();
$sessions = $user->getAttribute('sessions', []);
$current = 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;
foreach ($sessions as $session) {
/** @var Appwrite\Database\Document $session */
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);
}
@ -606,7 +792,7 @@ App::post('/v1/account/jwt')
// 'scopes' => ['user'],
// 'iss' => 'http://api.mysite.com',
'userId' => $user->getId(),
'sessionId' => $session->getId(),
'sessionId' => $current->getId(),
])]), Response::MODEL_JWT);
});
@ -614,7 +800,7 @@ App::get('/v1/account')
->desc('Get Account')
->groups(['api', '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.method', 'get')
->label('sdk.description', '/docs/references/account/get.md')
@ -634,13 +820,13 @@ App::get('/v1/account/prefs')
->desc('Get Account Preferences')
->groups(['api', '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.method', 'getPrefs')
->label('sdk.description', '/docs/references/account/get-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->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('user')
->action(function ($response, $user) {
@ -649,14 +835,14 @@ App::get('/v1/account/prefs')
$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')
->desc('Get Account Sessions')
->groups(['api', '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.method', 'getSessions')
->label('sdk.description', '/docs/references/account/get-sessions.md')
@ -671,22 +857,19 @@ App::get('/v1/account/sessions')
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
$tokens = $user->getAttribute('tokens', []);
$sessions = [];
$sessions = $user->getAttribute('sessions', []);
$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 */
if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) {
continue;
}
foreach ($sessions as $key => $session) {
/** @var Document $session */
$token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('countryCode'))]))
? $countries[strtoupper($token->getAttribute('countryCode'))]
$session->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))]))
? $countries[strtoupper($session->getAttribute('countryCode'))]
: $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([
@ -699,7 +882,7 @@ App::get('/v1/account/logs')
->desc('Get Account Logs')
->groups(['api', '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.method', 'getLogs')
->label('sdk.description', '/docs/references/account/get-logs.md')
@ -776,7 +959,7 @@ App::patch('/v1/account/name')
->groups(['api', 'account'])
->label('event', 'account.update.name')
->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.method', 'updateName')
->label('sdk.description', '/docs/references/account/update-name.md')
@ -816,7 +999,7 @@ App::patch('/v1/account/password')
->groups(['api', 'account'])
->label('event', 'account.update.password')
->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.method', 'updatePassword')
->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.model', Response::MODEL_USER)
->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('user')
->inject('projectDB')
@ -835,12 +1018,14 @@ App::patch('/v1/account/password')
/** @var Appwrite\Database\Database $projectDB */
/** @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);
}
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'password' => Auth::passwordHash($password),
'passwordUpdate' => \time(),
]));
if (false === $user) {
@ -861,7 +1046,7 @@ App::patch('/v1/account/email')
->groups(['api', 'account'])
->label('event', 'account.update.email')
->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.method', 'updateEmail')
->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\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);
}
@ -898,10 +1088,14 @@ App::patch('/v1/account/email')
// TODO after this user needs to confirm mail again
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'email' => $email,
'emailVerification' => false,
]));
$user = $projectDB->updateDocument(\array_merge(
$user->getArrayCopy(),
($isAnonymousUser ? [ 'password' => Auth::passwordHash($password) ] : []),
[
'email' => $email,
'emailVerification' => false,
]
));
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
@ -921,13 +1115,13 @@ App::patch('/v1/account/prefs')
->groups(['api', 'account'])
->label('event', 'account.update.prefs')
->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.method', 'updatePrefs')
->label('sdk.description', '/docs/references/account/update-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->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.')
->inject('response')
->inject('user')
@ -960,7 +1154,7 @@ App::delete('/v1/account')
->groups(['api', 'account'])
->label('event', 'account.delete')
->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.method', 'delete')
->label('sdk.description', '/docs/references/account/delete.md')
@ -1005,7 +1199,7 @@ App::delete('/v1/account')
;
$events
->setParam('payload', $response->output($user, Response::MODEL_USER))
->setParam('eventData', $response->output($user, Response::MODEL_USER))
;
if (!Config::getParam('domainVerification')) {
@ -1026,7 +1220,7 @@ App::delete('/v1/account/sessions/:sessionId')
->groups(['api', 'account'])
->label('scope', 'account')
->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.method', 'deleteSession')
->label('sdk.description', '/docs/references/account/delete-session.md')
@ -1050,14 +1244,16 @@ App::delete('/v1/account/sessions/:sessionId')
$protocol = $request->getProtocol();
$sessionId = ($sessionId === 'current')
? Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
: $sessionId;
$tokens = $user->getAttribute('tokens', []);
$sessions = $user->getAttribute('sessions', []);
foreach ($tokens as $token) { /* @var $token Document */
if (($sessionId == $token->getId()) && Auth::TOKEN_TYPE_LOGIN == $token->getAttribute('type')) {
if (!$projectDB->deleteDocument($token->getId())) {
foreach ($sessions as $session) {
/** @var Document $session */
if (($sessionId == $session->getId())) {
if (!$projectDB->deleteDocument($session->getId())) {
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
$token->setAttribute('current', true);
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$session->setAttribute('current', true);
$response
->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
@ -1085,7 +1281,7 @@ App::delete('/v1/account/sessions/:sessionId')
}
$events
->setParam('payload', $response->output($token, Response::MODEL_SESSION))
->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
;
return $response->noContent();
@ -1100,7 +1296,7 @@ App::delete('/v1/account/sessions')
->groups(['api', 'account'])
->label('scope', 'account')
->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.method', 'deleteSessions')
->label('sdk.description', '/docs/references/account/delete-sessions.md')
@ -1122,10 +1318,12 @@ App::delete('/v1/account/sessions')
/** @var Appwrite\Event\Event $events */
$protocol = $request->getProtocol();
$tokens = $user->getAttribute('tokens', []);
$sessions = $user->getAttribute('sessions', []);
foreach ($tokens as $token) { /* @var $token Document */
if (!$projectDB->deleteDocument($token->getId())) {
foreach ($sessions as $session) {
/** @var Document $session */
if (!$projectDB->deleteDocument($session->getId())) {
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
$token->setAttribute('current', true);
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$session->setAttribute('current', true);
$response
->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'))
@ -1153,9 +1351,9 @@ App::delete('/v1/account/sessions')
}
$events
->setParam('payload', $response->output(new Document([
'sum' => count($tokens),
'sessions' => $tokens
->setParam('eventData', $response->output(new Document([
'sum' => count($sessions),
'sessions' => $sessions
]), Response::MODEL_SESSION_LIST))
;
@ -1167,7 +1365,7 @@ App::post('/v1/account/recovery')
->groups(['api', 'account'])
->label('scope', 'public')
->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.method', 'createRecovery')
->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 $events */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$profile = $projectDB->getCollectionFirst([ // Get user by email address
@ -1278,7 +1476,7 @@ App::post('/v1/account/recovery')
;
$events
->setParam('payload',
->setParam('eventData',
$response->output($recovery->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
@ -1286,7 +1484,7 @@ App::post('/v1/account/recovery')
$recovery // Hide secret for clients, sp
->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $secret : '');
($isPrivilegedUser || $isAppUser) ? $secret : '');
$audits
->setParam('userId', $profile->getId())
@ -1305,7 +1503,7 @@ App::put('/v1/account/recovery')
->groups(['api', 'account'])
->label('scope', 'public')
->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.method', 'updateRecovery')
->label('sdk.description', '/docs/references/account/update-recovery.md')
@ -1384,7 +1582,7 @@ App::post('/v1/account/verification')
->groups(['api', 'account'])
->label('scope', 'account')
->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.method', 'createVerification')
->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 $mails */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$verificationSecret = Auth::tokenGenerator();
@ -1481,7 +1679,7 @@ App::post('/v1/account/verification')
;
$events
->setParam('payload',
->setParam('eventData',
$response->output($verification->setAttribute('secret', $verificationSecret),
Response::MODEL_TOKEN
))
@ -1489,7 +1687,7 @@ App::post('/v1/account/verification')
$verification // Hide secret for clients, sp
->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $verificationSecret : '');
($isPrivilegedUser || $isAppUser) ? $verificationSecret : '');
$audits
->setParam('userId', $user->getId())
@ -1508,7 +1706,7 @@ App::put('/v1/account/verification')
->groups(['api', 'account'])
->label('scope', 'public')
->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.method', 'updateVerification')
->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\Range;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Appwrite\Network\Validator\URL;
use Utopia\Validator\WhiteList;
$avatarCallback = function ($type, $code, $width, $height, $quality, $response) {
@ -38,7 +38,7 @@ $avatarCallback = function ($type, $code, $width, $height, $quality, $response)
$output = 'png';
$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];
$type = 'png';
@ -83,7 +83,7 @@ App::get('/v1/avatars/credit-cards/:code')
->desc('Get Credit Card Icon')
->groups(['api', 'avatars'])
->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.method', 'getCreditCard')
->label('sdk.methodType', 'location')
@ -103,7 +103,7 @@ App::get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon')
->groups(['api', 'avatars'])
->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.method', 'getBrowser')
->label('sdk.methodType', 'location')
@ -123,7 +123,7 @@ App::get('/v1/avatars/flags/:code')
->desc('Get Country Flag')
->groups(['api', 'avatars'])
->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.method', 'getFlag')
->label('sdk.methodType', 'location')
@ -143,7 +143,7 @@ App::get('/v1/avatars/image')
->desc('Get Image from URL')
->groups(['api', 'avatars'])
->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.method', 'getImage')
->label('sdk.methodType', 'location')
@ -212,7 +212,7 @@ App::get('/v1/avatars/favicon')
->desc('Get Favicon')
->groups(['api', 'avatars'])
->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.method', 'getFavicon')
->label('sdk.methodType', 'location')
@ -365,7 +365,7 @@ App::get('/v1/avatars/qr')
->desc('Get QR Code')
->groups(['api', 'avatars'])
->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.method', 'getQR')
->label('sdk.methodType', 'location')
@ -408,7 +408,7 @@ App::get('/v1/avatars/initials')
->desc('Get User Initials')
->groups(['api', 'avatars'])
->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.method', 'getInitials')
->label('sdk.methodType', 'location')

View file

@ -24,7 +24,7 @@ App::post('/v1/database/collections')
->label('event', 'database.collections.create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createCollection')
->label('sdk.description', '/docs/references/database/create-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -96,7 +96,7 @@ App::get('/v1/database/collections')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'listCollections')
->label('sdk.description', '/docs/references/database/list-collections.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -133,7 +133,7 @@ App::get('/v1/database/collections/:collectionId')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'getCollection')
->label('sdk.description', '/docs/references/database/get-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -161,7 +161,7 @@ App::put('/v1/database/collections/:collectionId')
->label('scope', 'collections.write')
->label('event', 'database.collections.update')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'updateCollection')
->label('sdk.description', '/docs/references/database/update-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -238,7 +238,7 @@ App::delete('/v1/database/collections/:collectionId')
->label('scope', 'collections.write')
->label('event', 'database.collections.delete')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'deleteCollection')
->label('sdk.description', '/docs/references/database/delete-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -271,7 +271,7 @@ App::delete('/v1/database/collections/:collectionId')
;
$events
->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION))
->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION))
;
$audits
@ -289,12 +289,12 @@ App::post('/v1/database/collections/:collectionId/documents')
->label('event', 'database.documents.create')
->label('scope', 'documents.write')
->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.description', '/docs/references/database/create-document.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->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('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)
@ -401,7 +401,7 @@ App::post('/v1/database/collections/:collectionId/documents')
$response
->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'])
->label('scope', 'documents.read')
->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.description', '/docs/references/database/list-documents.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -473,12 +473,12 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
->groups(['api', 'database'])
->label('scope', 'documents.read')
->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.description', '/docs/references/database/get-document.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->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('documentId', null, new UID(), 'Document unique ID.')
->inject('response')
@ -494,7 +494,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
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')
@ -503,12 +503,12 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->label('event', 'database.documents.update')
->label('scope', 'documents.write')
->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.description', '/docs/references/database/update-document.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->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('documentId', null, new UID(), 'Document unique ID.')
->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())
;
$response->dynamic($data, Response::MODEL_ANY);
$response->dynamic($data, Response::MODEL_DOCUMENT);
});
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('event', 'database.documents.delete')
->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.description', '/docs/references/database/delete-document.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -614,9 +614,9 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
}
$events
->setParam('payload', $response->output($document, Response::MODEL_ANY))
->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT))
;
$audits
->setParam('event', 'database.documents.delete')
->setParam('resource', 'database/document/'.$document->getId())

View file

@ -1,5 +1,7 @@
<?php
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
@ -27,7 +29,8 @@ App::post('/v1/functions')
->groups(['api', 'functions'])
->desc('Create Function')
->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.method', 'create')
->label('sdk.description', '/docs/references/functions/create-function.md')
@ -36,7 +39,7 @@ App::post('/v1/functions')
->label('sdk.response.model', Response::MODEL_FUNCTION)
->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('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('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
@ -80,7 +83,7 @@ App::get('/v1/functions')
->groups(['api', 'functions'])
->desc('List Functions')
->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/functions/list-functions.md')
@ -117,7 +120,7 @@ App::get('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Get Function')
->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/functions/get-function.md')
@ -144,7 +147,7 @@ App::get('/v1/functions/:functionId/usage')
->desc('Get Function Usage')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_CONSOLE])
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getUsage')
->param('functionId', '', new UID(), 'Function unique ID.')
@ -265,7 +268,8 @@ App::put('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Update Function')
->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.method', 'update')
->label('sdk.description', '/docs/references/functions/update-function.md')
@ -317,6 +321,7 @@ App::put('/v1/functions/:functionId')
if ($next && $schedule !== $original) {
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(),
'executionId' => null,
'trigger' => 'schedule',
@ -330,7 +335,8 @@ App::patch('/v1/functions/:functionId/tag')
->groups(['api', 'functions'])
->desc('Update Function Tag')
->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.method', 'updateTag')
->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
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(),
'executionId' => null,
'trigger' => 'schedule',
@ -387,7 +394,8 @@ App::delete('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Delete Function')
->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.method', 'delete')
->label('sdk.description', '/docs/references/functions/delete-function.md')
@ -424,7 +432,8 @@ App::post('/v1/functions/:functionId/tags')
->groups(['api', 'functions'])
->desc('Create Tag')
->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.method', 'createTag')
->label('sdk.description', '/docs/references/functions/create-tag.md')
@ -518,7 +527,7 @@ App::get('/v1/functions/:functionId/tags')
->groups(['api', 'functions'])
->desc('List Tags')
->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listTags')
->label('sdk.description', '/docs/references/functions/list-tags.md')
@ -563,7 +572,7 @@ App::get('/v1/functions/:functionId/tags/:tagId')
->groups(['api', 'functions'])
->desc('Get Tag')
->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getTag')
->label('sdk.description', '/docs/references/functions/get-tag.md')
@ -601,7 +610,8 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
->groups(['api', 'functions'])
->desc('Delete Tag')
->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.method', 'deleteTag')
->label('sdk.description', '/docs/references/functions/delete-tag.md')
@ -662,7 +672,8 @@ App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
->desc('Create Execution')
->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.method', 'createExecution')
->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-time', 60)
->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)
->inject('response')
->inject('project')
->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\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Database\Document $user */
Authorization::disable();
@ -712,7 +726,7 @@ App::post('/v1/functions/:functionId/executions')
$execution = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_EXECUTIONS,
'$permissions' => [
'read' => $function->getPermissions()['execute'] ?? [],
'read' => (!empty($user->getId())) ? ['user:' . $user->getId()] : [],
'write' => [],
],
'dateCreated' => time(),
@ -730,12 +744,37 @@ App::post('/v1/functions/:functionId/executions')
if (false === $execution) {
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', [
'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(),
'executionId' => $execution->getId(),
'trigger' => 'http',
'data' => $data,
'userId' => $user->getId(),
'jwt' => $jwt,
]);
$response
@ -748,7 +787,7 @@ App::get('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
->desc('List Executions')
->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.method', 'listExecutions')
->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\Database\Database $projectDB */
Authorization::disable();
$function = $projectDB->getDocument($functionId);
Authorization::reset();
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404);
@ -793,7 +834,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->groups(['api', 'functions'])
->desc('Get Execution')
->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.method', 'getExecution')
->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\Database\Database $projectDB */
Authorization::disable();
$function = $projectDB->getDocument($functionId);
Authorization::reset();
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404);

View file

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

View file

@ -9,7 +9,7 @@ App::get('/v1/locale')
->desc('Get User Locale')
->groups(['api', 'locale'])
->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.method', 'get')
->label('sdk.description', '/docs/references/locale/get-locale.md')
@ -74,7 +74,7 @@ App::get('/v1/locale/countries')
->desc('List Countries')
->groups(['api', 'locale'])
->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.method', 'getCountries')
->label('sdk.description', '/docs/references/locale/get-countries.md')
@ -106,7 +106,7 @@ App::get('/v1/locale/countries/eu')
->desc('List EU Countries')
->groups(['api', 'locale'])
->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.method', 'getCountriesEU')
->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')
->groups(['api', 'locale'])
->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.method', 'getCountriesPhones')
->label('sdk.description', '/docs/references/locale/get-countries-phones.md')
@ -177,7 +177,7 @@ App::get('/v1/locale/continents')
->desc('List Continents')
->groups(['api', 'locale'])
->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.method', 'getContinents')
->label('sdk.description', '/docs/references/locale/get-continents.md')
@ -208,7 +208,7 @@ App::get('/v1/locale/currencies')
->desc('List Currencies')
->groups(['api', 'locale'])
->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.method', 'getCurrencies')
->label('sdk.description', '/docs/references/locale/get-currencies.md')
@ -233,7 +233,7 @@ App::get('/v1/locale/languages')
->desc('List Languages')
->groups(['api', 'locale'])
->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.method', 'getLanguages')
->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\Text;
use Utopia\Validator\WhiteList;
use Utopia\Validator\URL;
use Appwrite\Network\Validator\URL;
use Utopia\Validator\Range;
use Utopia\Validator\Integer;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Appwrite\Auth\Auth;
@ -24,6 +25,7 @@ App::post('/v1/projects')
->desc('Create Project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'create')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -96,6 +98,7 @@ App::get('/v1/projects')
->desc('List Projects')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'list')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -131,6 +134,7 @@ App::get('/v1/projects/:projectId')
->desc('Get Project')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'get')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -156,6 +160,7 @@ App::get('/v1/projects/:projectId/usage')
->desc('Get Project')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getUsage')
->param('projectId', '', new UID(), 'Project unique ID.')
@ -356,6 +361,7 @@ App::patch('/v1/projects/:projectId')
->desc('Update Project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'update')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -408,6 +414,7 @@ App::patch('/v1/projects/:projectId/oauth2')
->desc('Update Project OAuth2')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateOAuth2')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -441,10 +448,85 @@ App::patch('/v1/projects/:projectId/oauth2')
$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')
->desc('Delete Project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'delete')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -510,6 +592,7 @@ App::post('/v1/projects/:projectId/webhooks')
->desc('Create Webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createWebhook')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -572,6 +655,7 @@ App::get('/v1/projects/:projectId/webhooks')
->desc('List Webhooks')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listWebhooks')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -602,6 +686,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Get Webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getWebhook')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -634,6 +719,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Update Webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateWebhook')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -687,6 +773,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Delete Webhook')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteWebhook')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -724,6 +811,7 @@ App::post('/v1/projects/:projectId/keys')
->desc('Create Key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createKey')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -777,6 +865,7 @@ App::get('/v1/projects/:projectId/keys')
->desc('List Keys')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listKeys')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -807,6 +896,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->desc('Get Key')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getKey')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -836,6 +926,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->desc('Update Key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateKey')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -879,6 +970,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->desc('Delete Key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteKey')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -916,6 +1008,7 @@ App::post('/v1/projects/:projectId/tasks')
->desc('Create Task')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createTask')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -996,6 +1089,7 @@ App::get('/v1/projects/:projectId/tasks')
->desc('List Tasks')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listTasks')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1027,6 +1121,7 @@ App::get('/v1/projects/:projectId/tasks/:taskId')
->desc('Get Task')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getTask')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1059,6 +1154,7 @@ App::put('/v1/projects/:projectId/tasks/:taskId')
->desc('Update Task')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateTask')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1127,6 +1223,7 @@ App::delete('/v1/projects/:projectId/tasks/:taskId')
->desc('Delete Task')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteTask')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -1164,6 +1261,7 @@ App::post('/v1/projects/:projectId/platforms')
->desc('Create Platform')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createPlatform')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -1224,6 +1322,7 @@ App::get('/v1/projects/:projectId/platforms')
->desc('List Platforms')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listPlatforms')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1254,6 +1353,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->desc('Get Platform')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getPlatform')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1286,6 +1386,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->desc('Update Platform')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updatePlatform')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1334,6 +1435,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->desc('Delete Platform')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deletePlatform')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
@ -1371,6 +1473,7 @@ App::post('/v1/projects/:projectId/domains')
->desc('Create Domain')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createDomain')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
@ -1440,6 +1543,7 @@ App::get('/v1/projects/:projectId/domains')
->desc('List Domains')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listDomains')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1470,6 +1574,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
->desc('Get Domain')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getDomain')
->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')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateDomainVerification')
->label('sdk.response.code', Response::STATUS_CODE_OK)
@ -1565,6 +1671,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
->desc('Delete Domain')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteDomain')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)

View file

@ -28,7 +28,7 @@ App::post('/v1/storage/files')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->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.method', 'createFile')
->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));
$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);
}
@ -167,7 +167,7 @@ App::get('/v1/storage/files')
->desc('List Files')
->groups(['api', 'storage'])
->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.method', 'listFiles')
->label('sdk.description', '/docs/references/storage/list-files.md')
@ -204,7 +204,7 @@ App::get('/v1/storage/files/:fileId')
->desc('Get File')
->groups(['api', 'storage'])
->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.method', 'getFile')
->label('sdk.description', '/docs/references/storage/get-file.md')
@ -231,7 +231,7 @@ App::get('/v1/storage/files/:fileId/preview')
->desc('Get File Preview')
->groups(['api', 'storage'])
->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.method', 'getFilePreview')
->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('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('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('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
->inject('request')
->inject('response')
->inject('project')
->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 Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
@ -273,7 +278,7 @@ App::get('/v1/storage/files/:fileId/preview')
$fileLogos = Config::getParam('storage-logos');
$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);
@ -293,7 +298,7 @@ App::get('/v1/storage/files/:fileId/preview')
$cipher = null;
$background = (empty($background)) ? 'eceff1' : $background;
$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();
@ -337,11 +342,28 @@ App::get('/v1/storage/files/:fileId/preview')
$image = new Image($source);
$image->crop((int) $width, (int) $height);
if (!empty($opacity) || $opacity==0) {
$image->setOpacity($opacity);
}
if (!empty($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;
$data = $image->output($output, $quality);
@ -362,7 +384,7 @@ App::get('/v1/storage/files/:fileId/download')
->desc('Get File for Download')
->groups(['api', 'storage'])
->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.method', 'getFileDownload')
->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')
->groups(['api', 'storage'])
->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.method', 'getFileView')
->label('sdk.description', '/docs/references/storage/get-file-view.md')
@ -489,7 +511,7 @@ App::put('/v1/storage/files/:fileId')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->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.method', 'updateFile')
->label('sdk.description', '/docs/references/storage/update-file.md')
@ -538,7 +560,7 @@ App::delete('/v1/storage/files/:fileId')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->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.method', 'deleteFile')
->label('sdk.description', '/docs/references/storage/delete-file.md')
@ -581,7 +603,7 @@ App::delete('/v1/storage/files/:fileId')
;
$events
->setParam('payload', $response->output($file, Response::MODEL_FILE))
->setParam('eventData', $response->output($file, Response::MODEL_FILE))
;
$response->noContent();
@ -590,8 +612,8 @@ App::delete('/v1/storage/files/:fileId')
// App::get('/v1/storage/files/:fileId/scan')
// ->desc('Scan Storage')
// ->groups(['api', 'storage'])
// ->label('scope', 'god')
// ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
// ->label('scope', 'root')
// ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
// ->label('sdk.namespace', 'storage')
// ->label('sdk.method', 'getFileScan')
// ->label('sdk.hide', true)

View file

@ -3,9 +3,9 @@
use Utopia\App;
use Utopia\Exception;
use Utopia\Config\Config;
use Utopia\Validator\Email;
use Appwrite\Network\Validator\Email;
use Utopia\Validator\Text;
use Utopia\Validator\Host;
use Appwrite\Network\Validator\Host;
use Utopia\Validator\Range;
use Utopia\Validator\ArrayList;
use Utopia\Validator\WhiteList;
@ -25,7 +25,7 @@ App::post('/v1/teams')
->groups(['api', 'teams'])
->label('event', 'teams.create')
->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.method', 'create')
->label('sdk.description', '/docs/references/teams/create-team.md')
@ -44,7 +44,7 @@ App::post('/v1/teams')
Authorization::disable();
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$team = $projectDB->createDocument([
@ -54,7 +54,7 @@ App::post('/v1/teams')
'write' => ['team:{self}/owner'],
],
'name' => $name,
'sum' => ($isPreviliggedUser || $isAppUser) ? 0 : 1,
'sum' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
'dateCreated' => \time(),
]);
@ -64,7 +64,7 @@ App::post('/v1/teams')
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([
'$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
'$permissions' => [
@ -100,7 +100,7 @@ App::get('/v1/teams')
->desc('List Teams')
->groups(['api', 'teams'])
->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.method', 'list')
->label('sdk.description', '/docs/references/teams/list-teams.md')
@ -137,7 +137,7 @@ App::get('/v1/teams/:teamId')
->desc('Get Team')
->groups(['api', 'teams'])
->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.method', 'get')
->label('sdk.description', '/docs/references/teams/get-team.md')
@ -165,7 +165,7 @@ App::put('/v1/teams/:teamId')
->groups(['api', 'teams'])
->label('event', 'teams.update')
->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.method', 'update')
->label('sdk.description', '/docs/references/teams/update-team.md')
@ -202,7 +202,7 @@ App::delete('/v1/teams/:teamId')
->groups(['api', 'teams'])
->label('event', 'teams.delete')
->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.method', 'delete')
->label('sdk.description', '/docs/references/teams/delete-team.md')
@ -243,7 +243,7 @@ App::delete('/v1/teams/:teamId')
}
$events
->setParam('payload', $response->output($team, Response::MODEL_TEAM))
->setParam('eventData', $response->output($team, Response::MODEL_TEAM))
;
$response->noContent();
@ -251,16 +251,18 @@ App::delete('/v1/teams/:teamId')
App::post('/v1/teams/:teamId/memberships')
->desc('Create Team Membership')
->groups(['api', 'teams'])
->groups(['api', 'teams', 'auth'])
->label('event', 'teams.memberships.create')
->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.method', 'createMembership')
->label('sdk.description', '/docs/references/teams/create-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->label('abuse-limit', 10)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('email', '', new Email(), 'New team member email.')
->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 $mails */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$name = (empty($name)) ? $email : $name;
@ -292,7 +294,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$memberships = $projectDB->getCollection([
'limit' => 50,
'limit' => 2000,
'offset' => 0,
'filters' => [
'$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
$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();
try {
@ -323,10 +341,16 @@ App::post('/v1/teams/:teamId/memberships')
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'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(),
'reset' => false,
'name' => $name,
'sessions' => [],
'tokens' => [],
], ['email' => $email]);
} 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);
}
@ -368,12 +392,12 @@ App::post('/v1/teams/:teamId/memberships')
'teamId' => $team->getId(),
'roles' => $roles,
'invited' => \time(),
'joined' => ($isPreviliggedUser || $isAppUser) ? \time() : 0,
'confirm' => ($isPreviliggedUser || $isAppUser),
'joined' => ($isPrivilegedUser || $isAppUser) ? \time() : 0,
'confirm' => ($isPrivilegedUser || $isAppUser),
'secret' => Auth::hash($secret),
]);
if ($isPreviliggedUser || $isAppUser) { // Allow admin to create membership
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
Authorization::disable();
$membership = $projectDB->createDocument($membership->getArrayCopy());
@ -400,7 +424,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$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);
$body = new Template(__DIR__.'/../../config/locale/templates/email-base.tpl');
@ -425,9 +449,9 @@ App::post('/v1/teams/:teamId/memberships')
->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
->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('recipient', $email)
->setParam('name', $name)
@ -439,7 +463,7 @@ App::post('/v1/teams/:teamId/memberships')
$audits
->setParam('userId', $invitee->getId())
->setParam('event', 'teams.membership.create')
->setParam('event', 'teams.memberships.create')
->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')
->desc('Get Team Memberships')
->groups(['api', 'teams'])
->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.method', 'getMemberships')
->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);
});
App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->desc('Update Team Membership Status')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.update.status')
->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.method', 'updateMembershipStatus')
->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.model', Response::MODEL_MEMBERSHIP)
->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('secret', '', new Text(256), 'Secret key.')
->inject('request')
@ -527,7 +614,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
->inject('projectDB')
->inject('geodb')
->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 Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
@ -536,7 +623,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
/** @var Appwrite\Event\Event $audits */
$protocol = $request->getProtocol();
$membership = $projectDB->getDocument($inviteId);
$membership = $projectDB->getDocument($membershipId);
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
throw new Exception('Invite not found', 404);
@ -561,7 +648,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
}
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())) {
@ -575,7 +662,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
}
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
@ -595,10 +682,11 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator();
$session = new Document(array_merge([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$collection' => Database::SYSTEM_COLLECTION_SESSIONS,
'$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$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
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
@ -606,7 +694,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
], $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);
@ -630,7 +718,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
$audits
->setParam('userId', $user->getId())
->setParam('event', 'teams.membership.update')
->setParam('event', 'teams.memberships.update.status')
->setParam('resource', 'teams/'.$teamId)
;
@ -651,30 +739,30 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
])), Response::MODEL_MEMBERSHIP);
});
App::delete('/v1/teams/:teamId/memberships/:inviteId')
App::delete('/v1/teams/:teamId/memberships/:membershipId')
->desc('Delete Team Membership')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.delete')
->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.method', 'deleteMembership')
->label('sdk.description', '/docs/references/teams/delete-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('inviteId', '', new UID(), 'Invite unique ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response')
->inject('projectDB')
->inject('audits')
->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\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
$membership = $projectDB->getDocument($inviteId);
$membership = $projectDB->getDocument($membershipId);
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
throw new Exception('Invite not found', 404);
@ -706,12 +794,12 @@ App::delete('/v1/teams/:teamId/memberships/:inviteId')
$audits
->setParam('userId', $membership->getAttribute('userId'))
->setParam('event', 'teams.membership.delete')
->setParam('event', 'teams.memberships.delete')
->setParam('resource', 'teams/'.$teamId)
;
$events
->setParam('payload', $response->output($membership, Response::MODEL_MEMBERSHIP))
->setParam('eventData', $response->output($membership, Response::MODEL_MEMBERSHIP))
;
$response->noContent();

View file

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

View file

@ -14,24 +14,69 @@ use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
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\V07;
use Utopia\CLI\Console;
Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
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 Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Database\Document $console */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
/** @var bool $mode */
/** @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', ''));
@ -41,7 +86,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
$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);
}
@ -92,6 +137,9 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
case version_compare ($responseFormat , '0.6.2', '<=') :
Response::setFilter(new V06());
break;
case version_compare ($responseFormat , '0.7.2', '<=') :
Response::setFilter(new V07());
break;
default:
Response::setFilter(null);
}
@ -208,7 +256,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
}
}, $user->getAttribute('memberships', []));
// TDOO Check if user is god
// TDOO Check if user is root
if (!\in_array($scope, $scopes)) {
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);
}
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'clients']);
}, ['utopia', 'request', 'response', 'console', 'project', 'consoleDB', 'user', 'locale', 'clients']);
App::options(function ($request, $response) {
/** @var Utopia\Swoole\Request $request */
@ -271,7 +319,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
switch ($error->getCode()) {
switch ($error->getCode()) { // Don't show 500 errors!
case 400: // Error allowed publicly
case 401: // 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 412: // Error allowed publicly
case 429: // Error allowed publicly
case 501: // Error allowed publicly
$code = $error->getCode();
$message = $error->getMessage();
break;
@ -423,4 +472,4 @@ include_once __DIR__ . '/shared/web.php';
foreach (Config::getParam('services', []) as $service) {
include_once $service['controller'];
}
}

View file

@ -3,19 +3,19 @@
global $utopia, $request, $response;
use Appwrite\Database\Document;
use Appwrite\Network\Validator\Host;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Validator\Numeric;
use Utopia\Validator\Text;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Host;
use Utopia\Storage\Validator\File;
App::get('/v1/mock/tests/foo')
->desc('Get Foo')
->groups(['mock'])
->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.method', 'get')
->label('sdk.description', 'Mock a get request.')
@ -33,7 +33,7 @@ App::post('/v1/mock/tests/foo')
->desc('Post Foo')
->groups(['mock'])
->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.method', 'post')
->label('sdk.description', 'Mock a post request.')
@ -51,7 +51,7 @@ App::patch('/v1/mock/tests/foo')
->desc('Patch Foo')
->groups(['mock'])
->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.method', 'patch')
->label('sdk.description', 'Mock a patch request.')
@ -69,7 +69,7 @@ App::put('/v1/mock/tests/foo')
->desc('Put Foo')
->groups(['mock'])
->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.method', 'put')
->label('sdk.description', 'Mock a put request.')
@ -87,7 +87,7 @@ App::delete('/v1/mock/tests/foo')
->desc('Delete Foo')
->groups(['mock'])
->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.method', 'delete')
->label('sdk.description', 'Mock a delete request.')
@ -105,7 +105,7 @@ App::get('/v1/mock/tests/bar')
->desc('Get Bar')
->groups(['mock'])
->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.method', 'get')
->label('sdk.description', 'Mock a get request.')
@ -123,7 +123,7 @@ App::post('/v1/mock/tests/bar')
->desc('Post Bar')
->groups(['mock'])
->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.method', 'post')
->label('sdk.description', 'Mock a post request.')
@ -141,7 +141,7 @@ App::patch('/v1/mock/tests/bar')
->desc('Patch Bar')
->groups(['mock'])
->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.method', 'patch')
->label('sdk.description', 'Mock a patch request.')
@ -159,7 +159,7 @@ App::put('/v1/mock/tests/bar')
->desc('Put Bar')
->groups(['mock'])
->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.method', 'put')
->label('sdk.description', 'Mock a put request.')
@ -177,7 +177,7 @@ App::delete('/v1/mock/tests/bar')
->desc('Delete Bar')
->groups(['mock'])
->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.method', 'delete')
->label('sdk.description', 'Mock a delete request.')
@ -195,7 +195,7 @@ App::post('/v1/mock/tests/general/upload')
->desc('Upload File')
->groups(['mock'])
->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.method', 'upload')
->label('sdk.description', 'Mock a file upload request.')
@ -240,7 +240,7 @@ App::get('/v1/mock/tests/general/redirect')
->desc('Redirect')
->groups(['mock'])
->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.method', 'redirect')
->label('sdk.description', 'Mock a redirect request.')
@ -258,7 +258,7 @@ App::get('/v1/mock/tests/general/redirect/done')
->desc('Redirected')
->groups(['mock'])
->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.method', 'redirected')
->label('sdk.description', 'Mock a redirected request.')
@ -273,7 +273,7 @@ App::get('/v1/mock/tests/general/set-cookie')
->desc('Set Cookie')
->groups(['mock'])
->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.method', 'setCookie')
->label('sdk.description', 'Mock a set cookie request.')
@ -292,7 +292,7 @@ App::get('/v1/mock/tests/general/get-cookie')
->desc('Get Cookie')
->groups(['mock'])
->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.method', 'getCookie')
->label('sdk.description', 'Mock a cookie response.')
@ -313,7 +313,7 @@ App::get('/v1/mock/tests/general/empty')
->desc('Empty Response')
->groups(['mock'])
->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.method', 'empty')
->label('sdk.description', 'Mock an empty response.')
@ -331,7 +331,7 @@ App::get('/v1/mock/tests/general/400-error')
->desc('400 Error')
->groups(['mock'])
->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.method', 'error400')
->label('sdk.description', 'Mock a 400 failed request.')
@ -347,7 +347,7 @@ App::get('/v1/mock/tests/general/500-error')
->desc('500 Error')
->groups(['mock'])
->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.method', 'error500')
->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('state', '', new Text(1024), 'OAuth2 state.')
->inject('response')
->action(function ($clientId, $redirectURI, $scope, $state, $response) {
->action(function ($client_id, $redirectURI, $scope, $state, $response) {
/** @var Appwrite\Utopia\Response $response */
$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('code', '', new Text(100), 'OAuth2 state.')
->inject('response')
->action(function ($clientId, $redirectURI, $clientSecret, $code, $response) {
->action(function ($client_id, $redirectURI, $client_secret, $code, $response) {
/** @var Appwrite\Utopia\Response $response */
if ($clientId != '1') {
if ($client_id != '1') {
throw new Exception('Invalid client ID');
}
if ($clientSecret != '123456') {
if ($client_secret != '123456') {
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);
if (($abuse->check() // Route is rate-limited
&& 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);
}
@ -76,9 +76,10 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
*/
$events
->setParam('projectId', $project->getId())
->setParam('webhooks', $project->getAttribute('webhooks', []))
->setParam('userId', $user->getId())
->setParam('event', $route->getLabel('event', ''))
->setParam('payload', [])
->setParam('eventData', [])
->setParam('functionId', null)
->setParam('executionId', null)
->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');
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) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
@ -123,8 +179,8 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
/** @var bool $mode */
if (!empty($events->getParam('event'))) {
if(empty($events->getParam('payload'))) {
$events->setParam('payload', $response->getPayload());
if(empty($events->getParam('eventData'))) {
$events->setParam('eventData', $response->getPayload());
}
$webhooks = clone $events;

View file

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

View file

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

View file

@ -1,5 +1,6 @@
<?php
use Appwrite\Database\Database;
use Appwrite\Specification\Format\OpenAPI3;
use Appwrite\Specification\Format\Swagger2;
use Appwrite\Specification\Specification;
@ -42,10 +43,38 @@ App::get('/')
->label('permission', 'public')
->label('scope', 'home')
->inject('response')
->action(function ($response) {
->inject('consoleDB')
->inject('project')
->action(function ($response, $consoleDB, $project) {
/** @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')
@ -58,6 +87,10 @@ App::get('/auth/signin')
$page = new View(__DIR__.'/../../views/home/auth/signin.phtml');
$page
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
;
$layout
->setParam('title', 'Sign In - '.APP_NAME)
->setParam('body', $page);
@ -72,6 +105,10 @@ App::get('/auth/signup')
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/signup.phtml');
$page
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
;
$layout
->setParam('title', 'Sign Up - '.APP_NAME)
->setParam('body', $page);
@ -87,6 +124,10 @@ App::get('/auth/recovery')
$page = new View(__DIR__.'/../../views/home/auth/recovery.phtml');
$page
->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST'))))
;
$layout
->setParam('title', 'Password Recovery - '.APP_NAME)
->setParam('body', $page);
@ -214,6 +255,12 @@ App::get('/specs/:format')
'console' => APP_PLATFORM_CONSOLE,
];
$authCounts = [
'client' => 1,
'server' => 2,
'console' => 1,
];
$routes = [];
$models = [];
$services = [];
@ -226,6 +273,12 @@ App::get('/specs/:format')
'description' => 'Your project ID',
'in' => 'header',
],
'JWT' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-JWT',
'description' => 'Your secret JSON Web Token',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
@ -246,6 +299,12 @@ App::get('/specs/:format')
'description' => 'Your secret API key',
'in' => 'header',
],
'JWT' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-JWT',
'description' => 'Your secret JSON Web Token',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
@ -266,6 +325,12 @@ App::get('/specs/:format')
'description' => 'Your secret API key',
'in' => 'header',
],
'JWT' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-JWT',
'description' => 'Your secret JSON Web Token',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'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 ($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)) {
continue;
}
@ -305,7 +388,7 @@ App::get('/specs/:format')
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;
}
@ -342,11 +425,11 @@ App::get('/specs/:format')
switch ($format) {
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;
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;
default:

View file

@ -12,6 +12,8 @@ use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
// xdebug_start_trace('/tmp/trace');
@ -65,18 +67,6 @@ Files::load(__DIR__ . '/../public');
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) {
$request = new Request($swooleRequest);
$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_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12;
const APP_CACHE_BUSTER = 145;
const APP_VERSION_STABLE = '0.7.2';
const APP_CACHE_BUSTER = 146;
const APP_VERSION_STABLE = '0.8.0';
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
const APP_STORAGE_CACHE = '/storage/cache';
@ -62,6 +62,11 @@ const DELETE_TYPE_EXECUTIONS = 'executions';
const DELETE_TYPE_AUDIT = 'audit';
const DELETE_TYPE_ABUSE = 'abuse';
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();
@ -71,10 +76,11 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
* ENV vars
*/
Config::load('events', __DIR__.'/config/events.php');
Config::load('auth', __DIR__.'/config/auth.php');
Config::load('providers', __DIR__.'/config/providers.php');
Config::load('platforms', __DIR__.'/config/platforms.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('scopes', __DIR__.'/config/scopes.php'); // User roles and scopes
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)) {
return;
}
$driver = new InfluxDB\Driver\Curl("http://{$host}:{$port}");
$client = new InfluxDB\Client($host, $port, '', '', false, false, 5);
$client->setDriver($driver);
return $client;
});
@ -314,7 +321,7 @@ App::setResource('layout', function($locale) {
}, ['locale']);
App::setResource('locale', function() {
return new Locale('en');
return new Locale(App::getEnv('_APP_LOCALE', 'en'));
});
// 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
|| 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]);
}

View file

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

View file

@ -2,17 +2,24 @@
global $cli;
use Appwrite\Auth\Auth;
use Appwrite\Docker\Compose;
use Appwrite\Docker\Env;
use Utopia\Analytics\GoogleAnalytics;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\View;
use Utopia\Validator\Text;
$cli
->task('install')
->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
* 2. Check for older setup and get older version - DONE
@ -60,6 +67,9 @@ $cli
$data = @file_get_contents($path.'/docker-compose.yml');
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);
$appwrite = $compose->getService('appwrite');
$oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null;
@ -85,6 +95,8 @@ $cli
$data = @file_get_contents($path.'/.env');
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);
foreach ($env->list() as $key => $value) {
@ -108,16 +120,36 @@ $cli
}
}
$httpPort = Console::confirm('Choose your server HTTP port: (default: '.$defaultHTTPPort.')');
$httpPort = ($httpPort) ? $httpPort : $defaultHTTPPort;
if(empty($httpPort)) {
$httpPort = Console::confirm('Choose your server HTTP port: (default: '.$defaultHTTPPort.')');
$httpPort = ($httpPort) ? $httpPort : $defaultHTTPPort;
}
$httpsPort = Console::confirm('Choose your server HTTPS port: (default: '.$defaultHTTPSPort.')');
$httpsPort = ($httpsPort) ? $httpsPort : $defaultHTTPSPort;
if(empty($httpsPort)) {
$httpsPort = Console::confirm('Choose your server HTTPS port: (default: '.$defaultHTTPSPort.')');
$httpsPort = ($httpsPort) ? $httpsPort : $defaultHTTPSPort;
}
$input = [];
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'];
continue;
}
@ -136,6 +168,8 @@ $cli
->setParam('httpPort', $httpPort)
->setParam('httpsPort', $httpsPort)
->setParam('version', APP_VERSION_STABLE)
->setParam('organization', $organization)
->setParam('image', $image)
;
$templateForEnv

View file

@ -36,7 +36,7 @@ $cli
$projects = [$console];
$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) {
foreach ($projects as $project) {

View file

@ -21,20 +21,6 @@ use Appwrite\SDK\Language\Swift;
$cli
->task('sdks')
->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');
$selected = \strtolower(Console::confirm('Choose SDK ("*" for all):'));
$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;
$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');
}
@ -193,6 +179,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
->setExamples($examples)
->setTwitter(APP_SOCIAL_TWITTER_HANDLE)
->setDiscord(APP_SOCIAL_DISCORD_CHANNEL, APP_SOCIAL_DISCORD)
->setDefaultHeaders([
'X-Appwrite-Response-Format' => '0.8.0',
])
;
try {

View file

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

View file

@ -36,9 +36,6 @@ $maxCells = 10;
<div class="zone xl">
<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}}">
<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>
@ -135,7 +132,7 @@ $maxCells = 10;
</div>
</div>
<div class="clear text-align-center paging">
<div class="pull-end text-align-center paging">
<form
data-service="database.listDocuments"
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>
</form>
</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>
</li>
<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 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>
</div>
</div>

View file

@ -12,40 +12,6 @@
<li data-state="/console/database?project={{router.params.project}}">
<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"
data-service="database.listCollections"
data-event="load,database.createCollection,database.updateCollection,database.deleteCollection"
@ -76,7 +42,7 @@
</ul>
</div>
<div class="clear text-align-center paging">
<div class="pull-end text-align-center paging">
<form
data-service="database.listCollections"
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>
</form>
</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 data-state="/console/database/usage?project={{router.params.project}}">
<h2>Usage</h2>

View file

@ -46,28 +46,13 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
<div class="box margin-bottom-large">
<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>
<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">
<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 data-ls-if="{{project-function.tag}} !== ''" 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>
</div>
</div>
</div>
@ -575,6 +560,31 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
</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">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>

View file

@ -1,5 +1,5 @@
<?php
$environments = $this->getParam('environments', []);
$runtimes = $this->getParam('runtimes', []);
?>
<div class="cover">
<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">
<li class="clear">
<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>
<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>
<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">
<?php foreach($environments as $key => $environment): ?>
<option value="<?php echo $this->escape($key); ?>"><?php echo $this->escape($environment['name']); ?> <?php echo $this->escape($environment['version']); ?></option>
<?php foreach($runtimes as $key => $runtime): ?>
<option value="<?php echo $this->escape($key); ?>"><?php echo $this->escape($runtime['name']); ?> <?php echo $this->escape($runtime['version']); ?></option>
<?php endforeach; ?>
</select>

View file

@ -93,7 +93,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
<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="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 class="col span-3">

View file

@ -415,7 +415,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
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="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>
</form>
@ -437,7 +437,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
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="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>
</form>

View file

@ -17,48 +17,6 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<li data-state="/console/database?project={{router.params.project}}">
<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"
data-service="storage.listFiles"
data-event="submit"
@ -215,7 +173,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
</div>
</div>
<div class="clear text-align-center paging">
<div class="pull-end text-align-center paging">
<form
data-service="storage.listFiles"
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>
</form>
</div>
<div data-ui-modal class="box modal sticky-footer close" data-button-text="Add File">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Upload File</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Storage File"
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>
</li>
</ul>

View file

@ -1,5 +1,6 @@
<?php
$providers = $this->getParam('providers', []);
$auth = $this->getParam('auth', []);
?>
<div class="cover">
@ -17,41 +18,6 @@ $providers = $this->getParam('providers', []);
<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"
data-service="users.list"
data-event="submit"
@ -111,7 +77,7 @@ $providers = $this->getParam('providers', []);
<td data-title="Name: ">
<a data-ls-attrs="href=/console/users/user?id={{user.$id}}&project={{router.params.project}}">
<span data-ls-bind="{{user.name}}"></span>
<span data-ls-if="{{user.name}} === ''">-----</span>
<span data-ls-if="{{user.name|escape}} === ''">-----</span>
</a>
</td>
<td data-title="Email: ">
@ -137,7 +103,7 @@ $providers = $this->getParam('providers', []);
</div>
</div>
<div class="clear text-align-center paging">
<div class="pull-end text-align-center paging">
<form
data-service="users.list"
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>
</form>
</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>
</li>
<li data-state="/console/users/teams?project={{router.params.project}}">
<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"
data-service="teams.list"
data-event="submit"
@ -239,7 +211,7 @@ $providers = $this->getParam('providers', []);
</div>
<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">
<table class="vertical">
@ -267,7 +239,7 @@ $providers = $this->getParam('providers', []);
</div>
</div>
<div class="clear text-align-center paging">
<div class="pull-end text-align-center paging">
<form
data-service="teams.list"
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>
</form>
</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>
</li>
<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-event="load,projects.create,projects.update,projects.deleteProject"
data-name="console-project"
@ -312,6 +402,7 @@ $providers = $this->getParam('providers', []);
<?php foreach ($providers as $provider => $data):
if (isset($data['enabled']) && !$data['enabled']) { continue; }
if (isset($data['mock']) && $data['mock']) { continue; }
$sandbox = $data['sandbox'] ?? false;
$form = $data['form'] ?? false;
$name = $data['name'] ?? 'Unknown';
$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; ?>">
<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
autocomplete="off"
data-analytics
data-analytics-activity
data-analytics-event="submit"
@ -338,6 +430,10 @@ $providers = $this->getParam('providers', []);
data-failure="alert"
data-failure-param-alert-text="Failed to update project OAuth2 settings"
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); ?>">
<?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>
</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">
<?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>
<p class="margin-bottom-no text-one-liner text-size-small">

View file

@ -105,7 +105,7 @@
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="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>
</form>

View file

@ -9,7 +9,7 @@
data-scope="console"
data-event="submit"
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-secret="{{router.params.secret}}"
data-success="redirect,alert,trigger"

View file

@ -1,3 +1,6 @@
<?php
$smtpEnabled = $this->getParam('smtpEnabled', false);
?>
<div class="zone medium">
<h1 class="zone xl margin-bottom-large margin-top">
Password Recovery
@ -25,7 +28,13 @@
<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>
</div>

View file

@ -1,3 +1,6 @@
<?php
$root = ($this->getParam('root') !== 'disabled');
?>
<div class="zone medium"
data-service="account.get"
data-name="account"
@ -43,7 +46,7 @@
<br />
<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>

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">
Sign Up
</h1>
@ -9,6 +19,7 @@
<form name="account.create"
data-analytics
data-newsletter
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="home"
@ -23,6 +34,10 @@
data-failure-param-alert-text="Registration Failed. Please try again later"
data-failure-param-alert-classname="error">
<?php if($root): ?>
<p>Please create your root account</p>
<?php endif; ?>
<label>Name</label>
<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>
</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>
</form>
</div>
<?php if(!$root): ?>
<div class="zone medium text-align-center">
<a href="/auth/signin">Already have an account?</a>
</div>
</div>
<?PHP endif; ?>

View file

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

View file

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

View file

@ -112,13 +112,21 @@ class DeletesV1
protected function deleteUser(Document $document, $projectId)
{
$tokens = $document->getAttribute('tokens', []);
foreach ($tokens as $token) {
if (!$this->getProjectDB($projectId)->deleteDocument($token->getId())) {
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
$this->deleteByGroup([
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,

View file

@ -14,21 +14,19 @@ use Utopia\Config\Config;
require_once __DIR__.'/../init.php';
Runtime::enableCoroutine(0);
Console::title('Functions V1 Worker');
Runtime::setHookFlags(SWOOLE_HOOK_ALL);
Console::success(APP_NAME.' functions worker v1 has started');
$environments = Config::getParam('environments');
$runtimes = Config::getParam('runtimes');
/**
* Warmup Docker Images
*/
$warmupStart = \microtime(true);
Co\run(function() use ($environments) { // Warmup: make sure images are ready to run fast 🚀
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
Co\run(function() use ($runtimes) { // Warmup: make sure images are ready to run fast 🚀
$dockerUser = App::getEnv('DOCKERHUB_PULL_USERNAME', 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);
}
foreach($environments as $environment) {
go(function() use ($environment) {
foreach($runtimes as $runtime) {
go(function() use ($runtime) {
$stdout = '';
$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)) {
Console::log($stdout);
@ -143,11 +141,15 @@ class FunctionsV1
$projectId = $this->args['projectId'] ?? '';
$functionId = $this->args['functionId'] ?? '';
$webhooks = $this->args['webhooks'] ?? [];
$executionId = $this->args['executionId'] ?? '';
$trigger = $this->args['trigger'] ?? '';
$event = $this->args['event'] ?? '';
$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->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
@ -195,7 +197,7 @@ class FunctionsV1
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;
@ -245,14 +247,14 @@ class FunctionsV1
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $projectId,
'webhooks' => $webhooks,
'functionId' => $function->getId(),
'executionId' => null,
'trigger' => 'schedule',
'scheduleOriginal' => $function->getAttribute('schedule', ''),
]); // Async task rescheduale
$this->execute($trigger, $projectId, $executionId, $database, $function);
$this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$eventData*/'', $data, $webhooks, $userId, $jwt);
break;
case 'http':
@ -264,7 +266,7 @@ class FunctionsV1
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;
default:
@ -282,15 +284,19 @@ class FunctionsV1
* @param Database $database
* @param Database $function
* @param string $event
* @param string $payload
* @param string $eventData
* @param string $data
* @param array $webhooks
* @param string $userId
* @param string $jwt
*
* @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;
$environments = Config::getParam('environments');
$runtimes = Config::getParam('runtimes');
Authorization::disable();
$tag = $database->getDocument($function->getAttribute('tag', ''));
@ -324,11 +330,11 @@ class FunctionsV1
Authorization::reset();
$environment = (isset($environments[$function->getAttribute('env', '')]))
? $environments[$function->getAttribute('env', '')]
$runtime = (isset($runtimes[$function->getAttribute('env', '')]))
? $runtimes[$function->getAttribute('env', '')]
: null;
if(\is_null($environment)) {
if(\is_null($runtime)) {
throw new Exception('Environment "'.$function->getAttribute('env', '').' is not supported');
}
@ -337,10 +343,14 @@ class FunctionsV1
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
'APPWRITE_FUNCTION_TAG' => $tag->getId(),
'APPWRITE_FUNCTION_TRIGGER' => $trigger,
'APPWRITE_FUNCTION_ENV_NAME' => $environment['name'],
'APPWRITE_FUNCTION_ENV_VERSION' => $environment['version'],
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
'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) {
@ -412,7 +422,7 @@ class FunctionsV1
" --volume {$tagPathTargetDir}:/tmp:rw".
" --workdir /usr/local/src".
" ".\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'"
, '', $stdout, $stderr, 30);
@ -469,6 +479,27 @@ class FunctionsV1
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

View file

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

View file

@ -26,20 +26,20 @@ class UsageV1
$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'];
$networkResponseSize = $this->args['networkResponseSize'];
$networkRequestSize = $this->args['networkRequestSize'] ?? 0;
$networkResponseSize = $this->args['networkResponseSize'] ?? 0;
$httpMethod = $this->args['httpMethod'];
$httpRequest = $this->args['httpRequest'];
$httpMethod = $this->args['httpMethod'] ?? '';
$httpRequest = $this->args['httpRequest'] ?? 0;
$functionId = $this->args['functionId'];
$functionExecution = $this->args['functionExecution'];
$functionExecutionTime = $this->args['functionExecutionTime'];
$functionStatus = $this->args['functionStatus'];
$functionExecution = $this->args['functionExecution'] ?? 0;
$functionExecutionTime = $this->args['functionExecutionTime'] ?? 0;
$functionStatus = $this->args['functionStatus'] ?? '';
$tags = ",project={$projectId},version=".App::getEnv('_APP_VERSION', 'UNKNOWN').'';

View file

@ -2,11 +2,6 @@
use Utopia\App;
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';
@ -24,34 +19,16 @@ class WebhooksV1
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 = [];
// Event
$projectId = $this->args['projectId'] ?? '';
$webhooks = $this->args['webhooks'] ?? [];
$userId = $this->args['userId'] ?? '';
$event = $this->args['event'] ?? '';
$payload = \json_encode($this->args['payload']);
$eventData = \json_encode($this->args['eventData']);
// 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) {
foreach ($webhooks as $webhook) {
if (!(isset($webhook['events']) && \is_array($webhook['events']) && \in_array($event, $webhook['events']))) {
continue;
}
@ -67,7 +44,7 @@ class WebhooksV1
$ch = \curl_init($url);
\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_RETURNTRANSFER, 1);
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(APP_USERAGENT,
@ -79,7 +56,7 @@ class WebhooksV1
CURLOPT_HTTPHEADER,
[
'Content-Type: application/json',
'Content-Length: '.\strlen($payload),
'Content-Length: '.\strlen($eventData),
'X-'.APP_NAME.'-Webhook-Id: '.$id,
'X-'.APP_NAME.'-Webhook-Event: '.$event,
'X-'.APP_NAME.'-Webhook-Name: '.$name,

View file

@ -21,7 +21,7 @@
}
},
"require": {
"php": ">=7.4.0",
"php": ">=8.0.0",
"ext-curl": "*",
"ext-imagick": "*",
"ext-mbstring": "*",
@ -35,52 +35,44 @@
"ext-zlib": "*",
"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/analytics": "0.2.*",
"utopia-php/audit": "0.5.*",
"utopia-php/cache": "0.2.*",
"utopia-php/cli": "0.10.0",
"utopia-php/cli": "0.11.*",
"utopia-php/config": "0.2.*",
"utopia-php/locale": "0.3.*",
"utopia-php/registry": "0.2.*",
"utopia-php/registry": "0.4.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/domains": "0.2.*",
"utopia-php/domains": "1.1.*",
"utopia-php/swoole": "0.2.*",
"utopia-php/system": "0.4.*",
"utopia-php/storage": "0.4.*",
"utopia-php/image": "0.1.*",
"utopia-php/storage": "0.5.*",
"utopia-php/image": "0.2.*",
"resque/php-resque": "1.3.6",
"matomo/device-detector": "4.1.0",
"matomo/device-detector": "4.2.2",
"dragonmantank/cron-expression": "3.1.0",
"influxdb/influxdb-php": "1.15.2",
"phpmailer/phpmailer": "6.4.1",
"chillerlan/php-qrcode": "4.3.0",
"adhocore/jwt": "1.1.2",
"slickdeals/statsd": "~3.0"
"slickdeals/statsd": "3.0.2"
},
"require-dev": {
"appwrite/sdk-generator": "0.9.0",
"phpunit/phpunit": "9.4.2",
"swoole/ide-helper": "4.5.5",
"vimeo/psalm": "4.1.1"
"appwrite/sdk-generator": "0.10.1",
"swoole/ide-helper": "4.6.6",
"phpunit/phpunit": "9.5.4",
"vimeo/psalm": "4.7.2"
},
"repositories": [
{
"type": "git",
"url": "https://github.com/appwrite/sdk-generator"
}
],
"provide": {
"ext-phpiredis": "*"
},
"minimum-stability": "dev",
"config": {
"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:
- mariadb
- redis
- clamav
# - clamav
- influxdb
environment:
- _APP_ENV
- _APP_LOCALE
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_WHITELIST_IPS
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
@ -110,7 +114,7 @@ services:
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_ENVS
- _APP_FUNCTIONS_RUNTIMES
appwrite-worker-usage:
entrypoint: worker-usage
@ -180,11 +184,6 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-worker-tasks:
entrypoint: worker-tasks
@ -201,6 +200,7 @@ services:
- mariadb
environment:
- _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@ -282,12 +282,12 @@ services:
- /tmp:/tmp:rw
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
- ./docker:/usr/src/code/docker
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@ -299,6 +299,7 @@ services:
- _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT
- _APP_FUNCTIONS_CONTAINERS
- _APP_FUNCTIONS_RUNTIMES
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
@ -413,16 +414,16 @@ services:
volumes:
- appwrite-redis:/data:rw
clamav:
image: appwrite/clamav:1.2.0
container_name: appwrite-clamav
networks:
- appwrite
volumes:
- appwrite-uploads:/storage/uploads
# clamav:
# image: appwrite/clamav:1.2.0
# container_name: appwrite-clamav
# networks:
# - appwrite
# volumes:
# - appwrite-uploads:/storage/uploads
influxdb:
image: influxdb:1.8-alpine
image: appwrite/influxdb:1.0.0
container_name: appwrite-influxdb
networks:
- appwrite
@ -430,7 +431,7 @@ services:
- appwrite-influxdb:/var/lib/influxdb:rw
telegraf:
image: appwrite/telegraf:1.0.0
image: appwrite/telegraf:1.1.0
container_name: appwrite-telegraf
networks:
- appwrite
@ -449,16 +450,16 @@ services:
# Webgrind - A nice UI for exploring and debugging code-level stuff
maildev: # used mainly for dev tests
image: djfarrelly/maildev
container_name: appwrite-maildev
image: appwrite/mailcatcher:1.0.0
container_name: appwrite-mailcatcher
ports:
- '9503:80'
- '9503:1080'
networks:
- appwrite
request-catcher: # used mainly for dev tests
image: smarterdm/http-request-catcher
container_name: appwrite-request-catcher
image: appwrite/requestcatcher:1.0.0
container_name: appwrite-requestcatcher
ports:
- '9504:5000'
networks:
@ -466,6 +467,7 @@ services:
adminer:
image: adminer
container_name: appwrite-adminer
restart: always
ports:
- 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