1
0
Fork 0
mirror of synced 2024-06-02 02:44:47 +12:00

Merge branch '0.7.x' of github.com:appwrite/appwrite into feat-465-add-arm-support

This commit is contained in:
Eldad Fux 2021-01-30 12:37:17 +02:00
commit 94a14bd8af
385 changed files with 8288 additions and 4236 deletions

9
.env
View file

@ -3,6 +3,7 @@ _APP_ENV=development
_APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
_APP_OPTIONS_ABUSE=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
@ -31,6 +32,10 @@ _APP_STORAGE_LIMIT=10000000
_APP_FUNCTIONS_TIMEOUT=900
_APP_FUNCTIONS_CONTAINERS=10
_APP_FUNCTIONS_CPUS=1
_APP_FUNCTIONS_MEMORY=128
_APP_FUNCTIONS_MEMORY_SWAP=128
_APP_FUNCTIONS_MEMORY=256
_APP_FUNCTIONS_MEMORY_SWAP=256
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_USAGE_STATS=enabled

1
.travis-ci/deploy.sh Normal file
View file

@ -0,0 +1 @@
echo 'Nothing to deploy right now.'

View file

@ -1,25 +1,33 @@
dist: xenial
arch:
- amd64
- arm64
os: linux
language: minimal
language: shell
notifications:
email:
- team@appwrite.io
before_install:
- curl -fsSL https://get.docker.com | sh
- echo '{"experimental":"enabled"}' | sudo tee /etc/docker/daemon.json
- mkdir -p $HOME/.docker
- echo '{"experimental":"enabled"}' | sudo tee $HOME/.docker/config.json
- sudo service docker start
- export COMPOSE_INTERACTIVE_NO_CLI=1
- curl -fsSL https://get.docker.com | sh
- echo '{"experimental":"enabled"}' | sudo tee /etc/docker/daemon.json
- mkdir -p $HOME/.docker
- echo '{"experimental":"enabled"}' | sudo tee $HOME/.docker/config.json
- sudo service docker start
- >
if [ ! -z "${DOCKERHUB_PULL_USERNAME:-}" ]; then
echo "${DOCKERHUB_PULL_PASSWORD}" | docker login --username "${DOCKERHUB_PULL_USERNAME}" --password-stdin
fi
- docker --version
- docker buildx create --use
- chmod -R u+x ./.travis-ci
- export COMPOSE_INTERACTIVE_NO_CLI=1
install:
- docker --version
- docker-compose up -d
- sleep 10
@ -29,3 +37,11 @@ script:
- docker-compose exec appwrite doctor
- docker-compose exec appwrite vars
- docker-compose exec appwrite test
deploy:
- provider: script
edge: true
script: ./.travis-ci/deploy.sh
on:
repo: appwrite/appwrite
branch: deploy

View file

@ -2,37 +2,22 @@
## Features
- Improved Webhooks and New System Events - [Learn more]()
- New QR code generator library (@PedroCisnerosSantana - [#475](https://github.com/appwrite/appwrite/issues/475))
- Improved Webhooks and added new system events - [Learn more]()
- Added response to /locale/languages API with a list of languages (@TorstenDittmann ,[#351](https://github.com/appwrite/appwrite/issues/351))
- Added API response payload structure info and examples to the docs site ([#381](https://github.com/appwrite/appwrite/issues/381))
- Added Google Fonts to Appwrite for offline availability
- Added a new route in the Avatars API to get user initials avatar ([#386](https://github.com/appwrite/appwrite/issues/386))
- Added option to delete team from the console ([#380](https://github.com/appwrite/appwrite/issues/380))
- Added option to view team members from the console ([#378](https://github.com/appwrite/appwrite/issues/378))
- Add option to assign new team members to a team from the console and the API ([#379](https://github.com/appwrite/appwrite/issues/379))
- Added API response payload structure info and examples to the docs site ([#381](https://github.com/appwrite/appwrite/issues/381))
- Added support for Brotli compression (@PedroCisnerosSantana, @Rohitub222, [#310](https://github.com/appwrite/appwrite/issues/310))
- Added Select All Checkbox for on Console API key Scopes Screen ([#477](https://github.com/appwrite/appwrite/issues/477))
- Added pagination and search for team memberships route ([#387](https://github.com/appwrite/appwrite/issues/387))
- UI performance & accessibility improvements ([#406](https://github.com/appwrite/appwrite/pull/406))
- Added option to delete user from the console (@PineappleIOnic - #538)
- Created lazy deletion of data worker ([#521](https://github.com/appwrite/appwrite/issues/521))
- All emails are now sent asynchronously for improved performance (@TorstenDittmann ,[#402](https://github.com/appwrite/appwrite/pull/402))
- Updated grid for OAuth2 providers list in the console ([#413](https://github.com/appwrite/appwrite/issues/413))
- Upgraded Redis Resque queue library to version 1.3.6 ([#319](https://github.com/appwrite/appwrite/issues/319))
- New deletion worker ([#521](https://github.com/appwrite/appwrite/issues/521))
- New maintenance worker - cleaning up system logs and other optimizations ([#766](https://github.com/appwrite/appwrite/pull/766))
- New email worker - all emails are now sent asynchronously for improved performance (@TorstenDittmann ,[#402](https://github.com/appwrite/appwrite/pull/402))
- Moved all Appwrite container logs to STDOUT & STDERR ([#389](https://github.com/appwrite/appwrite/issues/389))
- New UI micro-interactions and CSS fixes (@AnatoleLucet)
- New Doctor CLI to debug the Appwrite server ([#415](https://github.com/appwrite/appwrite/issues/415))
- Added container names to docker-compose.yml (@drandell)
- Upgraded ClamAV container image to version 1.0.11 ([#412](https://github.com/appwrite/appwrite/issues/412))
- Optimised function execution by using fully-qualified function calls
- Added support for boolean 'true' and 'false' in query strings alongside 1 and 0
- Added pagination for projects list on the console home page.
- Updated storage calculation to match IEC standards
- Now using Alpine as base Docker image
- Upgraded device detctor to version 3.12.6
- Upgraded MariaDB to version 10.5.5
- Switch standard ports to 95xx prefix ([#780](https://github.com/appwrite/appwrite/pull/780))
- Switch standard dev ports to 95xx prefix ([#780](https://github.com/appwrite/appwrite/pull/780))
- User & Team name max length is now 128 chars and not 100 for better API consistency
- Collection name max length is now 128 chars and not 256 for better API consistency
- Project name max length is now 128 chars and not 100 for better API consistency
@ -40,21 +25,52 @@
- API Key name max length is now 128 chars and not 256 for better API consistency
- Task name max length is now 128 chars and not 256 for better API consistency
- Platform name max length is now 128 chars and not 256 for better API consistency
- Webhooks payloads are now exactly the same as any of the API response objects
- Webhooks payloads are now exactly the same as any of the API response objects, documentation added
- Added new locale: Marathi -mr (@spielers)
- New and consistent response format for all API object + new response examples in the docs
- Removed user roles attribute from user object (can be fetched from /v1/teams/memberships) **
- Removed type attribute from session object response (used only internally)
- ** - might be changed before merging to master
- Upgraded Traefik image to version 2.3
- Upgraded Redis Docker image to version 6.0 (alpine)
- Upgraded Influxdb Docker image to version 1.8 (alpine)
- Added option to disable mail sending by setting empty SMTP host
- Added fallback option to 0.6 format for backward compatibility with any changes (@christyjacob4 [#772](https://github.com/appwrite/appwrite/pull/772))
- Added option to disable mail sending by setting an empty SMTP host value ([#730](https://github.com/appwrite/appwrite/issues/730))
- Upgraded installation script ([#490](https://github.com/appwrite/appwrite/issues/490))
- Added new environment variables for ClamAV hostname and port ([#780](https://github.com/appwrite/appwrite/pull/780))
- New OAuth adapter for Box.com (@armino-dev - [#420](https://github.com/appwrite/appwrite/issues/410))
- New OAuth adapter for PayPal sandbox (@armino-dev - [#420](https://github.com/appwrite/appwrite/issues/410))
- New OAuth adapter for Tradeshift (@armino-dev - [#855](https://github.com/appwrite/appwrite/pull/855))
- New OAuth adapter for Tradeshift sandbox (@armino-dev - [#855](https://github.com/appwrite/appwrite/pull/855))
- Introducing new permssion types: role:guest, role:member, role:app
- Disabled rate-limits on server side integrations
- Refactored migration script
### User Interface
- Updated grid for OAuth2 providers list in the console ([#413](https://github.com/appwrite/appwrite/issues/413))
- Added Google Fonts to Appwrite for offline availability
- Added option to delete user from the console (@PineappleIOnic - [#538](https://github.com/appwrite/appwrite/issues/538))
- Added option to delete team from the console ([#380](https://github.com/appwrite/appwrite/issues/380))
- Added option to view team members from the console ([#378](https://github.com/appwrite/appwrite/issues/378))
- Add option to assign new team members to a team from the console and the API ([#379](https://github.com/appwrite/appwrite/issues/379))
- Added Select All Checkbox for on Console API key Scopes Screen ([#477](https://github.com/appwrite/appwrite/issues/477))
- Added pagination and search for team memberships route ([#387](https://github.com/appwrite/appwrite/issues/387))
- Added pagination for projects list on the console home page.
- UI performance & accessibility improvements ([#406](https://github.com/appwrite/appwrite/pull/406))
- New UI micro-interactions and CSS fixes (@AnatoleLucet)
- Added toggle to hide/show secret keys and passwords inside the dashboard (@kodumbeats, [#535](https://github.com/appwrite/appwrite/issues/535))
### Upgrades
- Upgraded QR codes generator library (@PedroCisnerosSantana - [#475](https://github.com/appwrite/appwrite/issues/475))
- Upgraded Traefik image to version 2.3
- Upgraded MariaDB to version 10.5.5
- Upgraded Redis Docker image to version 6.0 (alpine)
- Upgraded Influxdb Docker image to version 1.8 (alpine)
- Upgraded Redis Resque queue library to version 1.3.6 ([#319](https://github.com/appwrite/appwrite/issues/319))
- Upgraded ClamAV container image to version 1.0.11 ([#412](https://github.com/appwrite/appwrite/issues/412))
- Upgraded device detctor to version 3.12.6
## Breaking Changes (Read before upgrading!)
- **Deprecated** `first` and `last` query params for documents list route in the database API
- **Deprecated** Deprectaed Pubjabi Translations ('pn')
- **Deprecated** `PATCH /account/prefs` is now updating the prefs payload and not just merging it
@ -91,13 +107,8 @@
- Fixed Bug when trying to overwrite OAuth cookie in the Flutter SDK
- Fixed OAuth redirect when using the self-hosted instance default success URL ([#454](https://github.com/appwrite/appwrite/issues/454))
- Fixed bug denying authentication with Github OAuth provider
- New OAuth adapter for Box.com
- New OAuth adapter for PayPal sandbox
- Fixed a bug making read permission overwrite write permission in some cases
## Breaking Changes
- **Deprecated** `first` and `last` query params for documents list route in the database API
- **Deprecated** Deprecated Punjabi Translations ('pn')
- Fixed consistent property names in databases by enforcing camel case
## Security
@ -106,6 +117,7 @@
- Now using your `_APP_SYSTEM_EMAIL_ADDRESS` as the email address for issuing and renewing SSL certificates
- Block iframe access to Appwrite console using the `X-Frame-Options` header.
- Fixed `roles` param input validator
- API Keys are now stored encrypted
# Version 0.6.2 (PRE-RELEASE)

View file

@ -72,7 +72,23 @@ cd appwrite
docker-compose up -d
```
After finishing the installation process, you can start writing and editing code. To compile new CSS and JS distribution files, use 'less' and 'build' tasks using gulp as a task manager.
### Code Autocompletion
To get proper autocompletion for all the different functions and classes in the codebase, you'll need to install Appwrite dependencies on your local machine. You can easily do that with PHP's package manager, [Composer](https://getcomposer.org/). If you don't have Composer installed, you can use the Docker Hub image to get the same result:
```bash
docker run --rm --interactive --tty \
--volume $PWD:/app \
composer install
```
### User Interface
Appwrite uses an internal micro-framework called Litespeed.js to build simple UI components in vanilla JS and [less](http://lesscss.org/) for compiling CSS code. To apply any of your changes to the UI, use the `gulp build` or `gulp less` commands, and restart the Appwrite main container to load the new static files to memory using `docker-compose restart appwrite`.
### Get Started
After finishing the installation process, you can start writing and editing code.
## Architecture
@ -216,7 +232,7 @@ For us to find the right balance, please open an issue explaining your ideas bef
This will allow the Appwrite community to have sufficient discussion about the new feature value and how it fits in the product roadmap and vision.
This is also important for the Appwrite lead developers to be able to give technical input and different emphasis regarding the feature design and architecture.
This is also important for the Appwrite lead developers to be able to give technical input and different emphasis regarding the feature design and architecture. Some bigger features might need to go through our [RFC process](https://github.com/appwrite/rfc).
## Build
@ -243,12 +259,29 @@ bash ./docker/environments/build.sh
## Tests
To run tests manually, use the Appwrite Docker CLI from your terminal:
To run all tests manually, use the Appwrite Docker CLI from your terminal:
```bash
docker-compose exec appwrite test
```
To run unit tests use:
```bash
docker-compose exec appwrite test /usr/src/code/tests/unit
```
To run end-2-end tests use:
```bash
docker-compose exec appwrite test /usr/src/code/tests/e2e
```
To run end-2-end tests for a spcific service use:
```bash
docker-compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName]
```
## Benchmarking
You can use WRK Docker image to benchmark the server performance. Benchmarking is extremely useful when you want to compare how the server behaves before and after a change has been applied. Replace [APPWRITE_HOSTNAME_OR_IP] with your Appwrite server hostname or IP. Note that localhost is not accessible from inside the WRK container.

View file

@ -99,7 +99,12 @@ ENV _APP_SERVER=swoole \
_APP_FUNCTIONS_MEMORY_SWAP=128 \
_APP_SETUP=self-hosted \
_APP_VERSION=$VERSION \
_APP_USAGE_STATS=enabled \
# 14 Days = 1209600 s
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600 \
_APP_MAINTENANCE_RETENTION_AUDIT=1209600 \
# 1 Day = 86400 s
_APP_MAINTENANCE_RETENTION_ABUSE=86400 \
_APP_MAINTENANCE_INTERVAL=86400
#ENV _APP_SMTP_SECURE ''
#ENV _APP_SMTP_USERNAME ''

View file

@ -1,185 +0,0 @@
FROM ubuntu:18.04 AS builder
LABEL maintainer="team@appwrite.io"
ARG TESTING=false
ENV TZ=Asia/Tel_Aviv \
DEBIAN_FRONTEND=noninteractive \
PHP_VERSION=7.4 \
PHP_REDIS_VERSION=5.2.1
RUN \
apt-get update && \
apt-get install -y --no-install-recommends --no-install-suggests ca-certificates software-properties-common wget git openssl && \
LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php && \
apt-get update && \
apt-get install -y --no-install-recommends --no-install-suggests make php$PHP_VERSION php$PHP_VERSION-dev zip unzip php$PHP_VERSION-zip && \
# Redis Extension
wget -q https://github.com/phpredis/phpredis/archive/$PHP_REDIS_VERSION.tar.gz && \
tar -xf $PHP_REDIS_VERSION.tar.gz && \
cd phpredis-$PHP_REDIS_VERSION && \
phpize$PHP_VERSION && \
./configure && \
make && \
# Composer
wget https://getcomposer.org/composer.phar && \
chmod +x ./composer.phar && \
mv ./composer.phar /usr/bin/composer && \
#Brotli
cd / && \
git clone https://github.com/eustas/ngx_brotli.git && \
cd ngx_brotli && git submodule update --init && cd ..
WORKDIR /usr/local/src/
# Updating PHP Dependencies and Auto-loading...
ENV TESTING=$TESTING
COPY composer.* /usr/local/src/
RUN composer update --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM ubuntu:18.04
LABEL maintainer="team@appwrite.io"
ARG VERSION=dev
ENV TZ=Asia/Tel_Aviv \
DEBIAN_FRONTEND=noninteractive \
PHP_VERSION=7.4 \
_APP_SERVER=nginx \
_APP_ENV=production \
_APP_DOMAIN=localhost \
_APP_DOMAIN_TARGET=localhost \
_APP_HOME=https://appwrite.io \
_APP_EDITION=community \
_APP_OPTIONS_ABUSE=enabled \
_APP_OPTIONS_FORCE_HTTPS=disabled \
_APP_OPENSSL_KEY_V1=your-secret-key \
_APP_STORAGE_LIMIT=10000000 \
_APP_STORAGE_ANTIVIRUS=enabled \
_APP_REDIS_HOST=redis \
_APP_REDIS_PORT=6379 \
_APP_DB_HOST=mariadb \
_APP_DB_PORT=3306 \
_APP_DB_USER=root \
_APP_DB_PASS=password \
_APP_DB_SCHEMA=appwrite \
_APP_INFLUXDB_HOST=influxdb \
_APP_INFLUXDB_PORT=8086 \
_APP_STATSD_HOST=telegraf \
_APP_STATSD_PORT=8125 \
_APP_SMTP_HOST=smtp \
_APP_SMTP_PORT=25 \
_APP_SETUP=self-hosted \
_APP_VERSION=$VERSION
#ENV _APP_SMTP_SECURE ''
#ENV _APP_SMTP_USERNAME ''
#ENV _APP_SMTP_PASSWORD ''
COPY --from=builder /phpredis-5.2.1/modules/redis.so /usr/lib/php/20190902/
COPY --from=builder /phpredis-5.2.1/modules/redis.so /usr/lib/php/20190902/
COPY --from=builder /ngx_brotli /ngx_brotli
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN \
apt-get update && \
apt-get install -y --no-install-recommends --no-install-suggests wget ca-certificates software-properties-common build-essential libpcre3-dev zlib1g-dev libssl-dev openssl gnupg htop supervisor && \
LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php && \
add-apt-repository universe && \
add-apt-repository ppa:certbot/certbot && \
apt-get update && \
apt-get install -y --no-install-recommends --no-install-suggests php$PHP_VERSION php$PHP_VERSION-fpm \
php$PHP_VERSION-mysqlnd php$PHP_VERSION-curl php$PHP_VERSION-imagick php$PHP_VERSION-mbstring php$PHP_VERSION-dom certbot && \
# Nginx
wget http://nginx.org/download/nginx-1.19.0.tar.gz && \
tar -xzvf nginx-1.19.0.tar.gz && rm nginx-1.19.0.tar.gz && \
cd nginx-1.19.0 && \
./configure --prefix=/usr/share/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/run/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--user=www-data \
--group=www-data \
--build=Ubuntu \
--with-http_gzip_static_module \
--with-http_ssl_module \
--with-http_v2_module \
--add-module=/ngx_brotli && \
make && \
make install && \
rm -rf ../nginx-1.19.0 && \
# Redis Extension
echo extension=redis.so >> /etc/php/$PHP_VERSION/fpm/conf.d/redis.ini && \
echo extension=redis.so >> /etc/php/$PHP_VERSION/cli/conf.d/redis.ini && \
# Cleanup
cd ../ && \
apt-get purge -y --auto-remove wget software-properties-common build-essential libpcre3-dev zlib1g-dev libssl-dev gnupg && \
apt-get clean && \
rm -rf /ngx_brotli && \
rm -rf /var/lib/apt/lists/*
# Set Upload Limit (default to 100MB)
RUN echo "upload_max_filesize = ${_APP_STORAGE_LIMIT}" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
RUN echo "post_max_size = ${_APP_STORAGE_LIMIT}" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
RUN echo "opcache.preload_user=www-data" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
RUN echo "opcache.preload=/usr/src/code/app/preload.php" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
RUN echo "opcache.enable_cli = 1" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
# Add logs file
RUN echo "" >> /var/log/appwrite.log
# Nginx Configuration (with self-signed ssl certificates)
COPY ./docker/nginx.conf.template /etc/nginx/nginx.conf.template
COPY ./docker/ssl/cert.pem /etc/nginx/ssl/cert.pem
COPY ./docker/ssl/key.pem /etc/nginx/ssl/key.pem
# PHP Configuration
RUN mkdir -p /var/run/php
COPY ./docker/www.conf /etc/php/$PHP_VERSION/fpm/pool.d/www.conf
# Add PHP Source Code
COPY ./app /usr/src/code/app
COPY ./bin /usr/local/bin
COPY ./docs /usr/src/code/docs
COPY ./public /usr/src/code/public
COPY ./src /usr/src/code/src
COPY --from=builder /usr/local/src/vendor /usr/src/code/vendor
RUN mkdir -p /storage/uploads && \
mkdir -p /storage/cache && \
mkdir -p /storage/config && \
mkdir -p /storage/certificates && \
mkdir -p /storage/functions && \
chown -Rf www-data.www-data /storage/uploads && chmod -Rf 0755 /storage/uploads && \
chown -Rf www-data.www-data /storage/cache && chmod -Rf 0755 /storage/cache && \
chown -Rf www-data.www-data /storage/config && chmod -Rf 0755 /storage/config && \
chown -Rf www-data.www-data /storage/certificates && chmod -Rf 0755 /storage/certificates && \
chown -Rf www-data.www-data /storage/functions && chmod -Rf 0755 /storage/functions
# Supervisord Conf
COPY ./docker/supervisord.conf /etc/supervisord.conf
# Executables
RUN chmod +x /usr/local/bin/start
RUN chmod +x /usr/local/bin/doctor
RUN chmod +x /usr/local/bin/migrate
RUN chmod +x /usr/local/bin/test
# Letsencrypt Permissions
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
EXPOSE 80
WORKDIR /usr/src/code
CMD ["/bin/bash", "/usr/local/bin/start"]

View file

@ -53,7 +53,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.0 --version 0.7.0
appwrite/appwrite:0.7.0
```
### Windows
@ -65,7 +65,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.0 --version 0.7.0
appwrite/appwrite:0.7.0
```
#### PowerShell
@ -75,7 +75,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.0 --version 0.7.0
appwrite/appwrite:0.7.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.
@ -104,7 +104,7 @@ Getting started with Appwrite is as easy as creating a new project, choosing you
* [**Teams**](https://appwrite.io/docs/client/teams) - Manage and group users in teams. Manage memberships, invites, and user roles within a team.
* [**Database**](https://appwrite.io/docs/client/database) - Manage database collections and documents. Read, create, update, and delete documents and filter lists of documents collections using an advanced filter with graph-like capabilities.
* [**Storage**](https://appwrite.io/docs/client/storage) - Manage storage files. Read, create, delete, and preview files. Manipulate the preview of your files to fit your app perfectly. All files are scanned by ClamAV and stored in a secure and encrypted way.
* [**Functions**](https://appwrite.io/docs/server/functions) - Customize your Appwrite server by executing your custom code in an isolate environment, you can trigger your code on any Appwrite system event, manually or using a CRON schedule.
* [**Functions**](https://appwrite.io/docs/server/functions) - Customize your Appwrite server by executing your custom code in a secure, isolated environment. You can trigger your code on any Appwrite system event, manually or using a CRON schedule.
* [**Locale**](https://appwrite.io/docs/client/locale) - Track your user's location, and manage your app locale-based data.
* [**Avatars**](https://appwrite.io/docs/client/avatars) - Manage your users' avatars, countries' flags, browser icons, credit card symbols, and generate QR codes.
@ -124,8 +124,9 @@ Below is a list of currently supported platforms and languages. If you wish to h
* ✅   [Deno](https://github.com/appwrite/sdk-for-deno) - **Beta** (Maintained by the Appwrite Team)
* ✅   [Ruby](https://github.com/appwrite/sdk-for-ruby) - **Beta** (Maintained by the Appwrite Team)
* ✅   [Python](https://github.com/appwrite/sdk-for-python) - **Beta** (Maintained by the Appwrite Team)
* ✅   [.NET](https://github.com/appwrite/sdk-for-dotnet) - **Experimental** (Maintained by the Appwrite Team)
* ✅   [Dart](https://github.com/appwrite/sdk-for-dart) **Experimental** (Maintained by the Appwrite Team)
* ✅   [Go](https://github.com/appwrite/sdk-for-go) **Work in progress** (Maintained by the Appwrite Team)
* ✅   [Dart](https://github.com/appwrite/sdk-for-dart) **Work in progress** (Maintained by the Appwrite Team)
Looking for more SDKs? - Help us by contributing a pull request to our [SDK Generator](https://github.com/appwrite/sdk-generator)!

View file

@ -228,7 +228,7 @@ $collections = [
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Password Update Date',
'key' => 'password-update',
'key' => 'passwordUpdate',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => true,
@ -827,6 +827,7 @@ $collections = [
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'filter' => ['encrypt'],
],
],
],

View file

@ -1,13 +1,29 @@
<?php
return [
'node-14' => [
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',
@ -16,6 +32,7 @@ return [
'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',
@ -24,6 +41,7 @@ return [
'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',
@ -32,6 +50,16 @@ return [
'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',
@ -40,6 +68,7 @@ return [
'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',
@ -48,6 +77,7 @@ return [
'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',
@ -56,13 +86,53 @@ return [
'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],
],
// 'dart-2.8' => [
// 'name' => 'Dart',
// 'version' => '2.8',
// 'base' => 'google/dart:2.8',
// 'image' => 'appwrite/env-dart:2.8',
// 'build' => '/usr/src/code/docker/environments/dart-2.8',
// 'logo' => 'dart.png',
// ],
];
'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

@ -96,6 +96,7 @@ return [
// 'enabled' => false,
// 'dev' => false,
// 'beta' => false,
// 'dev' => false,
// 'family' => APP_PLATFORM_CLIENT,
// 'prism' => 'java',
// 'source' => false,
@ -250,17 +251,33 @@ return [
'gitRepoName' => 'sdk-for-java',
'gitUserName' => 'appwrite',
],
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.0.3',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
'beta' => true,
'dev' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'csharp',
'source' => \realpath(__DIR__ . '/../sdks/server-dotnet'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-dotnet.git',
'gitRepoName' => 'sdk-for-dotnet',
'gitUserName' => 'appwrite',
],
[
'key' => 'dart',
'name' => 'Dart',
'version' => '0.0.1',
'version' => '0.1.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => '',
'enabled' => false,
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
'beta' => true,
'dev' => false,
'dev' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'java',
'prism' => 'dart',
'source' => \realpath(__DIR__ . '/../sdks/server-dart'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-dart.git',
'gitRepoName' => 'sdk-for-dart',

View file

@ -1,15 +1,8 @@
<?php
const ROLE_GUEST = 0;
const ROLE_MEMBER = 1;
const ROLE_ADMIN = 2;
const ROLE_DEVELOPER = 3;
const ROLE_OWNER = 4;
const ROLE_APP = 5;
const ROLE_SYSTEM = 6;
const ROLE_ALL = '*';
use Appwrite\Auth\Auth;
$logged = [
$member = [
'public',
'home',
'console',
@ -57,7 +50,7 @@ $admins = [
];
return [
ROLE_GUEST => [
Auth::USER_ROLE_GUEST => [
'label' => 'Guest',
'scopes' => [
'public',
@ -71,23 +64,23 @@ return [
'execution.write',
],
],
ROLE_MEMBER => [
Auth::USER_ROLE_MEMBER => [
'label' => 'Member',
'scopes' => \array_merge($logged, []),
'scopes' => \array_merge($member, []),
],
ROLE_ADMIN => [
Auth::USER_ROLE_ADMIN => [
'label' => 'Admin',
'scopes' => \array_merge($admins, []),
],
ROLE_DEVELOPER => [
Auth::USER_ROLE_DEVELOPER => [
'label' => 'Developer',
'scopes' => \array_merge($admins, []),
],
ROLE_OWNER => [
Auth::USER_ROLE_OWNER => [
'label' => 'Owner',
'scopes' => \array_merge($logged, $admins, []),
'scopes' => \array_merge($member, $admins, []),
],
ROLE_APP => [
Auth::USER_ROLE_APP => [
'label' => 'Application',
'scopes' => ['health.read'],
],

View file

@ -2,98 +2,126 @@
return [
'/' => [
'key' => 'homepage',
'name' => 'Homepage',
'controller' => 'web/home.php',
'sdk' => false,
'docs' => false,
'tests' => false,
],
'console/' => [
'key' => 'console',
'name' => 'Console',
'controller' => 'web/console.php',
'sdk' => false,
'docs' => false,
'tests' => false,
],
'v1/account' => [
'key' => 'account',
'name' => 'Account',
'description' => '/docs/services/account.md',
'controller' => 'api/account.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/avatars' => [
'key' => 'avatars',
'name' => 'Avatars',
'description' => '/docs/services/avatars.md',
'controller' => 'api/avatars.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/database' => [
'key' => 'database',
'name' => 'Database',
'description' => '/docs/services/database.md',
'controller' => 'api/database.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/locale' => [
'key' => 'locale',
'name' => 'Locale',
'description' => '/docs/services/locale.md',
'controller' => 'api/locale.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/health' => [
'key' => 'health',
'name' => 'Health',
'description' => '/docs/services/health.md',
'controller' => 'api/health.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/projects' => [
'key' => 'projects',
'name' => 'Projects',
'controller' => 'api/projects.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/storage' => [
'key' => 'storage',
'name' => 'Storage',
'description' => '/docs/services/storage.md',
'controller' => 'api/storage.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/teams' => [
'key' => 'teams',
'name' => 'Teams',
'description' => '/docs/services/teams.md',
'controller' => 'api/teams.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/users' => [
'key' => 'users',
'name' => 'Users',
'description' => '/docs/services/users.md',
'controller' => 'api/users.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/functions' => [
'name' => 'Users',
'key' => 'functions',
'name' => 'Functions',
'description' => '/docs/services/functions.md',
'controller' => 'api/functions.php',
'sdk' => true,
'docs' => true,
'tests' => false,
],
'v1/mock' => [
'key' => 'mock',
'name' => 'Mock',
'description' => '',
'controller' => 'mock.php',
'sdk' => false,
'docs' => false,
'tests' => true,
],
'v1/graphql' => [
'key' => 'graphql',
'name' => 'GraphQL',
'description' => 'GraphQL Endpoint',
'controller' => 'api/graphql.php',
'sdk' => false,
'docs' => false,
'tests' => false,
],
];

View file

@ -1,196 +1,404 @@
<?php
use Utopia\Config\Config;
return [
[
'name' => '_APP_ENV',
'default' => 'production',
'required' => false,
'question' => '',
'category' => 'General',
'description' => '',
'variables' => [
[
'name' => '_APP_ENV',
'description' => 'Set your server running environment. By default, the var is set to \'development\'. When deploying to production, change it to: \'production\'.',
'introduction' => '',
'default' => 'production',
'required' => false,
'question' => '',
],
[
'name' => '_APP_OPTIONS_ABUSE',
'description' => 'Allows you to disable abuse checks and API rate limiting. By default, set to \'enabled\'. To cancel the abuse checking, set to \'disabled\'. It is not recommended to disable this check-in a production environment.',
'introduction' => '',
'default' => 'enabled',
'required' => false,
'question' => '',
],
[
'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.',
'introduction' => '',
'default' => 'enabled',
'required' => false,
'question' => '',
],
[
'name' => '_APP_OPENSSL_KEY_V1',
'description' => 'This is your server private secret key that is used to encrypt all sensitive data on your server. Appwrite server encrypts all secret data on your server like webhooks, HTTP passwords, user sessions, and storage files. The var is not set by default, if you wish to take advantage of Appwrite encryption capabilities you should change it and make sure to **keep it a secret and have a backup for it**.',
'introduction' => '',
'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',
],
[
'name' => '_APP_DOMAIN',
'description' => 'Your Appwrite domain address. When setting a public suffix domain, Appwrite will attempt to issue a valid SSL certificate automatically. When used with a dev domain, Appwrite will assign a self-signed SSL certificate. The default value is \'localhost\'.',
'introduction' => '',
'default' => 'localhost',
'required' => true,
'question' => 'Enter your Appwrite hostname',
],
[
'name' => '_APP_DOMAIN_TARGET',
'description' => 'A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite \'_APP_DOMAIN\' variable. The default value is \'localhost\'.',
'introduction' => '',
'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.',
],
[
'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.',
'introduction' => '',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_CONSOLE_WHITELIST_DOMAINS',
'description' => 'This option allows you to limit creation of users to Appwrite console for users sharing the same email domains. This option is very useful for team working with company emails domain.\n\nTo enable this option, pass a list of allowed email domains separated by a comma.',
'introduction' => '',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_CONSOLE_WHITELIST_IPS',
'description' => 'This last option allows you to limit creation of users in Appwrite console for users sharing the same set of IP addresses. This option is very useful for team working with a VPN service or a company IP.\n\nTo enable/activate this option, pass a list of allowed IP addresses separated by a comma.',
'introduction' => '',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SYSTEM_EMAIL_NAME',
'description' => 'This is the sender name value that will appear on email messages sent to developers from the Appwrite console. The default value is: \'Appwrite\'. You can use url encoded strings for spaces and special chars.',
'introduction' => '0.7.0',
'default' => 'Appwrite',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SYSTEM_EMAIL_ADDRESS',
'description' => 'This is the sender email address that will appear on email messages sent to developers from the Appwrite console. The default value is \'team@appwrite.io\'. You should choose an email address that is allowed to be used from your SMTP server to avoid the server email ending in the users\' SPAM folders.',
'introduction' => '0.7.0',
'default' => 'team@appwrite.io',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SYSTEM_RESPONSE_FORMAT',
'description' => 'Use this environment variable to set the default Appwrite HTTP response format to support an older version of Appwrite. This option is useful to overcome breaking changes between versions. You can also use the `X-Appwrite-Response-Format` HTTP request header to overwrite the response for a specific request. This variable accepts any valid Appwrite version. To use the current version format, leave the value of the variable empty.',
'introduction' => '0.7.0',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_USAGE_STATS',
'description' => 'This variable allows you to disable the collection and displaying of usage stats. This value is set to \'enabled\' by default, to disable the usage stats set the value to \'disabled\'. When disabled, it\'s recommended to turn off the Worker Usage, Influxdb and Telegraf containers for better resource usage.',
'introduction' => '0.7.0',
'default' => 'enabled',
'required' => false,
'question' => '',
],
],
],
[
'name' => '_APP_OPTIONS_ABUSE',
'default' => 'enabled',
'required' => false,
'question' => '',
'category' => 'Redis',
'description' => 'Appwrite uses a Redis server for managing cache, queues and scheduled tasks. The Redis env vars are used to allow Appwrite server to connect to the Redis container.',
'variables' => [
[
'name' => '_APP_REDIS_HOST',
'description' => 'Redis server hostname address. Default value is: \'redis\'.',
'introduction' => '',
'default' => 'redis',
'required' => false,
'question' => '',
],
[
'name' => '_APP_REDIS_PORT',
'description' => 'Redis server TCP port. Default value is: \'6379\'.',
'introduction' => '',
'default' => '6379',
'required' => false,
'question' => '',
],
],
],
[
'name' => '_APP_OPTIONS_FORCE_HTTPS',
'default' => 'enabled',
'required' => false,
'question' => '',
'category' => 'MariaDB',
'description' => 'Appwrite is using a MariaDB server for managing persistent database data. The MariaDB env vars are used to allow Appwrite server to connect to the MariaDB container.',
'variables' => [
[
'name' => '_APP_DB_HOST',
'description' => 'MariaDB server host name address. Default value is: \'mariadb\'.',
'introduction' => '',
'default' => 'mariadb',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_PORT',
'description' => 'MariaDB server TCP port. Default value is: \'3306\'.',
'introduction' => '',
'default' => '3306',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_SCHEMA',
'description' => 'MariaDB server database schema. Default value is: \'appwrite\'.',
'introduction' => '',
'default' => 'appwrite',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_USER',
'description' => 'MariaDB server user name. Default value is: \'user\'.',
'introduction' => '',
'default' => 'user',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_PASS',
'description' => 'MariaDB server user password. Default value is: \'password\'.',
'introduction' => '',
'default' => 'password',
'required' => false,
'question' => '',
],
],
],
[
'name' => '_APP_OPENSSL_KEY_V1',
'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',
'category' => 'InfluxDB',
'description' => 'Appwrite uses an InfluxDB server for managing time-series data and server stats. The InfluxDB env vars are used to allow Appwrite server to connect to the InfluxDB container.',
'variables' => [
[
'name' => '_APP_INFLUXDB_HOST',
'description' => 'InfluxDB server host name address. Default value is: \'influxdb\'.',
'introduction' => '',
'default' => 'influxdb',
'required' => false,
'question' => '',
],
[
'name' => '_APP_INFLUXDB_PORT',
'description' => 'InfluxDB server TCP port. Default value is: \'8086\'.',
'introduction' => '',
'default' => '8086',
'required' => false,
'question' => '',
],
],
],
[
'name' => '_APP_DOMAIN',
'default' => 'localhost',
'required' => true,
'question' => 'Enter your Appwrite hostname',
'category' => 'StatsD',
'description' => 'Appwrite uses a StatsD server for aggregating and sending stats data over a fast UDP connection. The StatsD env vars are used to allow Appwrite server to connect to the StatsD container.',
'variables' => [
[
'name' => '_APP_STATSD_HOST',
'description' => 'StatsD server host name address. Default value is: \'telegraf\'.',
'introduction' => '',
'default' => 'telegraf',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STATSD_PORT',
'description' => 'StatsD server TCP port. Default value is: \'8125\'.',
'introduction' => '',
'default' => '8125',
'required' => false,
'question' => '',
],
],
],
[
'name' => '_APP_DOMAIN_TARGET',
'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.",
'category' => 'SMTP',
'description' => 'Appwrite is using an SMTP server for emailing your projects users and server admins. The SMTP env vars are used to allow Appwrite server to connect to the SMTP container.\n\nIf running in production, it might be easier to use a 3rd party SMTP server as it might be a little more difficult to set up a production SMTP server that will not send all your emails into your user\'s SPAM folder.',
'variables' => [
[
'name' => '_APP_SMTP_HOST',
'description' => 'SMTP server host name address. Default value is: \'smtp\'. Pass an empty string to disable all mail sending from the server.',
'introduction' => '',
'default' => 'smtp',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_PORT',
'description' => 'SMTP server TCP port. Default value is: \'25\'.',
'introduction' => '',
'default' => '25',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_SECURE',
'description' => 'SMTP secure connection protocol. Empty by default, change to \'tls\' if running on a secure connection.',
'introduction' => '',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_USERNAME',
'description' => 'SMTP server user name. Empty by default.',
'introduction' => '',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_PASSWORD',
'description' => 'SMTP server user password. Empty by default.',
'introduction' => '',
'default' => '',
'required' => false,
'question' => '',
],
],
],
[
'name' => '_APP_REDIS_HOST',
'default' => 'redis',
'required' => false,
'question' => '',
'category' => 'Storage',
'description' => '',
'variables' => [
[
'name' => '_APP_STORAGE_LIMIT',
'description' => 'Maximun file size allowed for file upload. The default value is 10MB limitation. You should pass your size limit value in bytes.',
'introduction' => '0.7.0',
'default' => '10000000',
'required' => false,
'question' => '',
],
[
'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.',
'introduction' => '',
'default' => 'enabled',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_HOST',
'description' => 'ClamAV server host name address. Default value is: \'clamav\'.',
'introduction' => '0.7.0',
'default' => 'clamav',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_PORT',
'description' => 'ClamAV server TCP port. Default value is: \'3310\'.',
'introduction' => '0.7.0',
'default' => '3310',
'required' => false,
'question' => '',
],
],
],
[
'name' => '_APP_REDIS_PORT',
'default' => '6379',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_HOST',
'default' => 'mariadb',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_PORT',
'default' => '3306',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_SCHEMA',
'default' => 'appwrite',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_USER',
'default' => 'user',
'required' => false,
'question' => '',
],
[
'name' => '_APP_DB_PASS',
'default' => 'password',
'required' => false,
'question' => '',
],
[
'name' => '_APP_INFLUXDB_HOST',
'default' => 'influxdb',
'required' => false,
'question' => '',
],
[
'name' => '_APP_INFLUXDB_PORT',
'default' => '8086',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STATSD_HOST',
'default' => 'telegraf',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STATSD_PORT',
'default' => '8125',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_HOST',
'default' => 'smtp',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_PORT',
'default' => '25',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_SECURE',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_USERNAME',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_PASSWORD',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_LIMIT',
'default' => '10000000',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS',
'default' => 'enabled',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_HOST',
'default' => 'clamav',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_PORT',
'default' => '3310',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_TIMEOUT',
'default' => '900',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_CONTAINERS',
'default' => '10',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_CPUS',
'default' => '1',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
'default' => '128',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_MEMORY_SWAP',
'default' => '128',
'required' => false,
'question' => '',
],
[
'name' => '_APP_MAINTENANCE_INTERVAL',
'default' => '86400',
'required' => false,
'question' => '',
'category' => 'Functions',
'description' => '',
'variables' => [
[
'name' => '_APP_FUNCTIONS_TIMEOUT',
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds.',
'introduction' => '0.7.0',
'default' => '900',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_CONTAINERS',
'description' => 'The maximum number of containers Appwrite is allowed to keep alive in the background for function environments. Running containers allow faster execution time as there is no need to recreate each container every time a function gets executed. The default value is 10.',
'introduction' => '0.7.0',
'default' => '10',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_CPUS',
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is 1.',
'introduction' => '0.7.0',
'default' => '1',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is 128.',
'introduction' => '0.7.0',
'default' => '256',
'required' => false,
'question' => '',
],
[
'name' => '_APP_FUNCTIONS_MEMORY_SWAP',
'description' => 'The maximum amount of swap memory a single cloud function is allowed to use in megabytes. The default value is 128.',
'introduction' => '0.7.0',
'default' => '256',
'required' => false,
'question' => '',
],
[
'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'))),
'introduction' => '0.7.0',
'default' => 'node-14.5,deno-1.6,php-7.4,python-3.8,ruby-3.0,dotnet-5.0',
'required' => false,
'question' => '',
],
],
[
'category' => 'Maintenance',
'description' => '',
'variables' => [
[
'name' => '_APP_MAINTENANCE_INTERVAL',
'description' => 'Interval value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 86400 seconds (1 day).',
'introduction' => '0.7.0',
'default' => '86400',
'required' => false,
'question' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION',
'description' => 'The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days).',
'introduction' => '0.7.0',
'default' => '1209600',
'required' => false,
'question' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
'description' => 'IThe maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
'introduction' => '0.7.0',
'default' => '1209600',
'required' => false,
'question' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
'description' => 'The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day).',
'introduction' => '0.7.0',
'default' => '86400',
'required' => false,
'question' => '',
]
],
],
],
];

View file

@ -1,5 +1,6 @@
<?php
use Ahc\Jwt\JWT;
use Utopia\App;
use Utopia\Exception;
use Utopia\Config\Config;
@ -99,7 +100,7 @@ App::post('/v1/account')
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash($password),
'password-update' => \time(),
'passwordUpdate' => \time(),
'registration' => \time(),
'reset' => false,
'name' => $name,
@ -511,7 +512,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()),
'password-update' => \time(),
'passwordUpdate' => \time(),
'registration' => \time(),
'reset' => false,
'name' => $name,
@ -637,6 +638,49 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
;
});
App::post('/v1/account/jwt')
->desc('Create Account JWT')
->groups(['api', 'account'])
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createJWT')
->label('sdk.description', '/docs/references/account/create-jwt.md')
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->inject('response')
->inject('user')
->action(function ($response, $user) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$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()) {
throw new Exception('No valid session found', 401);
}
$jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => $jwt->encode([
// 'uid' => 1,
// 'aud' => 'http://site.com',
// 'scopes' => ['user'],
// 'iss' => 'http://api.mysite.com',
'userId' => $user->getId(),
'sessionId' => $session->getId(),
])]), Response::MODEL_JWT);
});
App::get('/v1/account')
->desc('Get Account')
->groups(['api', 'account'])
@ -674,7 +718,7 @@ App::get('/v1/account/prefs')
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$prefs = $user->getAttribute('prefs', new \stdClass);
$prefs = $user->getAttribute('prefs', new \stdClass());
$response->dynamic(new Document($prefs), Response::MODEL_ANY);
});
@ -1412,7 +1456,7 @@ App::put('/v1/account/recovery')
$profile = $projectDB->updateDocument(\array_merge($profile->getArrayCopy(), [
'password' => Auth::passwordHash($password),
'password-update' => \time(),
'passwordUpdate' => \time(),
'emailVerification' => true,
]));

View file

@ -383,8 +383,9 @@ App::get('/v1/avatars/qr')
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
$options = new QROptions([
'quietzone' => $size,
'outputType' => QRCode::OUTPUT_IMAGICK
'addQuietzone' => true,
'quietzoneSize' => $margin,
'outputType' => QRCode::OUTPUT_IMAGICK,
]);
$qrcode = new QRCode($options);
@ -393,10 +394,14 @@ App::get('/v1/avatars/qr')
$response->addHeader('Content-Disposition', 'attachment; filename="qr.png"');
}
$resize = new Resize($qrcode->render($text));
$resize->crop((int) $size, (int) $size);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($qrcode->render($text))
->send($resize->output('png', 9))
;
});

View file

@ -4,11 +4,11 @@ use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Validator\UID;
use Appwrite\Storage\Storage;
use Appwrite\Storage\Validator\File;
use Appwrite\Storage\Validator\FileSize;
use Appwrite\Storage\Validator\FileType;
use Appwrite\Storage\Validator\Upload;
use Utopia\Storage\Storage;
use Utopia\Storage\Validator\File;
use Utopia\Storage\Validator\FileExt;
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
use Appwrite\Utopia\Response;
use Appwrite\Task\Validator\Cron;
use Utopia\App;
@ -44,6 +44,9 @@ App::post('/v1/functions')
->inject('response')
->inject('projectDB')
->action(function ($name, $execute, $env, $vars, $events, $schedule, $timeout, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$function = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_FUNCTIONS,
'$permissions' => [
@ -91,6 +94,9 @@ App::get('/v1/functions')
->inject('response')
->inject('projectDB')
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
@ -122,6 +128,9 @@ App::get('/v1/functions/:functionId')
->inject('response')
->inject('projectDB')
->action(function ($functionId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
@ -156,96 +165,100 @@ App::get('/v1/functions/:functionId/usage')
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404);
}
$period = [
'24h' => [
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
'group' => '30m',
],
'7d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'30d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'90d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
];
$client = $register->get('influxdb');
$executions = [];
$failures = [];
$compute = [];
if ($client) {
$start = $period[$range]['start']->format(DateTime::RFC3339);
$end = $period[$range]['end']->format(DateTime::RFC3339);
$database = $client->selectDB('telegraf');
// Executions
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$executions[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Failures
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' AND "functionStatus"=\'failed\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$failures[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Compute
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_time" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$compute[] = [
'value' => round((!empty($point['value'])) ? $point['value'] / 1000 : 0, 2), // minutes
'date' => \strtotime($point['time']),
];
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
$period = [
'24h' => [
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
'group' => '30m',
],
'7d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'30d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'90d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
];
$client = $register->get('influxdb');
$executions = [];
$failures = [];
$compute = [];
if ($client) {
$start = $period[$range]['start']->format(DateTime::RFC3339);
$end = $period[$range]['end']->format(DateTime::RFC3339);
$database = $client->selectDB('telegraf');
// Executions
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$executions[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Failures
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' AND "functionStatus"=\'failed\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$failures[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Compute
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_time" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$compute[] = [
'value' => round((!empty($point['value'])) ? $point['value'] / 1000 : 0, 2), // minutes
'date' => \strtotime($point['time']),
];
}
}
$response->json([
'range' => $range,
'executions' => [
'data' => $executions,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $executions)),
],
'failures' => [
'data' => $failures,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $failures)),
],
'compute' => [
'data' => $compute,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $compute)),
],
]);
} else {
$response->json([]);
}
$response->json([
'range' => $range,
'executions' => [
'data' => $executions,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $executions)),
],
'failures' => [
'data' => $failures,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $failures)),
],
'compute' => [
'data' => $compute,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $compute)),
],
]);
});
App::put('/v1/functions/:functionId')
@ -268,13 +281,19 @@ App::put('/v1/functions/:functionId')
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
->inject('response')
->inject('projectDB')
->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $projectDB) {
->inject('project')
->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $projectDB, $project) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Database\Document $project */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404);
}
$original = $function->getAttribute('schedule', '');
$cron = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? CronExpression::factory($schedule) : null;
$next = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : null;
@ -287,28 +306,23 @@ App::put('/v1/functions/:functionId')
'vars' => $vars,
'events' => $events,
'schedule' => $schedule,
'schedulePrevious' => null,
'scheduleNext' => $next,
'timeout' => $timeout,
'timeout' => $timeout,
]));
if ($next) {
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
]);
// ->setParam('projectId', $project->getId())
// ->setParam('event', $route->getLabel('event', ''))
// ->setParam('payload', [])
// ->setParam('functionId', null)
// ->setParam('executionId', null)
// ->setParam('trigger', 'event')
}
if (false === $function) {
throw new Exception('Failed saving function to DB', 500);
}
if ($next && $schedule !== $original) {
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'functionId' => $function->getId(),
'executionId' => null,
'trigger' => 'schedule',
]); // Async task rescheduale
}
$response->dynamic($function, Response::MODEL_FUNCTION);
});
@ -327,7 +341,12 @@ App::patch('/v1/functions/:functionId/tag')
->param('tag', '', new UID(), 'Tag unique ID.')
->inject('response')
->inject('projectDB')
->action(function ($functionId, $tag, $response, $projectDB) {
->inject('project')
->action(function ($functionId, $tag, $response, $projectDB, $project) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Database\Document $project */
$function = $projectDB->getDocument($functionId);
$tag = $projectDB->getDocument($tag);
@ -340,14 +359,23 @@ App::patch('/v1/functions/:functionId/tag')
}
$schedule = $function->getAttribute('schedule', '');
$cron = (!empty($function->getAttribute('tag')&& !empty($schedule))) ? CronExpression::factory($schedule) : null;
$next = (!empty($function->getAttribute('tag')&& !empty($schedule))) ? $cron->getNextRunDate()->format('U') : null;
$cron = (empty($function->getAttribute('tag')) && !empty($schedule)) ? CronExpression::factory($schedule) : null;
$next = (empty($function->getAttribute('tag')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : null;
$function = $projectDB->updateDocument(array_merge($function->getArrayCopy(), [
'tag' => $tag->getId(),
'scheduleNext' => $next,
]));
if ($next) { // Init first schedule
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'functionId' => $function->getId(),
'executionId' => null,
'trigger' => 'schedule',
]); // Async task rescheduale
}
if (false === $function) {
throw new Exception('Failed saving function to DB', 500);
}
@ -401,28 +429,33 @@ App::post('/v1/functions/:functionId/tags')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createTag')
->label('sdk.description', '/docs/references/functions/create-tag.md')
->label('sdk.packaging', true)
->label('sdk.request.type', 'multipart/form-data')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TAG)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('command', '', new Text('1028'), 'Code execution command.')
->param('code', [], new File(), 'Gzip file containing your code.', false)
// ->param('code', '', new Text(128), 'Code package. Use the '.APP_NAME.' code packager to create a deployable package file.')
->param('file', null, new File(), 'Gzip file with your code package.', false)
->inject('request')
->inject('response')
->inject('projectDB')
->inject('usage')
->action(function ($functionId, $command, $code, $request, $response, $projectDB, $usage) {
->action(function ($functionId, $command, $file, $request, $response, $projectDB, $usage) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $usage */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404);
}
$file = $request->getFiles('code');
$file = $request->getFiles('file');
$device = Storage::getDevice('functions');
$fileType = new FileType([FileType::FILE_TYPE_GZIP]);
$fileExt = new FileExt([FileExt::TYPE_GZIP]);
$fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
$upload = new Upload();
@ -435,10 +468,9 @@ App::post('/v1/functions/:functionId/tags')
$file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
$file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
// Check if file type is allowed (feature for project settings?)
// if (!$fileType->isValid($file['tmp_name'])) {
// throw new Exception('File type not allowed', 400);
// }
if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
throw new Exception('File type not allowed', 400);
}
if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit
throw new Exception('File size not allowed', 400);
@ -502,6 +534,9 @@ App::get('/v1/functions/:functionId/tags')
->inject('response')
->inject('projectDB')
->action(function ($functionId, $search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
@ -541,6 +576,9 @@ App::get('/v1/functions/:functionId/tags/:tagId')
->inject('response')
->inject('projectDB')
->action(function ($functionId, $tagId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
@ -577,6 +615,10 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
->inject('projectDB')
->inject('usage')
->action(function ($functionId, $tagId, $response, $projectDB, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $usage */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
@ -629,6 +671,8 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION)
->label('abuse-limit', 60)
->label('abuse-time', 60)
->param('functionId', '', new UID(), 'Function unique ID.')
// ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true)
->inject('response')
@ -721,6 +765,9 @@ App::get('/v1/functions/:functionId/executions')
->inject('response')
->inject('projectDB')
->action(function ($functionId, $search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
@ -760,6 +807,9 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->inject('response')
->inject('projectDB')
->action(function ($functionId, $executionId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {

View file

@ -2,8 +2,8 @@
use Utopia\App;
use Utopia\Exception;
use Appwrite\Storage\Device\Local;
use Appwrite\Storage\Storage;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Appwrite\ClamAV\Network;
use Appwrite\Event\Event;
@ -287,10 +287,6 @@ App::get('/v1/health/stats') // Currently only used internally
$response
->json([
'server' => [
'name' => 'nginx',
'version' => \shell_exec('nginx -v 2>&1'),
],
'storage' => [
'used' => Storage::human($device->getDirectorySize($device->getRoot().'/')),
'partitionTotal' => Storage::human($device->getPartitionTotalSpace()),

View file

@ -176,74 +176,82 @@ App::get('/v1/projects/:projectId/usage')
throw new Exception('Project not found', 404);
}
$period = [
'24h' => [
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
'group' => '30m',
],
'7d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'30d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'90d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
];
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
$client = $register->get('influxdb');
$requests = [];
$network = [];
$functions = [];
if ($client) {
$start = $period[$range]['start']->format(DateTime::RFC3339);
$end = $period[$range]['end']->format(DateTime::RFC3339);
$database = $client->selectDB('telegraf');
// Requests
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$requests[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Network
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$network[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Functions
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$functions[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
$period = [
'24h' => [
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
'group' => '30m',
],
'7d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'30d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'90d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
];
$client = $register->get('influxdb');
$requests = [];
$network = [];
$functions = [];
if ($client) {
$start = $period[$range]['start']->format(DateTime::RFC3339);
$end = $period[$range]['end']->format(DateTime::RFC3339);
$database = $client->selectDB('telegraf');
// Requests
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$requests[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Network
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$network[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Functions
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$functions[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
}
} else {
$requests = [];
$network = [];
$functions = [];
}
// Users
$projectDB->getCollection([

View file

@ -13,11 +13,11 @@ use Appwrite\ClamAV\Network;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\UID;
use Appwrite\Storage\Storage;
use Appwrite\Storage\Validator\File;
use Appwrite\Storage\Validator\FileSize;
use Appwrite\Storage\Validator\Upload;
use Appwrite\Storage\Compression\Algorithms\GZIP;
use Utopia\Storage\Storage;
use Utopia\Storage\Validator\File;
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
use Utopia\Storage\Compression\Algorithms\GZIP;
use Appwrite\Resize\Resize;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Utopia\Response;

View file

@ -324,7 +324,7 @@ App::post('/v1/teams/:teamId/memberships')
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash(Auth::passwordGenerator()),
'password-update' => \time(),
'passwordUpdate' => \time(),
'registration' => \time(),
'reset' => false,
'name' => $name,

View file

@ -62,7 +62,7 @@ App::post('/v1/users')
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash($password),
'password-update' => \time(),
'passwordUpdate' => \time(),
'registration' => \time(),
'reset' => false,
'name' => $name,
@ -165,7 +165,7 @@ App::get('/v1/users/:userId/prefs')
throw new Exception('User not found', 404);
}
$prefs = $user->getAttribute('prefs', '');
$prefs = $user->getAttribute('prefs', new \stdClass());
$response->dynamic(new Document($prefs), Response::MODEL_ANY);
});
@ -418,7 +418,6 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->param('userId', '', new UID(), 'User unique ID.')
->param('sessionId', null, new UID(), 'User unique session ID.')
->inject('response')
@ -449,6 +448,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
}
}
// TODO : Response filter implementation
$response->noContent();
});
@ -464,7 +464,6 @@ App::delete('/v1/users/:userId/sessions')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->param('userId', '', new UID(), 'User unique ID.')
->inject('response')
->inject('projectDB')
@ -492,6 +491,7 @@ App::delete('/v1/users/:userId/sessions')
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
// TODO : Response filter implementation
$response->noContent();
});
@ -507,7 +507,6 @@ App::delete('/v1/users/:userId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->param('userId', '', function () {return new UID();}, 'User unique ID.')
->inject('response')
->inject('projectDB')
@ -553,5 +552,6 @@ App::delete('/v1/users/:userId')
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
// TODO : Response filter implementation
$response->noContent();
});

View file

@ -14,27 +14,22 @@ use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Network\Validator\Origin;
use Appwrite\Storage\Device\Local;
use Appwrite\Storage\Storage;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Appwrite\Utopia\Response\Filters\V06;
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, $events, $audits, $usage, $deletes, $clients) {
App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $clients) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $console */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
/** @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 */
/** @var bool $mode */
/** @var array $clients */
@ -50,20 +45,17 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
throw new Exception('Missing or unknown project ID', 400);
}
$console->setAttribute('platforms', [ // Allways allow current host
'$collection' => Database::SYSTEM_COLLECTION_PLATFORMS,
'name' => 'Current Host',
'type' => 'web',
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
$referrer = $request->getReferer();
$origin = \parse_url($request->getOrigin($referrer), PHP_URL_HOST);
$protocol = \parse_url($request->getOrigin($referrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($referrer), PHP_URL_PORT);
$refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()).'://'.((\in_array($origin, $clients))
? $origin : 'localhost') . (!empty($port) ? ':'.$port : '');
? $origin : 'localhost').(!empty($port) ? ':'.$port : '');
$refDomain = (!$route->getLabel('origin', false)) // This route is publicly accessible
? $refDomain
: (!empty($protocol) ? $protocol : $request->getProtocol()).'://'.$origin.(!empty($port) ? ':'.$port : '');
$selfDomain = new Domain($request->getHostname());
$endDomain = new Domain((string)$origin);
@ -91,8 +83,21 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
: '.'.$request->getHostname()
);
Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId()));
/*
* Response format
*/
$responseFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($responseFormat) {
switch($responseFormat) {
case version_compare ($responseFormat , '0.6.2', '<=') :
Response::setFilter(new V06());
break;
default:
throw new Exception('No filter available for response format : '.$responseFormat, 400);
}
} else {
Response::setFilter(null);
}
/*
* Security Headers
@ -106,15 +111,13 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
}
$response->addHeader('Strict-Transport-Security', 'max-age='.(60 * 60 * 24 * 126)); // 126 days
}
}
$response
->addHeader('Server', 'Appwrite')
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url='.\urlencode($request->getURI()))
//->addHeader('X-Frame-Options', ($refDomain == 'http://localhost') ? 'SAMEORIGIN' : 'ALLOW-FROM ' . $refDomain)
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-SDK-Version, Cache-Control, Expires, Pragma')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-SDK-Version, Cache-Control, Expires, Pragma')
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Credentials', 'true')
@ -123,7 +126,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
/*
* Validate Client Domain - Check to avoid CSRF attack
* Adding Appwrite API domains to allow XDOMAIN communication
* Skip this check for non-web platforms which are not requiredto send an origin header
* Skip this check for non-web platforms which are not required to send an origin header
*/
$origin = $request->getOrigin($request->getReferer(''));
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
@ -162,27 +165,31 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
$roles = Config::getParam('roles', []);
$scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route
$scopes = $roles[$role]['scopes']; // Allowed scopes for user role
// Check if given key match project API keys
$key = $project->search('secret', $request->getHeader('x-appwrite-key', ''), $project->getAttribute('keys', []));
/*
* Try app auth when we have project key and no user
* Mock user to app and grant API key scopes in addition to default app scopes
*/
if (null !== $key && $user->isEmpty()) {
$user = new Document([
'$id' => '',
'status' => Auth::USER_STATUS_ACTIVATED,
'email' => 'app.'.$project->getId().'@service.'.$request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
$role = Auth::USER_ROLE_APP;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
$authKey = $request->getHeader('x-appwrite-key', '');
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
if (!empty($authKey)) { // API Key authentication
// Check if given key match project API keys
$key = $project->search('secret', $authKey, $project->getAttribute('keys', []));
/*
* Try app auth when we have project key and no user
* Mock user to app and grant API key scopes in addition to default app scopes
*/
if ($key && $user->isEmpty()) {
$user = new Document([
'$id' => '',
'status' => Auth::USER_STATUS_ACTIVATED,
'email' => 'app.'.$project->getId().'@service.'.$request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
$role = Auth::USER_ROLE_APP;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
}
}
if ($user->getId()) {
@ -219,99 +226,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
throw new Exception('Password reset is required', 412);
}
/*
* Background Jobs
*/
$events
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
->setParam('event', $route->getLabel('event', ''))
->setParam('payload', [])
->setParam('functionId', null)
->setParam('executionId', null)
->setParam('trigger', 'event')
;
$audits
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
->setParam('event', '')
->setParam('resource', '')
->setParam('userAgent', $request->getUserAgent(''))
->setParam('ip', $request->getIP())
->setParam('data', [])
;
$usage
->setParam('projectId', $project->getId())
->setParam('httpRequest', 1)
->setParam('httpUrl', $request->getHostname().$request->getURI())
->setParam('httpMethod', $request->getMethod())
->setParam('networkRequestSize', 0)
->setParam('networkResponseSize', 0)
->setParam('storage', 0)
;
$deletes
->setParam('projectId', $project->getId())
;
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'events', 'audits', 'usage', 'deletes', 'clients']);
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $mode) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @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 */
/** @var bool $mode */
if (!empty($events->getParam('event'))) {
if(empty($events->getParam('payload'))) {
$events->setParam('payload', $response->getPayload());
}
$webhooks = clone $events;
$functions = clone $events;
$webhooks
->setQueue('v1-webhooks')
->setClass('WebhooksV1')
->trigger();
$functions
->setQueue('v1-functions')
->setClass('FunctionsV1')
->trigger();
}
if (!empty($audits->getParam('event'))) {
$audits->trigger();
}
if (!empty($deletes->getParam('type')) && !empty($deletes->getParam('document'))) {
$deletes->trigger();
}
$route = $utopia->match($request);
if ($project->getId()
&& $mode !== APP_MODE_ADMIN //TODO: add check to make sure user is admin
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
$usage
->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
->setParam('networkResponseSize', $response->getSize())
->trigger()
;
}
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'mode']);
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'clients']);
App::options(function ($request, $response) {
/** @var Utopia\Swoole\Request $request */
@ -322,7 +237,7 @@ App::options(function ($request, $response) {
$response
->addHeader('Server', 'Appwrite')
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies')
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $origin)
->addHeader('Access-Control-Allow-Credentials', 'true')
@ -341,8 +256,11 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
$template = ($route) ? $route->getLabel('error', null) : null;
if (php_sapi_name() === 'cli') {
Console::error('[Error] Method: '.$route->getMethod());
Console::error('[Error] URL: '.$route->getURL());
if($route) {
Console::error('[Error] Method: '.$route->getMethod());
Console::error('[Error] URL: '.$route->getURL());
}
Console::error('[Error] Type: '.get_class($error));
Console::error('[Error] Message: '.$error->getMessage());
Console::error('[Error] File: '.$error->getFile());

View file

@ -8,7 +8,7 @@ use Utopia\Validator\Numeric;
use Utopia\Validator\Text;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Host;
use Appwrite\Storage\Validator\File;
use Utopia\Storage\Validator\File;
App::get('/v1/mock/tests/foo')
->desc('Mock a get request for SDK tests')

View file

@ -1,17 +1,29 @@
<?php
use Appwrite\Auth\Auth;
use Appwrite\Database\Validator\Authorization;
use Utopia\App;
use Utopia\Exception;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
App::init(function ($utopia, $request, $response, $project, $user, $register) {
App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes) {
/** @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 */
Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId()));
$route = $utopia->match($request);
@ -49,7 +61,105 @@ App::init(function ($utopia, $request, $response, $project, $user, $register) {
;
}
if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') {
$isPreviliggedUser = Auth::isPreviliggedUser(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
{
throw new Exception('Too many requests', 429);
}
}, ['utopia', 'request', 'response', 'project', 'user', 'register'], 'api');
/*
* Background Jobs
*/
$events
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
->setParam('event', $route->getLabel('event', ''))
->setParam('payload', [])
->setParam('functionId', null)
->setParam('executionId', null)
->setParam('trigger', 'event')
;
$audits
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
->setParam('event', '')
->setParam('resource', '')
->setParam('userAgent', $request->getUserAgent(''))
->setParam('ip', $request->getIP())
->setParam('data', [])
;
$usage
->setParam('projectId', $project->getId())
->setParam('httpRequest', 1)
->setParam('httpUrl', $request->getHostname().$request->getURI())
->setParam('httpMethod', $request->getMethod())
->setParam('networkRequestSize', 0)
->setParam('networkResponseSize', 0)
->setParam('storage', 0)
;
$deletes
->setParam('projectId', $project->getId())
;
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes'], 'api');
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $mode) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @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 */
/** @var bool $mode */
if (!empty($events->getParam('event'))) {
if(empty($events->getParam('payload'))) {
$events->setParam('payload', $response->getPayload());
}
$webhooks = clone $events;
$functions = clone $events;
$webhooks
->setQueue('v1-webhooks')
->setClass('WebhooksV1')
->trigger();
$functions
->setQueue('v1-functions')
->setClass('FunctionsV1')
->trigger();
}
if (!empty($audits->getParam('event'))) {
$audits->trigger();
}
if (!empty($deletes->getParam('type')) && !empty($deletes->getParam('document'))) {
$deletes->trigger();
}
$route = $utopia->match($request);
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
&& $project->getId()
&& $mode !== APP_MODE_ADMIN //TODO: add check to make sure user is admin
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
$usage
->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
->setParam('networkResponseSize', $response->getSize())
->trigger()
;
}
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'mode'], 'api');

View file

@ -37,6 +37,7 @@ App::init(function ($utopia, $request, $response, $layout) {
->addHeader('Cache-Control', 'public, max-age='.$time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->addHeader('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url='.\urlencode($request->getURI()))
->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode
;

View file

@ -7,7 +7,7 @@ use Utopia\Domains\Domain;
use Appwrite\Database\Database;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Validator\UID;
use Appwrite\Storage\Storage;
use Utopia\Storage\Storage;
App::init(function ($layout) {
/** @var Utopia\View $layout */
@ -122,7 +122,8 @@ App::get('/console/home')
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/home/index.phtml');
$page
->setParam('usageStatsEnabled',App::getEnv('_APP_USAGE_STATS','enabled') == 'enabled');
$layout
->setParam('title', APP_NAME.' - Console')
->setParam('body', $page);
@ -390,6 +391,7 @@ App::get('/console/functions/function')
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
->setParam('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900))
->setParam('usageStatsEnabled',App::getEnv('_APP_USAGE_STATS','enabled') == 'enabled');
;
$layout

View file

@ -191,7 +191,7 @@ App::get('/error/:code')
$layout
->setParam('title', 'Error'.' - '.APP_NAME)
->setParam('body', $page);
}, ['']);
});
App::get('/specs/:format')
->groups(['web', 'home'])
@ -216,6 +216,7 @@ App::get('/specs/:format')
$routes = [];
$models = [];
$services = [];
$keys = [
APP_PLATFORM_CLIENT => [
@ -317,6 +318,20 @@ App::get('/specs/:format')
}
}
foreach (Config::getParam('services', []) as $key => $service) {
if(!isset($service['docs']) // Skip service if not part of the public API
|| !isset($service['sdk'])
|| !$service['docs']
|| !$service['sdk']) {
continue;
}
$services[] = [
'name' => $service['key'] ?? '',
'description' => (!empty($service['description'])) ? file_get_contents(realpath(__DIR__.'/../../..'.$service['description'])) : '',
];
}
$models = $response->getModels();
foreach ($models as $key => $value) {
@ -327,11 +342,11 @@ App::get('/specs/:format')
switch ($format) {
case 'swagger2':
$format = new Swagger2($utopia, $routes, $models, $keys[$platform], $security[$platform]);
$format = new Swagger2($utopia, $services, $routes, $models, $keys[$platform], $security[$platform]);
break;
case 'open-api3':
$format = new OpenAPI3($utopia, $routes, $models, $keys[$platform], $security[$platform]);
$format = new OpenAPI3($utopia, $services, $routes, $models, $keys[$platform], $security[$platform]);
break;
default:

View file

@ -18,9 +18,10 @@ use Utopia\CLI\Console;
ini_set('memory_limit','512M');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
ini_set('default_socket_timeout', -1);
error_reporting(E_ALL);
$http = new Server("0.0.0.0", 80);
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
$payloadSize = max(4000000 /* 4mb */, App::getEnv('_APP_STORAGE_LIMIT', 10000000 /* 10mb */));
@ -48,6 +49,7 @@ $http->on('AfterReload', function($serv, $workerId) {
});
$http->on('start', function (Server $http) use ($payloadSize) {
Console::success('Server started succefully (max payload is '.number_format($payloadSize).' bytes)');
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");

View file

@ -11,6 +11,8 @@ if (\file_exists(__DIR__.'/../vendor/autoload.php')) {
require_once __DIR__.'/../vendor/autoload.php';
}
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Database\Database;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
@ -319,7 +321,14 @@ App::setResource('deletes', function($register) {
}, ['register']);
// Test Mock
App::setResource('clients', function($console, $project) {
App::setResource('clients', function($request, $console, $project) {
$console->setAttribute('platforms', [ // Allways allow current host
'$collection' => Database::SYSTEM_COLLECTION_PLATFORMS,
'name' => 'Current Host',
'type' => 'web',
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
/**
* Get All verified client URLs for both console and current projects
* + Filter for duplicated entries
@ -345,7 +354,7 @@ App::setResource('clients', function($console, $project) {
}))));
return $clients;
}, ['console', 'project']);
}, ['request', 'console', 'project']);
App::setResource('user', function($mode, $project, $console, $request, $response, $projectDB, $consoleDB) {
/** @var Utopia\Swoole\Request $request */
@ -405,6 +414,29 @@ App::setResource('user', function($mode, $project, $console, $request, $response
}
}
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT)) { // JWT authentication
$jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
try {
$payload = $jwt->decode($authJWT);
} catch (JWTException $error) {
throw new Exception('Failed to verify JWT. '.$error->getMessage(), 401);
}
$jwtUserId = $payload['userId'] ?? '';
$jwtSessionId = $payload['sessionId'] ?? '';
if($jwtUserId && $jwtSessionId) {
$user = $projectDB->getDocument($jwtUserId);
}
if (empty($user->search('$id', $jwtSessionId, $user->getAttribute('tokens')))) { // Match JWT to active token
$user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]);
}
}
return $user;
}, ['mode', 'project', 'console', 'request', 'response', 'projectDB', 'consoleDB']);

View file

@ -3,8 +3,8 @@
global $cli;
use Appwrite\ClamAV\Network;
use Appwrite\Storage\Device\Local;
use Appwrite\Storage\Storage;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Domains\Domain;

View file

@ -6,14 +6,12 @@ use Appwrite\Docker\Compose;
use Appwrite\Docker\Env;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Validator\Mock;
use Utopia\View;
$cli
->task('install')
->desc('Install Appwrite')
->param('version', APP_VERSION_STABLE, new Mock(), 'Appwrite version', true)
->action(function ($version) {
->action(function () {
/**
* 1. Start - DONE
* 2. Check for older setup and get older version - DONE
@ -30,10 +28,17 @@ $cli
* 5. Run docker-compose up -d - DONE
* 6. Run data migration
*/
$vars = Config::getParam('variables');
$config = Config::getParam('variables');
$path = '/usr/src/code/appwrite';
$defaultHTTPPort = '80';
$defaultHTTPSPort = '443';
$vars = [];
foreach($config as $category) {
foreach($category['variables'] ?? [] as $var) {
$vars[] = $var;
}
}
Console::success('Starting Appwrite installation...');
@ -41,7 +46,7 @@ $cli
if (null !== $path && !\file_exists(\dirname($path))) {
if (!@\mkdir(\dirname($path), 0755, true)) {
Console::error('Can\'t create directory '.\dirname($path));
exit(1);
Console::exit(1);
}
}
@ -123,7 +128,7 @@ $cli
$templateForCompose
->setParam('httpPort', $httpPort)
->setParam('httpsPort', $httpsPort)
->setParam('version', $version)
->setParam('version', APP_VERSION_STABLE)
;
$templateForEnv
@ -132,25 +137,32 @@ $cli
if(!file_put_contents($path.'/docker-compose.yml', $templateForCompose->render(false))) {
Console::error('Failed to save Docker Compose file');
exit(1);
Console::exit(1);
}
if(!file_put_contents($path.'/.env', $templateForEnv->render(false))) {
Console::error('Failed to save environment variables file');
exit(1);
Console::exit(1);
}
$env = '';
$stdout = '';
$stderr = '';
foreach ($input as $key => $value) {
if($value) {
$env .= $key.'='.$value.' ';
}
}
Console::log("Running \"docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\"");
$exit = Console::execute("docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
$exit = Console::execute("${env} docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
if ($exit !== 0) {
Console::error("Failed to install Appwrite dockers");
Console::error($stderr);
exit($exit);
Console::exit($exit);
} else {
Console::success("Appwrite installed successfully");
}

View file

@ -4,64 +4,52 @@ global $cli;
require_once __DIR__.'/../init.php';
use Appwrite\Database\Database;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Event\Event;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
// TODO: Think of a better way to access consoleDB
function getConsoleDB() {
global $register;
$consoleDB = new Database();
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$consoleDB->setNamespace('app_console'); // Main DB
$consoleDB->setMocks(Config::getParam('collections', []));
return $consoleDB;
}
function notifyDeleteExecutionLogs()
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_EXECUTIONS
]);
}
function notifyDeleteAbuseLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_ABUSE,
'timestamp' => time() - $interval
]);
}
function notifyDeleteAuditLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_AUDIT,
'timestamp' => time() - $interval
]);
}
$cli
->task('maintenance')
->desc('Schedules maintenance tasks and publishes them to resque')
->action(function () {
Console::title('Maintenance V1');
Console::success(APP_NAME.' maintenance process v1 has started');
function notifyDeleteExecutionLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_EXECUTIONS,
'timestamp' => time() - $interval
]);
}
function notifyDeleteAbuseLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_ABUSE,
'timestamp' => time() - $interval
]);
}
function notifyDeleteAuditLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_AUDIT,
'timestamp' => time() - $interval
]);
}
// # of days in seconds (1 day = 86400s)
$interval = (int) App::getEnv('_APP_MAINTENANCE_INTERVAL', '86400');
//Convert Seconds to microseconds
$intervalMicroseconds = $interval * 1000000;
$consoleDB = getConsoleDB();
Console::loop(function() use ($consoleDB, $interval){
Console::info("[ MAINTENANCE TASK ] Notifying deletes workers every {$interval} seconds");
notifyDeleteExecutionLogs();
notifyDeleteAbuseLogs($interval);
notifyDeleteAuditLogs($interval);
}, $intervalMicroseconds);
$executionLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', '1209600');
$auditLogRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', '1209600');
$abuseLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', '86400');
Console::loop(function() use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention){
$time = date('d-m-Y H:i:s', time());
Console::info("[{$time}] Notifying deletes workers every {$interval} seconds");
notifyDeleteExecutionLogs($executionLogsRetention);
notifyDeleteAbuseLogs($abuseLogsRetention);
notifyDeleteAuditLogs($auditLogRetention);
}, $interval);
});

View file

@ -5,187 +5,26 @@ global $cli, $register, $projectDB, $console;
use Utopia\Config\Config;
use Utopia\CLI\Console;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
$callbacks = [
'0.4.0' => function() {
Console::log('I got nothing to do.');
},
'0.5.0' => function($project) use ($register, $projectDB) {
$db = $register->get('db');
Console::log('Migrating project: '.$project->getAttribute('name').' ('.$project->getId().')');
// Update all documents $uid -> $id
$limit = 30;
$sum = 30;
$offset = 0;
while ($sum >= 30) {
$all = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderType' => 'DESC',
]);
$sum = \count($all);
Console::log('Migrating: '.$offset.' / '.$projectDB->getSum());
foreach($all as $document) {
$document = fixDocument($document);
if(empty($document->getId())) {
throw new Exception('Missing ID');
}
try {
$new = $projectDB->overwriteDocument($document->getArrayCopy());
} catch (\Throwable $th) {
var_dump($document);
Console::error('Failed to update document: '.$th->getMessage());
continue;
}
if($new->getId() !== $document->getId()) {
throw new Exception('Duplication Error');
}
}
$offset = $offset + $limit;
}
$schema = $_SERVER['_APP_DB_SCHEMA'] ?? '';
try {
$statement = $db->prepare("
CREATE TABLE IF NOT EXISTS `template.database.unique` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index1` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `{$schema}`.`app_{$project->getId()}.database.unique` LIKE `template.database.unique`;
ALTER TABLE `{$schema}`.`app_{$project->getId()}.audit.audit` DROP COLUMN IF EXISTS `userType`;
ALTER TABLE `{$schema}`.`app_{$project->getId()}.audit.audit` DROP INDEX IF EXISTS `index_1`;
ALTER TABLE `{$schema}`.`app_{$project->getId()}.audit.audit` ADD INDEX IF NOT EXISTS `index_1` (`userId` ASC);
");
$statement->closeCursor();
$statement->execute();
}
catch (\Exception $e) {
Console::error('Failed to alter table for project: '.$project->getId().' with message: '.$e->getMessage().'/');
}
},
];
function fixDocument(Document $document) {
$providers = Config::getParam('providers');
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_PROJECTS){
foreach($providers as $key => $provider) {
if(!empty($document->getAttribute('usersOauth'.\ucfirst($key).'Appid'))) {
$document
->setAttribute('usersOauth2'.\ucfirst($key).'Appid', $document->getAttribute('usersOauth'.\ucfirst($key).'Appid', ''))
->removeAttribute('usersOauth'.\ucfirst($key).'Appid')
;
}
if(!empty($document->getAttribute('usersOauth'.\ucfirst($key).'Secret'))) {
$document
->setAttribute('usersOauth2'.\ucfirst($key).'Secret', $document->getAttribute('usersOauth'.\ucfirst($key).'Secret', ''))
->removeAttribute('usersOauth'.\ucfirst($key).'Secret')
;
}
}
}
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_WEBHOOKS){
$document->setAttribute('security', ($document->getAttribute('security')) ? true : false);
}
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_TASKS){
$document->setAttribute('security', ($document->getAttribute('security')) ? true : false);
}
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_USERS) {
foreach($providers as $key => $provider) {
if(!empty($document->getAttribute('oauth'.\ucfirst($key)))) {
$document
->setAttribute('oauth2'.\ucfirst($key), $document->getAttribute('oauth'.\ucfirst($key), ''))
->removeAttribute('oauth'.\ucfirst($key))
;
}
if(!empty($document->getAttribute('oauth'.\ucfirst($key).'AccessToken'))) {
$document
->setAttribute('oauth2'.\ucfirst($key).'AccessToken', $document->getAttribute('oauth'.\ucfirst($key).'AccessToken', ''))
->removeAttribute('oauth'.\ucfirst($key).'AccessToken')
;
}
}
if($document->getAttribute('confirm', null) !== null) {
$document
->setAttribute('emailVerification', $document->getAttribute('confirm', $document->getAttribute('emailVerification', false)))
->removeAttribute('confirm')
;
}
}
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_PLATFORMS) {
if($document->getAttribute('url', null) !== null) {
$document
->setAttribute('hostname', \parse_url($document->getAttribute('url', $document->getAttribute('hostname', '')), PHP_URL_HOST))
->removeAttribute('url')
;
}
}
$document
->setAttribute('$id', $document->getAttribute('$uid', $document->getAttribute('$id')))
->removeAttribute('$uid')
;
foreach($document as &$attr) { // Handle child documents
if($attr instanceof Document) {
$attr = fixDocument($attr);
}
if(\is_array($attr)) {
foreach($attr as &$child) {
if($child instanceof Document) {
$child = fixDocument($child);
}
}
}
}
return $document;
}
use Appwrite\Migration\Version;
$cli
->task('migrate')
->action(function () use ($register, $callbacks) {
->action(function () use ($register) {
Console::success('Starting Data Migration');
$consoleDB = new Database();
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$consoleDB->setNamespace('app_console'); // Main DB
$consoleDB->setMocks(Config::getParam('collections', []));
$consoleDB
->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register))
->setNamespace('app_console') // Main DB
->setMocks(Config::getParam('collections', []));
$projectDB = new Database();
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$projectDB->setMocks(Config::getParam('collections', []));
$projectDB
->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register))
->setMocks(Config::getParam('collections', []));
$console = $consoleDB->getDocument('console');
@ -197,13 +36,14 @@ $cli
$projects = [$console];
$count = 0;
while ($sum >= 30) {
foreach($projects as $project) {
$projectDB->setNamespace('app_'.$project->getId());
$migration = new Version\V06($register->get('db')); //TODO: remove hardcoded version and move to dynamic migration
while ($sum > 0) {
foreach ($projects as $project) {
try {
$callbacks['0.5.0']($project, $projectDB);
$migration
->setProject($project, $projectDB)
->execute();
} catch (\Throwable $th) {
throw $th;
Console::error('Failed to update project ("'.$project->getId().'") version with error: '.$th->getMessage());
@ -221,9 +61,11 @@ $cli
$sum = \count($projects);
$offset = $offset + $limit;
$count = $count + $sum;
Console::log('Fetched '.$count.'/'.$consoleDB->getSum().' projects...');
if ($sum > 0) {
Console::log('Fetched '.$count.'/'.$consoleDB->getSum().' projects...');
}
}
Console::success('Data Migration Completed');
});
});

View file

@ -11,6 +11,7 @@ use Appwrite\SDK\Language\Python;
use Appwrite\SDK\Language\Ruby;
use Appwrite\SDK\Language\Dart;
use Appwrite\SDK\Language\Deno;
use Appwrite\SDK\Language\DotNet;
use Appwrite\SDK\Language\Flutter;
use Appwrite\SDK\Language\Go;
use Appwrite\SDK\Language\Java;
@ -39,7 +40,6 @@ $cli
$git = (Console::confirm('Should we use git push? (yes/no)') == 'yes');
$production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false;
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
$warning = '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check previous releases.**';
if(!in_array($version, ['0.6.2', '0.7.0'])) {
throw new Exception('Unknown version given');
@ -60,6 +60,7 @@ $cli
$spec = file_get_contents(__DIR__.'/../config/specs/'.$version.'.'.$language['family'].'.json');
$cover = 'https://appwrite.io/images/github.png';
$result = \realpath(__DIR__.'/..').'/sdks/'.$key.'-'.$language['key'];
$resultExamples = \realpath(__DIR__.'/../..').'/docs/examples/'.$version.'/'.$key.'-'.$language['key'];
$target = \realpath(__DIR__.'/..').'/sdks/git/'.$language['key'].'/';
@ -69,9 +70,9 @@ $cli
$examples = ($examples) ? \file_get_contents($examples) : '';
$changelog = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/CHANGELOG.md');
$changelog = ($changelog) ? \file_get_contents($changelog) : '# Change Log';
$warning = ($language['beta']) ? '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check previous releases.**' : '';
$warning = '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check [previous releases]('.$language['url'].'/releases).**';
$license = 'BSD-3-Clause';
$licenseContent = 'Copyright (c) 2019 Appwrite (https://appwrite.io) and individual contributors.
$licenseContent = 'Copyright (c) ' . date('Y') . ' Appwrite (https://appwrite.io) and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
@ -99,6 +100,8 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
$config = new Node();
$config->setNPMPackage('node-appwrite');
$config->setBowerPackage('appwrite');
$warning = $warning."\n\n > This is the Node.js SDK for integrating with Appwrite from your Node.js server-side code.
If you're looking to integrate from the browser, you should check [appwrite/sdk-for-web](https://github.com/appwrite/sdk-for-web)";
break;
case 'deno':
$config = new Deno();
@ -122,6 +125,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
break;
case 'dart':
$config = new Dart();
$config->setPackageName('dart_appwrite');
$warning = $warning."\n\n > This is the Dart SDK for integrating with Appwrite from your Dart server-side code.
If you're looking for the Flutter SDK you should check [appwrite/sdk-for-flutter](https://github.com/appwrite/sdk-for-flutter)";
break;
case 'go':
$config = new Go();
@ -132,6 +138,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
case 'swift':
$config = new Swift();
break;
case 'dotnet':
$cover = '';
$config = new DotNet();
break;
default:
throw new Exception('Language "'.$language['key'].'" not supported');
break;
@ -154,7 +164,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
->setGitRepo($language['gitUrl'])
->setGitRepoName($language['gitRepoName'])
->setGitUserName($language['gitUserName'])
->setLogo('https://appwrite.io/images/github.png')
->setLogo($cover)
->setURL('https://appwrite.io')
->setShareText('Appwrite is a backend as a service for building web or mobile apps')
->setShareURL('http://appwrite.io')
@ -209,5 +219,5 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
}
}
exit();
Console::exit();
});

View file

@ -10,9 +10,16 @@ $cli
->task('vars')
->desc('List all the server environment variables')
->action(function () {
$variables = Config::getParam('variables', []);
$config = Config::getParam('variables', []);
$vars = [];
foreach ($variables as $key => $value) {
foreach($config as $category) {
foreach($category['variables'] ?? [] as $var) {
$vars[] = $var;
}
}
foreach ($vars as $key => $value) {
Console::log('- '.$value['name'].'='.App::getEnv($value['name'], ''));
}
});

View file

@ -29,6 +29,8 @@
<div class="box margin-bottom-xl">
<div>
<form name="account.update"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Account Name"
@ -58,6 +60,8 @@
<hr />
<form name="update-email"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Account Email"
@ -99,6 +103,8 @@
<h1>Update Password</h1>
<form name="update-password"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Account Password"
@ -129,6 +135,8 @@
<hr />
<form class="margin-top"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Account Current Session"
@ -163,6 +171,8 @@
<p>PLEASE NOTE: Account deletion is irreversible.</p>
<form class="inline"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Account"
@ -197,6 +207,8 @@
<span data-ls-if="true != {{session.current}}">
<!-- From remote session (-logout event) -->
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Account Session"
@ -218,6 +230,8 @@
<span data-ls-if="true == {{session.current}}">
<!-- From current session (+logout event) -->
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Account Current Session"
@ -254,6 +268,8 @@
</div>
<form class="inline margin-bottom-large"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Account Sessions"

View file

@ -6,6 +6,7 @@ $version = $this->getParam('version', '').'.'.APP_CACHE_BUSTER;
<ul class="copyright pull-start">
<li>
<a class="link-animation-enabled"
data-analytics
data-analytics-event="click"
data-analytics-category="console/footer"
data-analytics-label="GitHub Link"
@ -13,6 +14,7 @@ $version = $this->getParam('version', '').'.'.APP_CACHE_BUSTER;
</li>
<li>
<a class="link-animation-enabled"
data-analytics
data-analytics-event="click"
data-analytics-category="console/footer"
data-analytics-label="New GitHub Issue"
@ -20,6 +22,7 @@ $version = $this->getParam('version', '').'.'.APP_CACHE_BUSTER;
</li>
<li>
<a class="link-animation-enabled"
data-analytics
data-analytics-event="click"
data-analytics-category="console/footer"
data-analytics-label="Docs Link"

View file

@ -51,6 +51,7 @@
<span class="link"><i class="icon-sun-inv force-dark pull-start"></i><i class="icon-moon-inv force-light pull-start"></i> &nbsp; Change Theme
<div class="pull-end switch-theme">
<button data-general-theme
data-analytics
data-analytics-event="click"
data-analytics-category="console/header"
data-analytics-label="Switch Theme">
@ -67,6 +68,7 @@
<nav class="project-only" data-ls-ui-open="" data-button-class="round icon-btn phones-only tablets-only" data-button-aria="Navigation" data-button-icon="icon-dot-3">
<a class="logo" href="/console"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Logo Link">
@ -81,6 +83,7 @@
<ul class="links">
<li>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Home Link">
@ -95,6 +98,7 @@
<ul class="links">
<li>
<a data-ls-attrs="href=/console/database?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Database Link">
@ -104,6 +108,7 @@
</li>
<li>
<a data-ls-attrs="href=/console/storage?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Storage Link">
@ -113,6 +118,7 @@
</li>
<li>
<a data-ls-attrs="href=/console/users?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Users Link">
@ -122,6 +128,7 @@
</li>
<li>
<a data-ls-attrs="href=/console/functions?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Functions Link"
@ -137,6 +144,7 @@
<ul class="links">
<li>
<a data-ls-attrs="href=/console/tasks?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Tasks Link">
@ -146,6 +154,7 @@
</li>
<li>
<a data-ls-attrs="href=/console/webhooks?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Webhooks Links">
@ -155,6 +164,7 @@
</li>
<li>
<a data-ls-attrs="href=/console/keys?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="API Keys Link">
@ -168,6 +178,7 @@
<ul class="links bottom">
<li>
<a data-ls-attrs="href=/console/settings?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Settings Link">
@ -194,6 +205,8 @@
<form
data-setup
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project">

View file

@ -174,6 +174,8 @@ $maxCells = 10;
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Database Collection"
@ -340,6 +342,8 @@ $maxCells = 10;
</ul>
<form name="database.deleteCollection" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Database Collection"
@ -363,6 +367,7 @@ $maxCells = 10;
<ul data-ls-loop="project-collection.rules" data-ls-as="rule" class="sortable">
<li data-forms-remove data-forms-move-up data-forms-move-down>
<form
data-analytics
data-analytics-event="splice-rule-{{$index}}"
data-analytics-category="console"
data-analytics-label="Spliced Collection Rule"
@ -384,6 +389,8 @@ $maxCells = 10;
<h1>Add Rule</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Added Collection Rule"

View file

@ -192,6 +192,8 @@ $collections = [];
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Database Document"
@ -288,6 +290,8 @@ $collections = [];
<div data-ls-if="({{project-document.$id}})">
<form name="database.deleteDocument" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Collection Document"

View file

@ -18,6 +18,8 @@
<h1>New Collection</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Database Collection"

View file

@ -3,6 +3,7 @@ $fileLimit = $this->getParam('fileLimit', 0);
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
$events = array_keys($this->getParam('events', []));
$timeout = $this->getParam('timeout', 900);
$usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
?>
<div
data-service="functions.get"
@ -51,6 +52,8 @@ $timeout = $this->getParam('timeout', 900);
</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"
@ -93,6 +96,8 @@ $timeout = $this->getParam('timeout', 900);
<ul data-ls-loop="project-function-tags.tags" data-ls-as="tag" class="list">
<li class="clear">
<form data-ls-if="{{tag.$id}} !== {{project-function.tag}}" name="functions.updateTag" class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Function Execution"
@ -115,6 +120,8 @@ $timeout = $this->getParam('timeout', 900);
<span class="pull-start" data-ls-bind="Created {{tag.dateCreated|timeSince}} &nbsp; | &nbsp; {{tag.size|humanFileSize}}"></span>
<form data-ls-if="{{tag.$id}} !== {{project-function.tag}}" name="functions.deleteTag" class="pull-start"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Function Tag"
@ -146,6 +153,8 @@ $timeout = $this->getParam('timeout', 900);
<h1>Deploy a New Tag</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Function Tag"
@ -220,6 +229,8 @@ $timeout = $this->getParam('timeout', 900);
</ul>
<form name="functions.delete" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Function"
@ -240,6 +251,7 @@ $timeout = $this->getParam('timeout', 900);
</div>
</div>
</li>
<?php if($usageStatsEnabled): ?>
<li data-state="/console/functions/function/monitors?id={{router.params.id}}&project={{router.params.project}}">
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
@ -318,6 +330,7 @@ $timeout = $this->getParam('timeout', 900);
</ul>
</div>
</li>
<?php endif; ?>
<li data-state="/console/functions/function/logs?id={{router.params.id}}&project={{router.params.project}}">
<div class="text-fade text-size-small pull-end margin-top" data-ls-bind="{{project-function-executions.sum}} executions found"></div>
@ -466,6 +479,8 @@ $timeout = $this->getParam('timeout', 900);
<label>&nbsp;</label>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Function"
@ -522,7 +537,7 @@ $timeout = $this->getParam('timeout', 900);
<hr class="margin-bottom margin-top-no" />
<fieldset name="vars" data-cast-to="object">
<div data-ls-loop="project-function.vars" data-ls-as="var" id="project-vars">
<div data-ls-loop="project-function.vars" data-ls-as="var" id="project-vars" style="visibility: visible;">
<div class="margin-bottom-small">
<div data-forms-remove class="row thin">
<div class="col span-10">
@ -568,6 +583,8 @@ $timeout = $this->getParam('timeout', 900);
</ul>
<form name="functions.delete" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Function"

View file

@ -90,6 +90,8 @@ $environments = $this->getParam('environments', []);
<h1>Add Function</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Function"

View file

@ -1,5 +1,6 @@
<?php
$graph = $this->getParam('graph', false);
$usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
?>
<div class="cover margin-bottom-small">
@ -29,6 +30,10 @@ $graph = $this->getParam('graph', false);
<div class="pull-end">
<form class="margin-start-small inline" data-ls-if="{{usage.range}} !== '24h'"
data-analytics
data-analytics-event="click"
data-analytics-category="console"
data-analytics-label="Usage 24h"
data-service="projects.getUsage"
data-event="submit"
data-name="usage"
@ -40,6 +45,10 @@ $graph = $this->getParam('graph', false);
<button class="tick margin-start-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
<form class="margin-start-small inline" data-ls-if="{{usage.range}} !== '30d'"
data-analytics
data-analytics-event="click"
data-analytics-category="console"
data-analytics-label="Usage 30d"
data-service="projects.getUsage"
data-event="submit"
data-name="usage"
@ -50,6 +59,10 @@ $graph = $this->getParam('graph', false);
<button class="tick margin-start-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
<form class="margin-start-small inline" data-ls-if="{{usage.range}} !== '90d'"
data-analytics
data-analytics-event="click"
data-analytics-category="console"
data-analytics-label="Usage 90d"
data-service="projects.getUsage"
data-event="submit"
data-name="usage"
@ -68,7 +81,7 @@ $graph = $this->getParam('graph', false);
data-param-project-id="{{router.params.project}}"
data-param-range="30d">
<?php if (!$graph) : ?>
<?php if (!$graph && $usageStatsEnabled): ?>
<div class="box dashboard">
<div class="row responsive">
<div class="col span-9">
@ -139,6 +152,8 @@ $graph = $this->getParam('graph', false);
</div>
<form class="pull-end margin-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Project Platform"
@ -190,7 +205,11 @@ $graph = $this->getParam('graph', false);
</div>
<div class="pull-end desktops-only tablets-only">
<a data-ls-attrs="href=/console/keys?project={{router.params.project}}">Manage Your Server API Keys</a>
<a data-analytics
data-analytics-event="click"
data-analytics-category="console"
data-analytics-label="API Keys Link"
data-ls-attrs="href=/console/keys?project={{router.params.project}}">Manage Your Server API Keys</a>
</div>
<div class="drop-list pull-start" data-ls-ui-open="" data-button-aria="Choose Platform" data-button-text="Add Platform" data-button-class="button" data-blur="1">
@ -220,6 +239,8 @@ $graph = $this->getParam('graph', false);
<h1>New Web App</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Platform (Web)"
@ -258,6 +279,8 @@ $graph = $this->getParam('graph', false);
<script type="text/html" id="template-web-update">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Platform (Web)"
@ -296,6 +319,8 @@ $graph = $this->getParam('graph', false);
<h2 style="display: none">&nbsp;&nbsp;iOS&nbsp;&nbsp;</h2>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Platform (Flutter / iOS)"
@ -328,6 +353,8 @@ $graph = $this->getParam('graph', false);
<h2 style="display: none">&nbsp;&nbsp;Android&nbsp;&nbsp;</h2>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Platform (Flutter / Android)"
@ -360,9 +387,11 @@ $graph = $this->getParam('graph', false);
<script type="text/html" id="template-ios-update">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Platform (iOS)"
data-analytics-label="Update Project Platform (Flutter / iOS)"
data-service="projects.updatePlatform"
data-scope="console"
data-event="submit"
@ -390,9 +419,10 @@ $graph = $this->getParam('graph', false);
<script type="text/html" id="template-android-update">
<form
data-analytics
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Platform (Android)"
data-analytics-label="Update Project Platform (Flutter / Android)"
data-service="projects.updatePlatform"
data-scope="console"
data-event="submit"

View file

@ -93,23 +93,28 @@ $home = $this->getParam('home', '');
<p class="text-fade">Join Appwrite growing developers community channels.</p>
<a href="<?php echo APP_SOCIAL_TWITTER; ?>" target="_blank" rel="noopener" title="<?php echo APP_NAME;?> on Twitter"
data-analytics
data-analytics-event="click"
data-analytics-category="console/home"
data-analytics-label="Twitter Link"><i class="icon-twitter"></i></a>
<a href="<?php echo APP_SOCIAL_FACEBOOK; ?>" target="_blank" rel="noopener" title="<?php echo APP_NAME;?> on Facebook"
data-analytics
data-analytics-event="click"
data-analytics-category="console/home"
data-analytics-label="Facebook Link"><i class="icon-facebook"></i></a>
<a href="<?php echo APP_SOCIAL_LINKEDIN; ?>" target="_blank" rel="noopener" title="<?php echo APP_NAME;?> on Linkedin"
data-analytics
data-analytics-event="click"
data-analytics-category="console/home"
data-analytics-label="Linkedin Link"><i class="icon-linkedin"></i></a>
<a href="<?php echo APP_SOCIAL_DISCORD; ?>" target="_blank" rel="noopener" title="<?php echo APP_NAME;?> Discord Server"
data-analytics
data-analytics-event="click"
data-analytics-category="console/home"
data-analytics-label="Discord Link"><i class="icon-discord"></i></a>
<a href="<?php echo APP_SOCIAL_GITHUB; ?>" target="_blank" rel="noopener" title="<?php echo APP_NAME;?> on Github"
data-analytics-event="click"
data-analytics
data-analytics-type="click"
data-analytics-category="console/home"
data-analytics-label="GitHub Link"><i class="icon-github-circled"></i></a>
</div>

View file

@ -33,6 +33,8 @@ $scopes = $this->getParam('scopes', []);
<h1>Update API Key</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Key"
@ -57,7 +59,7 @@ $scopes = $this->getParam('scopes', []);
<div class="row responsive thin">
<?php foreach ($scopes as $i => $scope) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large text-size-small" title="<?php echo $scope; ?>">
<input type="checkbox" name="scopes" data-ls-bind="{{key.scopes}}" value="<?php echo $scope; ?>" /> &nbsp; <?php echo $scope; ?>
<input data-ls-attrs="id=scope-<?php echo $scope; ?>" type="checkbox" name="scopes" data-ls-bind="{{key.scopes}}" value="<?php echo $scope; ?>" /> &nbsp; <?php echo $scope; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
@ -75,6 +77,8 @@ $scopes = $this->getParam('scopes', []);
</div>
<form class="pull-end margin-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Project Key"
@ -131,9 +135,11 @@ $scopes = $this->getParam('scopes', []);
<h1>Add API Keys</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Platform"
data-analytics-label="Create Project Key"
data-service="projects.createKey"
data-scope="console"
data-event="submit"

View file

@ -33,6 +33,8 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<div class="box margin-bottom-large">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project"
@ -87,6 +89,8 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<p>PLEASE NOTE: Project deletion is irreversible.</p>
<form class="inline"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Project"
@ -281,6 +285,8 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<li>
Confirm and verify your CNAME record values:
<form class="strip"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Domain Verification"
@ -313,6 +319,8 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
</td>
<td data-title="">
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Project Domain"
@ -345,6 +353,8 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<h1>Add Domain</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Domain"
@ -388,9 +398,11 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<ul data-ls-loop="members.memberships" data-ls-as="member" class="list">
<li class="clear">
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Team Membership"
data-analytics-label="Delete Project Membership"
data-service="teams.deleteMembership"
data-scope="console"
data-event="submit"
@ -409,9 +421,11 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<div data-ls-if="false === {{member.confirm}}" class="pull-end margin-end">
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Team Membership (resend)"
data-analytics-label="Create Project Membership (resend)"
data-service="teams.deleteMembership"
data-scope="console"
data-event="submit"
@ -462,9 +476,11 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<h1>Invite Member</h1>
<form name="teams.createTeamMembership"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Team Membership"
data-analytics-label="Create Project Membership"
data-service="teams.createMembership"
data-scope="console"
data-event="submit"

View file

@ -23,6 +23,8 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<h1>Upload File</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Storage File"
@ -124,6 +126,8 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<div class="row responsive modalize">
<div class="col span-8">
<form class="strip"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Storage File"
@ -153,6 +157,8 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
</form>
<form class="strip"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete File"

View file

@ -60,7 +60,7 @@
<br />
<span data-ls-if="undefined !== {{task.previous}}">
<span data-ls-if="undefined !== {{task.previous}} && {{task.previous}}">
<span data-ls-bind="Prev: {{task.previous|dateTime}}"></span>
<!-- <span data-ls-if="undefined !== {{task.delay}} && 59 < {{task.delay}}" class="text-danger margin-top-tiny">
@ -78,6 +78,8 @@
<h1>Update Task</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Task"
@ -202,6 +204,8 @@
</div>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Project Task"
@ -238,6 +242,8 @@
<h1>Add Task</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Task"

View file

@ -23,6 +23,8 @@ $providers = $this->getParam('providers', []);
<h1>Create User</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create User"
@ -176,6 +178,8 @@ $providers = $this->getParam('providers', []);
<h1>Create Team</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Team"
@ -319,6 +323,8 @@ $providers = $this->getParam('providers', []);
<h1><?php echo $this->escape($name); ?> OAuth2 Settings</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project OAuth2"
@ -339,7 +345,7 @@ $providers = $this->getParam('providers', []);
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid}}">
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret">App Secret</label>
<input name="secret" data-forms-show-secret data-forms-show-secret-above="true" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="password" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret}}">
<input name="secret" data-forms-show-secret id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="password" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret}}">
<?php else: ?>
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">Bundle ID <span class="tooltip" data-tooltip="Attribute internal display name"><i class="icon-info-circled"></i></span></label>
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="com.company.appname" />

View file

@ -39,6 +39,8 @@
<div class="box margin-bottom-large">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Team"
@ -88,6 +90,8 @@
<ul data-ls-loop="project-members.memberships" data-ls-as="member" class="list">
<li class="clear">
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Team Membership"
@ -125,6 +129,8 @@
<h1>Add Member</h1>
<form name="teams.createTeamMembership"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Team Membership"
@ -204,6 +210,8 @@
</ul>
<form name="teams.delete" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Team"

View file

@ -104,6 +104,8 @@
<p>PLEASE NOTE: User deletion is irreversible.</p>
<form class="inline"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete User"
@ -136,6 +138,8 @@
<div data-ls-if="{{user.status}} !== <?php echo \Appwrite\Auth\Auth::USER_STATUS_BLOCKED; ?>" style="display: none">
<form name="users.updateStatus" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update User Status"
@ -149,12 +153,14 @@
data-failure-param-alert-text="Failed to block user"
data-failure-param-alert-classname="error">
<button name="status" type="submit" class="danger fill" value="<?php echo \Appwrite\Auth\Auth::USER_STATUS_BLOCKED; ?>">Block Account</button>
<button name="status" type="submit" class="danger fill" value="<?php echo \Appwrite\Auth\Auth::USER_STATUS_BLOCKED; ?>" data-cast-to="integer">Block Account</button>
</form>
</div>
<div data-ls-if="{{user.status}} === <?php echo \Appwrite\Auth\Auth::USER_STATUS_BLOCKED; ?>" style="display: none">
<form name="users.updateStatus" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update User Status"
@ -168,7 +174,7 @@
data-failure-param-alert-text="Failed to activate user"
data-failure-param-alert-classname="error">
<button name="status" type="submit" class="fill" value="<?php echo \Appwrite\Auth\Auth::USER_STATUS_ACTIVATED; ?>">Activate Account</button>
<button name="status" type="submit" class="fill" value="<?php echo \Appwrite\Auth\Auth::USER_STATUS_ACTIVATED; ?>" data-cast-to="integer">Activate Account</button>
</form>
</div>
</div>
@ -192,6 +198,8 @@
<ul data-ls-loop="sessions.sessions" data-ls-as="session" class="list">
<li class="clear">
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete User Session"
@ -222,6 +230,8 @@
</div>
<form class="inline margin-bottom-large"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete User Sessions"

View file

@ -37,6 +37,8 @@ $events = array_keys($this->getParam('events', []));
<h1>Update Webhook</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Webhook"
@ -116,6 +118,8 @@ $events = array_keys($this->getParam('events', []));
</div>
<form class="pull-end margin-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Project Webhook"
@ -154,6 +158,8 @@ $events = array_keys($this->getParam('events', []));
<h1>Add Webhook</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Webhook"

View file

@ -1,5 +1,7 @@
<section class="zone medium">
<form class="box margin-top-large"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="home"
data-analytics-label="Update Team Membership Status"

View file

@ -6,6 +6,8 @@
<small class="pull-end text-size-small">* All fields are required</small>
<form name="account.createRecovery"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="home"
data-analytics-label="Create Account Recovery"

View file

@ -9,6 +9,8 @@
<br />
<form name="recovery-reset"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="home"
data-analytics-label="Update Account Recovery"

View file

@ -17,6 +17,8 @@
<p>Login using email and password</p>
<form name="account.createSession"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="home"
data-analytics-label="Create Account Session"

View file

@ -8,6 +8,8 @@
<small class="pull-end text-size-small">* All fields are required</small>
<form name="account.create"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="home"
data-analytics-label="Create Account"

View file

@ -76,6 +76,7 @@ services:
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_USAGE_STATS
- _APP_INFLUXDB_HOST
- _APP_INFLUXDB_PORT
- _APP_STORAGE_LIMIT
@ -84,6 +85,7 @@ services:
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_ENVS
appwrite-worker-usage:
image: appwrite/appwrite:<?php echo $version."\n"; ?>
@ -239,6 +241,7 @@ services:
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_USAGE_STATS
appwrite-worker-mails:
image: appwrite/appwrite:<?php echo $version."\n"; ?>
@ -263,10 +266,9 @@ services:
- _APP_SMTP_PASSWORD
appwrite-maintenance:
image: appwrite/appwrite:<?php echo $version."\n"; ?>
entrypoint: maintenance
container_name: appwrite-maintenance
build:
context: .
restart: unless-stopped
networks:
- appwrite
@ -277,11 +279,10 @@ services:
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_MAINTENANCE_INTERVAL
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
appwrite-schedule:
image: appwrite/appwrite:<?php echo $version."\n"; ?>
@ -305,8 +306,6 @@ services:
- appwrite
volumes:
- appwrite-mariadb:/var/lib/mysql:rw
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=rootsecretpassword
- MYSQL_DATABASE=${_APP_DB_SCHEMA}
@ -370,5 +369,4 @@ volumes:
appwrite-certificates:
appwrite-functions:
appwrite-influxdb:
appwrite-chronograf:
appwrite-config:

View file

@ -6,7 +6,7 @@ use Utopia\CLI\Console;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Audits V1 Worker');
Console::title('Audits V1 Worker');
Console::success(APP_NAME.' audits worker v1 has started');

View file

@ -12,7 +12,7 @@ use Appwrite\Network\Validator\CNAME;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Certificates V1 Worker');
Console::title('Certificates V1 Worker');
Console::success(APP_NAME.' certificates worker v1 has started');

View file

@ -5,7 +5,7 @@ use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Storage\Device\Local;
use Utopia\Storage\Device\Local;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\CLI\Console;
@ -15,7 +15,7 @@ use Utopia\Audit\Adapters\MySQL as AuditAdapter;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Deletes V1 Worker');
Console::title('Deletes V1 Worker');
Console::success(APP_NAME.' deletes worker v1 has started'."\n");
@ -59,7 +59,7 @@ class DeletesV1
break;
case DELETE_TYPE_EXECUTIONS:
$this->deleteExecutionLogs();
$this->deleteExecutionLogs($this->args['timestamp']);
break;
case DELETE_TYPE_AUDIT:
@ -121,16 +121,17 @@ class DeletesV1
], $this->getProjectDB($projectId));
}
protected function deleteExecutionLogs()
protected function deleteExecutionLogs($timestamp)
{
$this->deleteForProjectIds(function($projectId) {
$this->deleteForProjectIds(function($projectId) use ($timestamp) {
if (!($projectDB = $this->getProjectDB($projectId))) {
throw new Exception('Failed to get projectDB for project '.$projectId);
}
// Delete Executions
$this->deleteByGroup([
'$collection='.Database::SYSTEM_COLLECTION_EXECUTIONS
'$collection='.Database::SYSTEM_COLLECTION_EXECUTIONS,
'dateCreated<'.$timestamp
], $projectDB);
});
}

View file

@ -1,7 +1,4 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
use Appwrite\Database\Database;
use Appwrite\Database\Document;
@ -9,6 +6,7 @@ use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Event\Event;
use Cron\CronExpression;
use Swoole\Runtime;
use Utopia\App;
use Utopia\CLI\Console;
@ -16,7 +14,7 @@ use Utopia\Config\Config;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Functions V1 Worker');
Console::title('Functions V1 Worker');
Runtime::setHookFlags(SWOOLE_HOOK_ALL);
@ -30,21 +28,16 @@ $environments = Config::getParam('environments');
$warmupStart = \microtime(true);
Co\run(function() use ($environments) { // Warmup: make sure images are ready to run fast 🚀
Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
foreach($environments as $environment) {
go(function() use ($environment) {
$stdout = '';
$stderr = '';
Console::info('Warming up '.$environment['name'].' environment...');
Console::info('Warming up '.$environment['name'].' '.$environment['version'].' environment...');
if(App::isDevelopment()) {
Console::execute('docker build '.$environment['build'].' -t '.$environment['image'], '', $stdout, $stderr);
}
else {
Console::execute('docker pull '.$environment['image'], '', $stdout, $stderr);
}
Console::execute('docker pull '.$environment['image'], '', $stdout, $stderr);
if(!empty($stdout)) {
Console::log($stdout);
@ -87,14 +80,6 @@ $stdout = \explode("\n", $stdout);
\parse_str($value, $container);
if(isset($container['name'])) {
// $labels = [];
// $temp = explode(',', $container['labels'] ?? []);
// foreach($temp as &$label) {
// $label = explode('=', $label);
// $labels[$label[0] || 0] = $label[1] || '';
// }
$container = [
'name' => $container['name'],
'online' => (\substr($container['status'], 0, 2) === 'Up'),
@ -116,19 +101,6 @@ $stdout = \explode("\n", $stdout);
Console::info(count($list)." functions listed in " . ($executionEnd - $executionStart) . " seconds with exit code {$exitCode}");
/*
* 1. Get Original Task
* 2. Check for updates
* If has updates skip task and don't reschedule
* If status not equal to play skip task
* 3. Check next run date, update task and add new job at the given date
* 4. Execute task (set optional timeout)
* 5. Update task response to log
* On success reset error count
* On failure add error count
* If error count bigger than allowed change status to pause
*/
/**
* 1. Get event args - DONE
* 2. Unpackage code in the isolated container - DONE
@ -144,23 +116,6 @@ Console::info(count($list)." functions listed in " . ($executionEnd - $execution
//TODO aviod scheduled execution if delay is bigger than X offest
/**
* Limit CPU Usage - DONE
* Limit Memory Usage - DONE
* Limit Network Usage
* Limit Storage Usage (//--storage-opt size=120m \)
* Make sure no access to redis, mariadb, influxdb or other system services
* Make sure no access to NFS server / storage volumes
* Access Appwrite REST from internal network for improved performance
*/
/**
* Get Usage Stats
* -> Network (docker stats --no-stream --format="{{.NetIO}}" appwrite)
* -> CPU Time - DONE
* -> Invoctions (+1) - DONE
*/
class FunctionsV1
{
public $args = [];
@ -180,6 +135,7 @@ class FunctionsV1
$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']) : '';
$database = new Database();
@ -228,9 +184,7 @@ class FunctionsV1
Console::success('Triggered function: '.$event);
Swoole\Coroutine\run(function () use ($projectId, $database, $function, $event, $payload) {
$this->execute('event', $projectId, '', $database, $function, $event, $payload);
});
$this->execute('event', $projectId, '', $database, $function, $event, $payload);
}
}
break;
@ -248,6 +202,45 @@ class FunctionsV1
* On failure add error count
* If error count bigger than allowed change status to pause
*/
// Reschedule
Authorization::disable();
$function = $database->getDocument($functionId);
Authorization::reset();
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found ('.$functionId.')');
}
if($scheduleOriginal && $scheduleOriginal !== $function->getAttribute('schedule')) { // Schedule has changed from previous run, ignore this run.
return;
}
$cron = CronExpression::factory($function->getAttribute('schedule'));
$next = (int) $cron->getNextRunDate()->format('U');
$function
->setAttribute('scheduleNext', $next)
->setAttribute('schedulePrevious', \time())
;
Authorization::disable();
$function = $database->updateDocument(array_merge($function->getArrayCopy(), [
'scheduleNext' => $next,
]));
Authorization::reset();
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $projectId,
'functionId' => $function->getId(),
'executionId' => null,
'trigger' => 'schedule',
'scheduleOriginal' => $function->getAttribute('schedule', ''),
]); // Async task rescheduale
$this->execute($trigger, $projectId, $executionId, $database, $function);
break;
@ -260,9 +253,7 @@ class FunctionsV1
throw new Exception('Function not found ('.$functionId.')');
}
Swoole\Coroutine\run(function () use ($trigger, $projectId, $executionId, $database, $function) {
$this->execute($trigger, $projectId, $executionId, $database, $function);
});
$this->execute($trigger, $projectId, $executionId, $database, $function);
break;
default:
@ -316,8 +307,8 @@ class FunctionsV1
'time' => 0,
]);
if(false === $execution) {
throw new Exception('Failed to create execution');
if(false === $execution || ($execution instanceof Document && $execution->isEmpty())) {
throw new Exception('Failed to create or read execution');
}
Authorization::reset();
@ -344,7 +335,7 @@ class FunctionsV1
\array_walk($vars, function (&$value, $key) {
$key = $this->filterEnvKey($key);
$value = \escapeshellarg((empty($value)) ? 'null' : $value);
$value = "\t\t\t--env {$key}={$value} \\";
$value = "--env {$key}={$value}";
});
$tagPath = $tag->getAttribute('path', '');
@ -380,6 +371,15 @@ class FunctionsV1
unset($list[$container]);
}
/**
* Limit CPU Usage - DONE
* Limit Memory Usage - DONE
* Limit Network Usage
* Limit Storage Usage (//--storage-opt size=120m \)
* Make sure no access to redis, mariadb, influxdb or other system services
* Make sure no access to NFS server / storage volumes
* Access Appwrite REST from internal network for improved performance
*/
if(!isset($list[$container])) { // Create contianer if not ready
$stdout = '';
$stderr = '';
@ -387,20 +387,20 @@ class FunctionsV1
$executionStart = \microtime(true);
$executionTime = \time();
$exitCode = Console::execute("docker run \
-d \
--entrypoint=\"\" \
--cpus=".App::getEnv('_APP_FUNCTIONS_CPUS', '1')." \
--memory=".App::getEnv('_APP_FUNCTIONS_MEMORY', '128')."m \
--memory-swap=".App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', '128')."m \
--name={$container} \
--label appwrite-type=function \
--label appwrite-created=".$executionTime." \
--volume {$tagPathTargetDir}:/tmp:rw \
--workdir /usr/local/src \
".\implode("\n", $vars)."
{$environment['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'"
$exitCode = Console::execute("docker run ".
" -d".
" --entrypoint=\"\"".
" --cpus=".App::getEnv('_APP_FUNCTIONS_CPUS', '1').
" --memory=".App::getEnv('_APP_FUNCTIONS_MEMORY', '256')."m".
" --memory-swap=".App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', '256')."m".
" --name={$container}".
" --label appwrite-type=function".
" --label appwrite-created={$executionTime}".
" --volume {$tagPathTargetDir}:/tmp:rw".
" --workdir /usr/local/src".
" ".\implode(" ", $vars).
" {$environment['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);
$executionEnd = \microtime(true);
@ -430,11 +430,8 @@ class FunctionsV1
$executionStart = \microtime(true);
$exitCode = Console::execute("docker exec \
".\implode("\n", $vars)."
{$container} \
{$command}"
, '', $stdout, $stderr, $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)));
$exitCode = Console::execute("docker exec ".\implode(" ", $vars)." {$container} {$command}"
, '', $stdout, $stderr, $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)));
$executionEnd = \microtime(true);
$executionTime = ($executionEnd - $executionStart);
@ -470,8 +467,10 @@ class FunctionsV1
->setParam('networkRequestSize', 0)
->setParam('networkResponseSize', 0)
;
$usage->trigger();
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
$usage->trigger();
}
$this->cleanup();
}

View file

@ -5,7 +5,7 @@ use Utopia\CLI\Console;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Mails V1 Worker');
Console::title('Mails V1 Worker');
Console::success(APP_NAME.' mails worker v1 has started'."\n");

View file

@ -11,7 +11,7 @@ use Cron\CronExpression;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Tasks V1 Worker');
Console::title('Tasks V1 Worker');
Console::success(APP_NAME.' tasks worker v1 has started');

View file

@ -5,7 +5,7 @@ use Utopia\CLI\Console;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Usage V1 Worker');
Console::title('Usage V1 Worker');
Console::success(APP_NAME.' usage worker v1 has started');

View file

@ -10,7 +10,7 @@ use Appwrite\Database\Validator\Authorization;
require_once __DIR__.'/../init.php';
\cli_set_process_title('Webhooks V1 Worker');
Console::title('Webhooks V1 Worker');
Console::success(APP_NAME.' webhooks worker v1 has started');

View file

@ -1,37 +0,0 @@
#!/bin/sh
export PHP_VERSION=$PHP_VERSION
chown -Rf www-data.www-data /usr/src/code/
sed 's/%_APP_STORAGE_LIMIT%/'$_APP_STORAGE_LIMIT'/g' /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
# Function to update the fpm configuration to make the service environment variables available
function setEnvironmentVariable() {
if [ -z "$2" ]; then
echo "Environment variable '$1' not set."
return
fi
# Check whether variable already exists
if ! grep -q "\[$1\]" /etc/php/$PHP_VERSION/fpm/pool.d/www.conf; then
# Add variable
echo "env[$1] = $2" >> /etc/php/$PHP_VERSION/fpm/pool.d/www.conf
fi
# Reset variable
# sed -i "s/^env\[$1.*/env[$1] = $2/g" /etc/php/$PHP_VERSION/fpm/pool.d/www.conf
}
# Grep for variables that look like MySQL (APP_)
for _curVar in $(env | grep _APP_ | awk -F = '{print $1}');do
# awk has split them by the equals sign
# Pass the name and value to our function
setEnvironmentVariable ${_curVar} ${!_curVar}
done
# Init server settings
php /usr/src/code/app/tasks/init.php ssl
# Start supervisord and services
/usr/bin/supervisord -n -c /etc/supervisord.conf

View file

@ -38,13 +38,15 @@
"utopia-php/abuse": "0.3.*",
"utopia-php/audit": "0.5.*",
"utopia-php/cache": "0.2.*",
"utopia-php/cli": "0.8.0",
"utopia-php/cli": "0.9.0",
"utopia-php/config": "0.2.*",
"utopia-php/locale": "0.3.*",
"utopia-php/registry": "0.2.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/domains": "0.2.*",
"utopia-php/swoole": "0.2.*",
"utopia-php/system": "0.3.*",
"utopia-php/storage": "0.2.*",
"resque/php-resque": "1.3.6",
"matomo/device-detector": "3.13.0",
@ -52,11 +54,12 @@
"domnikl/statsd": "3.0.2",
"influxdb/influxdb-php": "1.15.1",
"phpmailer/phpmailer": "6.1.7",
"chillerlan/php-qrcode": "4.2.0"
"chillerlan/php-qrcode": "4.3.0",
"adhocore/jwt": "1.1.0"
},
"require-dev": {
"swoole/ide-helper": "4.5.5",
"appwrite/sdk-generator": "0.2.3",
"appwrite/sdk-generator": "0.4.5",
"phpunit/phpunit": "9.4.2",
"vimeo/psalm": "4.1.1"
},

580
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,215 +0,0 @@
version: '3'
services:
traefik:
image: traefik:v2.2
container_name: appwrite-traefik
command:
- --log.level=DEBUG
- --api.insecure=true
- --providers.file.directory=/storage/config
- --providers.file.watch=true
- --providers.docker=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --accesslog=true
restart: unless-stopped
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- appwrite-config:/storage/config:ro
- appwrite-certificates:/storage/certificates:ro
depends_on:
- appwrite
networks:
- gateway
- appwrite
appwrite:
container_name: appwrite
build:
context: .
args:
- TESTING=true
- VERSION=dev
restart: unless-stopped
networks:
- appwrite
labels:
- traefik.http.routers.appwrite.rule=PathPrefix(`/`)
- traefik.http.routers.appwrite-secure.rule=PathPrefix(`/`)
- traefik.http.routers.appwrite-secure.tls=true
volumes:
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
- appwrite-functions:/storage/functions:rw
- ./phpunit.xml:/usr/src/code/phpunit.xml
- ./tests:/usr/src/code/tests
- ./app:/usr/src/code/app
# - ./vendor:/usr/src/code/vendor
- ./docs:/usr/src/code/docs
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
ports:
- 9501:80
depends_on:
- mariadb
- redis
# - smtp
- clamav
- influxdb
- telegraf
- maildev
environment:
#- _APP_ENV=production
- _APP_ENV=development
- _APP_OPTIONS_ABUSE=disabled
- _APP_OPTIONS_FORCE_HTTPS=disabled
- _APP_OPENSSL_KEY_V1=your-secret-key
- _APP_DOMAIN=demo.appwrite.io
- _APP_DOMAIN_TARGET=demo.appwrite.io
- _APP_REDIS_HOST=redis
- _APP_REDIS_PORT=6379
- _APP_DB_HOST=mariadb
- _APP_DB_PORT=3306
- _APP_DB_SCHEMA=appwrite
- _APP_DB_USER=user
- _APP_DB_PASS=password
- _APP_INFLUXDB_HOST=influxdb
- _APP_INFLUXDB_PORT=8086
- _APP_STATSD_HOST=telegraf
- _APP_STATSD_PORT=8125
- _APP_SMTP_HOST=maildev
- _APP_SMTP_PORT=25
mariadb:
image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:
- appwrite
volumes:
- appwrite-mariadb:/var/lib/mysql:rw
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=rootsecretpassword
- MYSQL_DATABASE=appwrite
- MYSQL_USER=user
- MYSQL_PASSWORD=password
command: 'mysqld --innodb-flush-method=fsync'
maildev:
image: djfarrelly/maildev
container_name: appwrite-maildev
restart: unless-stopped
ports:
- '1080:80'
networks:
- appwrite
# smtp:
# image: appwrite/smtp:1.0.1
# container_name: appwrite-smtp
# restart: unless-stopped
# networks:
# - appwrite
# environment:
# - MAILNAME=appwrite
# - RELAY_NETWORKS=:192.168.0.0/24:10.0.0.0/16
redis:
image: redis:5.0
container_name: appwrite-redis
restart: unless-stopped
networks:
- appwrite
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
influxdb:
image: influxdb:1.6
container_name: appwrite-influxdb
restart: unless-stopped
networks:
- appwrite
volumes:
- appwrite-influxdb:/var/lib/influxdb:rw
telegraf:
image: appwrite/telegraf:1.0.0
container_name: appwrite-telegraf
restart: unless-stopped
networks:
- appwrite
# redis-commander:
# image: rediscommander/redis-commander:latest
# restart: unless-stopped
# networks:
# - appwrite
# environment:
# - REDIS_HOSTS=redis
# ports:
# - "8081:8081"
# resque:
# image: registry.gitlab.com/appwrite/appwrite/resque-web:v1.0.2
# restart: unless-stopped
# networks:
# - appwrite
# ports:
# - "5678:5678"
# environment:
# - RESQUE_WEB_HOST=redis
# - RESQUE_WEB_PORT=6379
# - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user
# - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password
# chronograf:
# image: chronograf:1.5
# container_name: appwrite-chronograf
# restart: unless-stopped
# networks:
# - appwrite
# volumes:
# - appwrite-chronograf:/var/lib/chronograf
# ports:
# - "8888:8888"
# environment:
# - INFLUXDB_URL=http://influxdb:8086
# - KAPACITOR_URL=http://kapacitor:9092
# - AUTH_DURATION=48h
# - TOKEN_SECRET=duperduper5674829!jwt
# - GH_CLIENT_ID=d86f7145a41eacfc52cc
# - GH_CLIENT_SECRET=9e0081062367a2134e7f2ea95ba1a32d08b6c8ab
# - GH_ORGS=appwrite
networks:
gateway:
appwrite:
volumes:
appwrite-mariadb:
appwrite-redis:
appwrite-cache:
appwrite-uploads:
appwrite-certificates:
appwrite-functions:
appwrite-influxdb:
appwrite-chronograf:
appwrite-config:

View file

@ -21,7 +21,6 @@ services:
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --accesslog=true
restart: unless-stopped
ports:
- 80:80
- 443:443
@ -43,7 +42,6 @@ services:
args:
- TESTING=true
- VERSION=dev
restart: unless-stopped
ports:
- 9501:80
networks:
@ -80,6 +78,7 @@ services:
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_SYSTEM_RESPONSE_FORMAT
- _APP_OPTIONS_ABUSE
- _APP_OPTIONS_FORCE_HTTPS
- _APP_OPENSSL_KEY_V1
@ -100,6 +99,7 @@ services:
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_USAGE_STATS
- _APP_INFLUXDB_HOST
- _APP_INFLUXDB_PORT
- _APP_STORAGE_LIMIT
@ -108,13 +108,13 @@ services:
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_ENVS
appwrite-worker-usage:
entrypoint: worker-usage
container_name: appwrite-worker-usage
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -135,7 +135,6 @@ services:
container_name: appwrite-worker-audits
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -159,7 +158,6 @@ services:
container_name: appwrite-worker-webhooks
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -185,7 +183,6 @@ services:
container_name: appwrite-worker-tasks
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -210,7 +207,6 @@ services:
container_name: appwrite-worker-deletes
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -237,7 +233,6 @@ services:
container_name: appwrite-worker-certificates
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -264,7 +259,6 @@ services:
container_name: appwrite-worker-functions
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -272,8 +266,8 @@ services:
- appwrite-functions:/storage/functions:rw
- /tmp:/tmp:rw
- ./app:/usr/src/code/app
- ./docker:/usr/src/code/docker
- ./src:/usr/src/code/src
- ./docker:/usr/src/code/docker
depends_on:
- redis
- mariadb
@ -291,13 +285,13 @@ services:
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_USAGE_STATS
appwrite-worker-mails:
entrypoint: worker-mails
container_name: appwrite-worker-mails
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -324,9 +318,11 @@ services:
container_name: appwrite-maintenance
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
environment:
@ -334,18 +330,15 @@ services:
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_MAINTENANCE_INTERVAL
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
appwrite-schedule:
entrypoint: schedule
container_name: appwrite-schedule
build:
context: .
restart: unless-stopped
networks:
- appwrite
volumes:
@ -361,13 +354,12 @@ services:
mariadb:
image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:
- appwrite
volumes:
- appwrite-mariadb:/var/lib/mysql:rw
ports:
- "9502:3306"
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=${_APP_DB_SCHEMA}
@ -391,7 +383,6 @@ services:
redis:
image: redis:6.0-alpine
container_name: appwrite-redis
restart: unless-stopped
networks:
- appwrite
volumes:
@ -400,7 +391,6 @@ services:
clamav:
image: appwrite/clamav:1.2.0
container_name: appwrite-clamav
restart: unless-stopped
networks:
- appwrite
volumes:
@ -409,7 +399,6 @@ services:
influxdb:
image: appwrite/influxdb:1.0.0
container_name: appwrite-influxdb
restart: unless-stopped
networks:
- appwrite
volumes:
@ -418,14 +407,25 @@ services:
telegraf:
image: appwrite/telegraf:1.1.0
container_name: appwrite-telegraf
restart: unless-stopped
networks:
- appwrite
# Dev Tools Start ------------------------------------------------------------------------------------------
#
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
#
# Here is a description of the different tools and why are we using them:
#
# MailCatcher - An SMTP server. Catches all system emails and displays them in a nice UI.
# RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks
# RedisCommander - A nice UI for exploring Redis data
# Resque - A nice UI for exploring Reddis pub/sub, view the different queues workloads, pending and failed tasks
# Chronograf - A nice UI for exploring InfluxDB data
# Webgrind - A nice UI for exploring and debugging code-level stuff
maildev: # used mainly for dev tests
image: appwrite/mailcatcher:1.0.0
container_name: appwrite-mailcatcher
restart: unless-stopped
ports:
- '9503:1080'
networks:
@ -434,12 +434,19 @@ services:
request-catcher: # used mainly for dev tests
image: appwrite/requestcatcher:1.0.0
container_name: appwrite-requestcatcher
restart: unless-stopped
ports:
- '9504:5000'
networks:
- appwrite
adminer:
image: adminer
restart: always
ports:
- 9505:8080
networks:
- appwrite
# redis-commander:
# image: rediscommander/redis-commander:latest
# restart: unless-stopped
@ -488,6 +495,8 @@ services:
# - './debug:/tmp'
# ports:
# - '3001:80'
# Dev Tools End ------------------------------------------------------------------------------------------
networks:
gateway:
@ -501,5 +510,5 @@ volumes:
appwrite-certificates:
appwrite-functions:
appwrite-influxdb:
appwrite-chronograf:
appwrite-config:
# appwrite-chronograf:

View file

@ -6,9 +6,15 @@ docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
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
@ -20,3 +26,15 @@ docker buildx build --platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
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

@ -0,0 +1,9 @@
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

@ -0,0 +1,11 @@
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

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

View file

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

View file

@ -0,0 +1,9 @@
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

@ -0,0 +1,12 @@
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

@ -1,149 +0,0 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
daemon off;
events {
worker_connections 2048;
# multi_accept on;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size %_APP_STORAGE_LIMIT%;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# SSL Settings
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
#ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED';
ssl_prefer_server_ciphers off;
# Logging Settings
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip Settings
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
# Brotli Settings
brotli on;
brotli_comp_level 5;
brotli_static on;
brotli_types application/atom+xml application/javascript application/json application/rss+xml
application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype
application/x-font-ttf application/x-javascript application/xhtml+xml application/xml
font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon
image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml;
# Virtual Host Configs
server {
listen 80; ## listen for ipv4; this line is default and implied
listen [::]:80 ipv6only=on; ## listen for ipv6
listen 443 default ssl http2;
#ssl on;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
root /usr/src/code/public;
index index.php index.html index.htm;
server_tokens off;
# Make site accessible from http://localhost/
#server_name localhost;
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
sendfile off;
# Add stdout logging
#error_log /dev/stdout info;
#access_log /dev/stdout;
access_log off;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
try_files $uri $uri/ /index.php?q=$uri&$args;
}
# Media: images, icons, video, audio, HTC
location ~* \.(?:ico|cur|gz|svg|svgz|mp4|ogg|woff|woff2|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and JavaScript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location /images {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location /favicon.png {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/src/code;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
#fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param HTTP_IF_NONE_MATCH $http_if_none_match;
fastcgi_param HTTP_IF_MODIFIED_SINCE $http_if_modified_since;
fastcgi_read_timeout 600;
fastcgi_index index.php;
include fastcgi_params;
}
# deny access to . files, for security
location ~ /\.(?!well-known).* {
#log_not_found off;
deny all;
}
}
}

View file

@ -1,27 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEpDCCAowCCQDLt1wOwov7hTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTkxMTA5MTgyNDA3WhcNMjAxMTA4MTgyNDA3WjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCp
nPP8ck7HKPVorhtIZfxY8Mf+rnhIhenRMzDTmGtq53dhbg4I1kGcy8WZkyh6QaxQ
PyrNKZXBAQtELSWGgjL3PnVsKWVtQT2YzBSIhHTXct6RbYpd2yLmK77IOmu0DtWF
0QX5TxEB/ryDn92uGxIlKptDxqLrsbN1GhZYkXn0jVp6jIqC32YnxJVItzhSqCns
eJrF96XeXZKlN5TmSbpAwbjyZCkCCWi8q5CLpjrhFdud754pmuiJ03xQn9LXhvIx
OzQ8jPiINr+cR19sZHiAwOdb028gjL8VYdvAiOiJ85UwPwPBeCpJkS4FVWxDpBhQ
XjUGbr5YiACcUNenoEjbSut80VhyAqHVPtEY4WMFclua6vuszGQW4cyhWLKO2pN2
lno0EakXDZT9P53OKB1gFDvpyK3LmD6B4OvddIBCbSYgAotkcojOlcHVDYJUVcN6
VgiJQann/QLQXcgu8K0CUY4hVvtHV7RlfMc1QcOote5lpUWDpByYWw8TAUHa7MGX
VjNu4q//w3SVRW1RyxVjtgOznLvAM+kFZFV3epS9A9zyk0XyxDuqjz8oLQwKhzms
/ORaDa7WPlqyJ9mkwRWgs+zthFp5fcMJ4onwXqR9Yem4UMsugBEFFn/3kto2ZsAg
DZjrVnlWOpee645S3GRtNjpu0LFqIfYeuZmowDgYQQIDAQABMA0GCSqGSIb3DQEB
CwUAA4ICAQAbSqv901fqZn2CiSVHP7hZkbPjR3y4gMh5U5UlRviG9pu7ljN7x8Sa
XKPT6AYvDFCe3Xux0EQO2nYVkB8jkEyztZ7DjkF36ww52k/aGD+aQAahqzAgumVK
JsQ4nqek8E1FFquX4uoiVZwulWGQE5XDTJBOzLLHvEmk1xKY1AJ9eEUiUm5eKEvM
kVgQo9nRdkwqWOfV2qmYHQ5co6wpHWL9Vl+jeODeonBmsVcaYXc32bOvtKfJL2rW
8IPrvCEieFlcrK8bnjMPZe1rOichri31nsHfrO8LZFu+ZSU2xsjd4Ao0Fm99/27X
rm+e0XsHpKDok+nYUZ3O8cA16fcn2uZulbihSZDbtaxFJvzHDoBl/reJhVKsNVb8
f99ygx1435GG9NUMTHJtlMel/vu6uujIvFVIfd9Dl3anjaPNBnhuXQCaZUISRMhF
jAiajQJzSzJknEDss+G+WHTbPip7xRl1L43AHdIqlFRwPVRV7pQWi99+2YOtYYo4
YZmqksQeBIi8gS1T7sDIheSkvr9nr40W4ez6RIlgNYFm+RvVue1tQJNoZYH+6lvT
Qoe+YOSaXotOV9mBd8ffH84bYoMvKooldRE4q3azunCl3HDcZcjarrCFmqzIDzQw
yAlhTb8+aXDY2EHCYBmbwV9AcgPtQ5LuCPeAsh+g9pNhaqF/f/ljLA==
-----END CERTIFICATE-----

View file

@ -1,52 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCpnPP8ck7HKPVo
rhtIZfxY8Mf+rnhIhenRMzDTmGtq53dhbg4I1kGcy8WZkyh6QaxQPyrNKZXBAQtE
LSWGgjL3PnVsKWVtQT2YzBSIhHTXct6RbYpd2yLmK77IOmu0DtWF0QX5TxEB/ryD
n92uGxIlKptDxqLrsbN1GhZYkXn0jVp6jIqC32YnxJVItzhSqCnseJrF96XeXZKl
N5TmSbpAwbjyZCkCCWi8q5CLpjrhFdud754pmuiJ03xQn9LXhvIxOzQ8jPiINr+c
R19sZHiAwOdb028gjL8VYdvAiOiJ85UwPwPBeCpJkS4FVWxDpBhQXjUGbr5YiACc
UNenoEjbSut80VhyAqHVPtEY4WMFclua6vuszGQW4cyhWLKO2pN2lno0EakXDZT9
P53OKB1gFDvpyK3LmD6B4OvddIBCbSYgAotkcojOlcHVDYJUVcN6VgiJQann/QLQ
Xcgu8K0CUY4hVvtHV7RlfMc1QcOote5lpUWDpByYWw8TAUHa7MGXVjNu4q//w3SV
RW1RyxVjtgOznLvAM+kFZFV3epS9A9zyk0XyxDuqjz8oLQwKhzms/ORaDa7WPlqy
J9mkwRWgs+zthFp5fcMJ4onwXqR9Yem4UMsugBEFFn/3kto2ZsAgDZjrVnlWOpee
645S3GRtNjpu0LFqIfYeuZmowDgYQQIDAQABAoICADrBq2fldVLa5oDX542h/tQU
vUOFzxdYhJI7CIwUfgmvm5R92pDHID2f/Zjg+KG5hGbcKwidgko1AWEhvqElE2DB
G05X3NIHSr5W3DoaoJtOKLn6V3eCBUn1F4cnbc4XYXKU4VvnPv4Q798tD09UA2oq
o1TMR/4cNg239svBwZytJw3TB9ykZTAbkpd5GSLRLIzFjuBLlQM+KSHg6k0Id2Qd
d+NIPUh+V/EcAdvOvxDgUI8axhCloC62u5b2dsTA87+IQeVD9IjDZodN1kmnWHNJ
4BvYV+PPvhY7KzQ8eUnovuLSwYtRBF0t1OJ2ICYif2W/7OCIlpn2qzd7bemcxf/Z
EhZDW856ZluZy5QVAOEWslHCF2di1PoxSJSoyhdHeJHd8QLMUxpKWMAJwakwNJ7W
LximvjCihxq+MoGXhOtQLKFV5YxXqt0/cMaU4QHromInP7o4eSdleQd91W5c5+Fl
4gj9ICHpVXOhbuhO+cx/k53nEMHsyA4XotHgcrF50wUy4Ow9FqaGG0gmL3GEWzbn
S2KUEfPC8nUqezdqtaPpjmQbL4rTp11pYsrN3pd48j+WNodqu2Mi2r3kT3EcvosI
8okAdW82GhMWZjmGn6SLB//RegP7E28sbqEFZFUPvOd2g+Lma/kVAx7qI4/5ndey
pffNnfHgqqNiSe9WsG0BAoIBAQDcKtrKpuBpb3ENZf1Q0EWBi1RveilKGbaHPrq0
ESiiLfG1nyL5o0LI/LEYspjiad5Z/VDPLpRux7yI2fDHYeDsMvVeHCjvJe2Csw4D
iYSeHGIBMgsMI8573+t0DqFHdfe97Ds1JFflpotnAo8dBwu6bxvXLofCQZuLwa7E
73G515JqK9Koxv32Wp6RnnkKRxRPwFpmjbepwWf2nUvndyy0YjlmpNSsg89Nc2cA
a86k/V2Wt7Nt9I4hPZ9E4CeW2DakJnm5fasBEPiK7qtt2bp+/YPQNACGJarIy+3F
OcRdA0gTDZ9x9H3zHS9G5Dn1Szp3dG7dhVoIMyRILAG5d8yxAoIBAQDFN8h48RPH
K85guCiDrIO1dpG773YFJ/vy6zMXKf8bF4Hw2LaIO1adKvMIIJbpvGcvmFWLsU+I
XbZjeoMe9LH60m0tIqGxvV7SSbHPX/1MmDOoSw83e0x0LOKaWocmorxBhtOdeAB9
WT5xb7K9exgUKBi2dYLW0P+WUG4n0M4RivB6y1mKIRNtqcaDu0ZbyW+cAF7G4RlM
CFV89dcM36DwvvVe4R7HPSk+tDgm01gocYEntC6K26BeW65k2Sfe9CJAEGazdbak
u8v9PJ55XZWyGkIVpoKF4loF7BYdzoOmgkYM+f0afMyhmBfJ5H6fNFYLgrutmg4b
8YCvaL2D2aiRAoIBAQCnMvxZLgX6zCEE1dFcT+6ZBKCo0BMPLRvK9b6ABQ/gqheH
oETFZFDRpeUwJmGogFHV8WQvEuaygokRPMF4CULw3XotcCE+DIWk3inkUcke8dsT
oVd2brLerBx5VKryRApSd1Y3c1Q1GReAsRbSKomjmcGA1ttOkNh5eCsrb9PkGGwe
qQ0gE47GSedmGv086uHn9uIwQ6uZBUHYrXf5Xi3bB0UkSEUihi8mWF9+mGCkN62d
SgC/nhtZ7xxHCBvImIZWfsmuLltxQdweVkZl9BWHXyt9MCC9v1lFiGkXgFk5ccaI
ga32sn/74swGgEfrmqfaE9gl7qGC3KPPE2xz1yDhAoIBAEoECYT6VUXmtumts+bX
FAdCnKc/07dTrkcY5m/HHyr3w5i0fKzcOEF8IQHn2TuXrdI7BcALp6GyKgVjsVoo
07Mizj6mRLEENVYOumDt0Y6xgJGkue1EpQjk35a2awqhAK5G/5yVsPlaSQkhtp9O
V1cZRU0VBSnB/mpXfUAMKYqD7oTnVI92omgB07MU0e8Yxn5x1SAm0uuqJQtk6HS4
aRpxUH1vV7HGznfuAzTvFKL5FlPkV6Ndke5X0jefGEugrEoG3cR0ZTumD4TW/1Ll
QI07NZoSh+HfdZHLbPF61AXl1oyANfF+7P2oqyTmUG9HoRNo2S7qJmluVbF/ScD2
K0ECggEBAJj8ZlacBoTxXy9mf6pMnDpCznzhs1AHQMZ6XIqgW3QoUNkAm4Fnf7G4
PdhXRAm2NeQ+CSBtmHl7PZHzLlF4RcQnC9DQHy/P+PGfcYfwWNqZyF7pAkwhtFYK
zsxekl6PxjZ6DvclwU3jgQ4OiNElcF2Yjhtwh0nryyz+SktJieFKoWUz1F71tONw
5TNbiKb1sWKzZvvcvyjUdLuMH/HzfNg5ZxxiJHrD0Zga1jLMzZil+eYhIbUuQT9b
IdEYCvfruwoF/SjjPH/aDPu6jZug/iE288Msqm/YGC9uTW1/GLhrpIyWdposa1Jh
Fdo3SqBMHZO0uGFt0FSpZNv0jNUJdB0=
-----END PRIVATE KEY-----

View file

@ -1,200 +0,0 @@
[unix_http_server]
file=/tmp/supervisor.sock ; (the path to the socket file)
[supervisord]
;logfile=/tmp/supervisord.log ; (main log file;default.conf $CWD/supervisord.log)
logfile=/dev/null
logfile_maxbytes=0 ; (max main logfile bytes b4 rotation;default.conf 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default.conf 10)
loglevel=info ; (log level;default.conf info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default.conf supervisord.pid)
nodaemon=false ; (start in foreground if true;default.conf false)
minfds=1024 ; (min. avail startup file descriptors;default.conf 1024)
minprocs=200 ; (min. avail process descriptors;default.conf 200)
user=root ;
; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
[program:php7-fpm]
command=php-fpm%(ENV_PHP_VERSION)s -F
autostart=true
autorestart=true
priority=5
stdout_events_enabled=true
stderr_events_enabled=true
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:nginx]
command=nginx
autostart=true
autorestart=true
priority=10
stdout_events_enabled=true
stderr_events_enabled=true
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-webhooks]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-webhooks',APP_INCLUDE='/usr/src/code/app/workers/webhooks.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-mails]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-mails',APP_INCLUDE='/usr/src/code/app/workers/mails.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-audits]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-audits',APP_INCLUDE='/usr/src/code/app/workers/audits.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-usage]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-usage',APP_INCLUDE='/usr/src/code/app/workers/usage.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-tasks]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-tasks',APP_INCLUDE='/usr/src/code/app/workers/tasks.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-deletes]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-deletes',APP_INCLUDE='/usr/src/code/app/workers/deletes.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-certificates]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-certificates',APP_INCLUDE='/usr/src/code/app/workers/certificates.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-functions]
command=php /usr/src/code/vendor/bin/resque
autostart=true
autorestart=true
priority=10
environment=QUEUE='v1-functions',APP_INCLUDE='/usr/src/code/app/workers/functions.php',REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stdout_logfile=/var/log/appwrite.log
stdout_logfile_maxbytes=5000000
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
[program:v1-schedule]
command=php /usr/src/code/vendor/bin/resque-scheduler
autostart=true
autorestart=true
priority=10
environment=REDIS_BACKEND='%(ENV__APP_REDIS_HOST)s:%(ENV__APP_REDIS_PORT)s',RESQUE_PHP='/usr/src/code/vendor/autoload.php'
stdout_events_enabled=true
stderr_events_enabled=true
stopsignal=QUIT
startretries=10
;stdout_logfile=/dev/stdout
;stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0

View file

@ -1,412 +0,0 @@
; Start a new pool named 'www'.
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('www' here)
[www]
; Per pool prefix
; It only applies on the following directives:
; - 'access.log'
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or /usr) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
; will be used.
user = www-data
group = www-data
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all IPv4 addresses on a
; specific port;
; '[::]:port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
; listen = /var/run/php5-fpm.sock
listen = 127.0.0.1:9000
; Set listen(2) backlog.
; Default Value: 65535 (-1 on FreeBSD and OpenBSD)
;listen.backlog = 65535
; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
; Default Values: user and group are set as the running user
; mode is set to 0660
listen.owner = www-data
listen.group = www-data
;listen.mode = 0660
; When POSIX Access Control Lists are supported you can set them using
; these options, value is a comma separated list of user/group names.
; When set, listen.owner and listen.group are ignored
;listen.acl_users =
;listen.acl_groups =
; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
;listen.allowed_clients = 127.0.0.1
; Specify the nice(2) priority to apply to the pool processes (only if set)
; The value can vary from -19 (highest priority) to 20 (lower priority)
; Note: - It will only work if the FPM master process is launched as root
; - The pool processes will inherit the master process priority
; unless it specified otherwise
; Default Value: no set
; process.priority = -19
; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives. With this process management, there will be
; always at least 1 children.
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; ondemand - no children are created at startup. Children will be forked when
; new requests will connect. The following parameter are used:
; pm.max_children - the maximum number of children that
; can be alive at the same time.
; pm.process_idle_timeout - The number of seconds after which
; an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 5
; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 2
; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 1
; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 3
; The number of seconds after which an idle process will be killed.
; Note: Used only when pm is set to 'ondemand'
; Default Value: 10s
;pm.process_idle_timeout = 10s;
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
;pm.max_requests = 500
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following informations:
; pool - the name of the pool;
; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
; listen queue - the number of request in the queue of pending
; connections (see backlog in listen(2));
; max listen queue - the maximum number of requests in the queue
; of pending connections since FPM has started;
; listen queue len - the size of the socket queue of pending connections;
; idle processes - the number of idle processes;
; active processes - the number of active processes;
; total processes - the number of idle + active processes;
; max active processes - the maximum number of active processes since FPM
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www
; process manager: static
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 62636
; accepted conn: 190460
; listen queue: 0
; max listen queue: 1
; listen queue len: 42
; idle processes: 4
; active processes: 11
; total processes: 15
; max active processes: 12
; max children reached: 0
;
; By default the status page output is formatted as text/plain. Passing either
; 'html', 'xml' or 'json' in the query string will return the corresponding
; output syntax. Example:
; http://www.foo.bar/status
; http://www.foo.bar/status?json
; http://www.foo.bar/status?html
; http://www.foo.bar/status?xml
;
; By default the status page only outputs short status. Passing 'full' in the
; query string will also return status for each pool process.
; Example:
; http://www.foo.bar/status?full
; http://www.foo.bar/status?json&full
; http://www.foo.bar/status?html&full
; http://www.foo.bar/status?xml&full
; The Full status returns for each process:
; pid - the PID of the process;
; state - the state of the process (Idle, Running, ...);
; start time - the date and time the process has started;
; start since - the number of seconds since the process has started;
; requests - the number of requests the process has served;
; request duration - the duration in µs of the requests;
; request method - the request method (GET, POST, ...);
; request URI - the request URI with the query string;
; content length - the content length of the request (only with POST);
; user - the user (PHP_AUTH_USER) (or '-' if not set);
; script - the main script called (or '-' if not set);
; last request cpu - the %cpu the last request consumed
; it's always 0 if the process is not in Idle state
; because CPU calculation is done when the request
; processing has terminated;
; last request memory - the max amount of memory the last request consumed
; it's always 0 if the process is not in Idle state
; because memory calculation is done when the request
; processing has terminated;
; If the process is in Idle state, then informations are related to the
; last request the process has served. Otherwise informations are related to
; the current request being served.
; Example output:
; ************************
; pid: 31330
; state: Running
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 63087
; requests: 12808
; request duration: 1250261
; request method: GET
; request URI: /test_mem.php?N=10000
; content length: 0
; user: -
; script: /home/fat/web/docs/php/test_mem.php
; last request cpu: 0.00
; last request memory: 0
;
; Note: There is a real-time FPM status monitoring sample web page available
; It's available in: /usr/share/php5/fpm/status.html
;
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;pm.status_path = /status
; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;ping.path = /ping
; This directive may be used to customize the response of a ping request. The
; response is formatted as text/plain with a 200 response code.
; Default Value: pong
;ping.response = pong
; The access log file
; Default: not set
;access.log = log/$pool.access.log
; The access log format.
; The following syntax is allowed
; %%: the '%' character
; %C: %CPU used by the request
; it can accept the following format:
; - %{user}C for user CPU only
; - %{system}C for system CPU only
; - %{total}C for user + system CPU (default)
; %d: time taken to serve the request
; it can accept the following format:
; - %{seconds}d (default)
; - %{miliseconds}d
; - %{mili}d
; - %{microseconds}d
; - %{micro}d
; %e: an environment variable (same as $_ENV or $_SERVER)
; it must be associated with embraces to specify the name of the env
; variable. Some exemples:
; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
; %f: script filename
; %l: content-length of the request (for POST request only)
; %m: request method
; %M: peak of memory allocated by PHP
; it can accept the following format:
; - %{bytes}M (default)
; - %{kilobytes}M
; - %{kilo}M
; - %{megabytes}M
; - %{mega}M
; %n: pool name
; %o: output header
; it must be associated with embraces to specify the name of the header:
; - %{Content-Type}o
; - %{X-Powered-By}o
; - %{Transfert-Encoding}o
; - ....
; %p: PID of the child that serviced the request
; %P: PID of the parent of the child that serviced the request
; %q: the query string
; %Q: the '?' character if query string exists
; %r: the request URI (without the query string, see %q and %Q)
; %R: remote IP address
; %s: status (response code)
; %t: server time the request was received
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; %T: time the log has been written (the request has finished)
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; %u: remote user
;
; Default: "%R - %u %t \"%m %r\" %s"
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
;slowlog = log/$pool.log.slow
; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_slowlog_timeout = 0
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_terminate_timeout = 0
; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024
; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0
; Chroot to this directory at the start. This value must be defined as an
; absolute path. When this value is not set, chroot is not used.
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
; of its subdirectories. If the pool prefix is not set, the global prefix
; will be used instead.
; Note: chrooting is a great security feature and should be used whenever
; possible. However, all PHP paths will be relative to the chroot
; (error_log, sessions.save_path, ...).
; Default Value: not set
;chroot =
; Chdir to this directory at the start.
; Note: relative path can be used.
; Default Value: current directory or / when chroot
chdir = /
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environement, this can cause some delay in the page
; process time (several ms).
; Default Value: no
;catch_workers_output = yes
; Clear environment in FPM workers
; Prevents arbitrary environment variables from reaching FPM worker processes
; by clearing the environment in workers before env vars specified in this
; pool configuration are added.
; Setting to "no" will make all environment variables available to PHP code
; via getenv(), $_ENV and $_SERVER.
; Default Value: yes
;clear_env = no
; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; exectute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
;security.limit_extensions = .php .php3 .php4 .php5
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr)
; Default Value: nothing is defined by default except the values in php.ini and
; specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
;php_admin_value[error_log] = /var/log/fpm-php.www.log
;php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M

View file

@ -0,0 +1,18 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Avatars avatars = Avatars(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
String result = avatars.getBrowser(
code: 'aa',
);
print(result); // Resource URL string
}

View file

@ -0,0 +1,18 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Avatars avatars = Avatars(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
String result = avatars.getCreditCard(
code: 'amex',
);
print(result); // Resource URL string
}

View file

@ -0,0 +1,18 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Avatars avatars = Avatars(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
String result = avatars.getFavicon(
url: 'https://example.com',
);
print(result); // Resource URL string
}

View file

@ -0,0 +1,18 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Avatars avatars = Avatars(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
String result = avatars.getFlag(
code: 'af',
);
print(result); // Resource URL string
}

View file

@ -0,0 +1,18 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Avatars avatars = Avatars(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
String result = avatars.getImage(
url: 'https://example.com',
);
print(result); // Resource URL string
}

View file

@ -0,0 +1,17 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Avatars avatars = Avatars(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
String result = avatars.getInitials(
);
print(result); // Resource URL string
}

View file

@ -0,0 +1,18 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Avatars avatars = Avatars(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
String result = avatars.getQR(
text: '[TEXT]',
);
print(result); // Resource URL string
}

View file

@ -0,0 +1,26 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Database database = Database(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
Future result = database.createCollection(
name: '[NAME]',
read: [],
write: [],
rules: [],
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,26 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Database database = Database(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
Future result = database.createDocument(
collectionId: '[COLLECTION_ID]',
data: {},
read: [],
write: [],
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,23 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Database database = Database(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
Future result = database.deleteCollection(
collectionId: '[COLLECTION_ID]',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,24 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Database database = Database(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
Future result = database.deleteDocument(
collectionId: '[COLLECTION_ID]',
documentId: '[DOCUMENT_ID]',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,23 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Database database = Database(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
Future result = database.getCollection(
collectionId: '[COLLECTION_ID]',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,24 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Database database = Database(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
Future result = database.getDocument(
collectionId: '[COLLECTION_ID]',
documentId: '[DOCUMENT_ID]',
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}

View file

@ -0,0 +1,22 @@
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Database database = Database(client);
client
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
Future result = database.listCollections(
);
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