mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '0.13.x' of github.com:appwrite/appwrite into feat-func-storage-sync
This commit is contained in:
@@ -20,6 +20,7 @@ _APP_DB_PORT=3306
|
||||
_APP_DB_SCHEMA=appwrite
|
||||
_APP_DB_USER=user
|
||||
_APP_DB_PASS=password
|
||||
_APP_STORAGE_DEVICE=Local
|
||||
_APP_STORAGE_ANTIVIRUS=disabled
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||
@@ -32,7 +33,7 @@ _APP_SMTP_PORT=1025
|
||||
_APP_SMTP_SECURE=
|
||||
_APP_SMTP_USERNAME=
|
||||
_APP_SMTP_PASSWORD=
|
||||
_APP_STORAGE_LIMIT=10000000
|
||||
_APP_STORAGE_LIMIT=30000000
|
||||
_APP_FUNCTIONS_TIMEOUT=900
|
||||
_APP_FUNCTIONS_BUILD_TIMEOUT=900
|
||||
_APP_FUNCTIONS_CONTAINERS=10
|
||||
|
||||
+13
-19
@@ -1,9 +1,10 @@
|
||||
name: "Tests"
|
||||
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
tests:
|
||||
name: Unit & E2E
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -18,29 +19,16 @@ jobs:
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: Prepare Docker
|
||||
- name: Build Appwrite
|
||||
# Upstream bug causes buildkit pulls to fail so prefetch base images
|
||||
# https://github.com/moby/moby/issues/41864
|
||||
run: |
|
||||
export COMPOSE_INTERACTIVE_NO_CLI
|
||||
export DOCKER_BUILDKIT=1
|
||||
export COMPOSE_DOCKER_CLI_BUILD=1
|
||||
echo "_APP_FUNCTIONS_RUNTIMES=php-8.0" >> .env
|
||||
docker pull composer:2.0
|
||||
docker pull php:8.0-cli-alpine
|
||||
docker compose pull
|
||||
|
||||
- name: Prepare Cache
|
||||
uses: satackey/action-docker-layer-caching@v0.0.11
|
||||
# Ignore the failure of a step and avoid terminating the job.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Build Appwrite
|
||||
run: docker compose build --progress=plain
|
||||
|
||||
- name: Start Appwrite
|
||||
run: |
|
||||
docker compose build --progress=plain
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
sleep 10
|
||||
- name: Doctor
|
||||
run: docker compose exec -T appwrite doctor
|
||||
|
||||
@@ -49,3 +37,9 @@ jobs:
|
||||
|
||||
- name: Run Tests
|
||||
run: docker compose exec -T appwrite test --debug
|
||||
|
||||
- name: Teardown
|
||||
if: always()
|
||||
run: |
|
||||
docker compose down -v
|
||||
docker ps -aq | xargs docker rm --force
|
||||
+30
@@ -1,4 +1,5 @@
|
||||
# Unreleased Version 0.13.0
|
||||
|
||||
- Added ability to create syncronous function executions
|
||||
- Introduced new execution model for functions
|
||||
- Improved functions execution times
|
||||
@@ -10,6 +11,20 @@
|
||||
- Updated endpoints to reflect the new terminology
|
||||
- Updated UI with these changes
|
||||
- Updated event names from `function.tags.*` to `function.deployments.*`
|
||||
|
||||
# Version 0.12.2
|
||||
|
||||
## Bugs
|
||||
- Fix security vulnerability in the Console (#2778)
|
||||
- Fix security vulnerability in the ACME-Challenge (#2780)
|
||||
|
||||
## Upgrades
|
||||
|
||||
- Upgraded `redis` extenstion to version 5.3.6
|
||||
- Upgraded `swoole` extenstion to version 4.8.6
|
||||
- Upgraded `imagick` extenstion to version 3.7.0
|
||||
- Upgraded GEO IP database to version February 2022
|
||||
|
||||
# Version 0.12.1
|
||||
|
||||
## Bugs
|
||||
@@ -95,6 +110,21 @@
|
||||
- Upgraded Redis to 6.2
|
||||
- Upgraded InfluxDB to 1.4.0
|
||||
- Upgraded Telegraf to 1.3.0
|
||||
|
||||
# Version 0.11.1
|
||||
|
||||
## Bugs
|
||||
- Fix security vulnerability in the Console (#2777)
|
||||
- Fix security vulnerability in the ACME-Challenge (#2779)
|
||||
|
||||
## Upgrades
|
||||
- Upgraded redis extenstion to version 5.3.6
|
||||
- Upgraded swoole extenstion to version 4.8.6
|
||||
- Upgraded imagick extenstion to version 3.7.0
|
||||
- Upgraded yaml extenstion to version 2.2.2
|
||||
- Upgraded maxminddb extenstion to version 1.11.0
|
||||
- Upgraded GEO IP database to version February 2022
|
||||
|
||||
# Version 0.11.0
|
||||
|
||||
## Features
|
||||
|
||||
+12
-3
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
|
||||
--no-plugins --no-scripts --prefer-dist \
|
||||
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
|
||||
|
||||
FROM node:16-alpine as node
|
||||
FROM node:16.13.2-alpine3.15 as node
|
||||
|
||||
WORKDIR /usr/local/src/
|
||||
|
||||
@@ -24,7 +24,7 @@ COPY public /usr/local/src/public
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
|
||||
FROM php:8.0.14-cli-alpine as compile
|
||||
FROM php:8.0.14-cli-alpine3.15 as compile
|
||||
|
||||
ARG DEBUG=false
|
||||
ENV DEBUG=$DEBUG
|
||||
@@ -123,7 +123,7 @@ RUN \
|
||||
./configure && \
|
||||
make && make install
|
||||
|
||||
FROM php:8.0.14-cli-alpine as final
|
||||
FROM php:8.0.14-cli-alpine3.15 as final
|
||||
|
||||
LABEL maintainer="team@appwrite.io"
|
||||
|
||||
@@ -152,6 +152,15 @@ ENV _APP_SERVER=swoole \
|
||||
_APP_STORAGE_ANTIVIRUS=enabled \
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav \
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310 \
|
||||
_APP_STORAGE_DEVICE=Local \
|
||||
_APP_STORAGE_DEVICE_S3_ACCESS_KEY= \
|
||||
_APP_STORAGE_DEVICE_S3_SECRET= \
|
||||
_APP_STORAGE_DEVICE_S3_REGION= \
|
||||
_APP_STORAGE_DEVICE_S3_BUCKET= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_SECRET= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_REGION= \
|
||||
_APP_STORAGE_DEVICE_DO_SPACES_BUCKET= \
|
||||
_APP_REDIS_HOST=redis \
|
||||
_APP_REDIS_PORT=6379 \
|
||||
_APP_DB_HOST=mariadb \
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
<br />
|
||||
<p align="center">
|
||||
<a href="https://appwrite.io" target="_blank"><img width="260" height="39" src="https://appwrite.io/images/appwrite.svg" alt="Appwrite Logo"></a>
|
||||
<br />
|
||||
<br />
|
||||
<b>适用于[Flutter/Vue/Angular/React/iOS/Android/* 等等平台 *]的完整后端服务</b>
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
|
||||
<!-- [](https://hacktoberfest.appwrite.io) -->
|
||||
[](https://appwrite.io/discord?r=Github)
|
||||
[](https://hub.docker.com/r/appwrite/appwrite)
|
||||
[](https://travis-ci.com/appwrite/appwrite)
|
||||
[](https://twitter.com/appwrite)
|
||||
[](docs/tutorials/add-translations.md)
|
||||
[](https://store.appwrite.io)
|
||||
|
||||
[English](README.md) | 简体中文
|
||||
|
||||
Appwrite是一个基于Docker的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面极简了从零编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
|
||||
|
||||
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存, 图像处理,云函数计算,[等多种服务](https:/ /appwrite.io/docs)。
|
||||
|
||||

|
||||
|
||||
更多信息请到 Appwrite 官网查看: [https://appwrite.io](https://appwrite.io)
|
||||
|
||||
内容:
|
||||
|
||||
- [安装](#安装)
|
||||
- [Unix](#unix)
|
||||
- [Windows](#windows)
|
||||
- [CMD](#cmd)
|
||||
- [PowerShell](#powershell)
|
||||
- [从旧版本升级](#从旧版本升级)
|
||||
- [快速入门](#入门)
|
||||
- [软件服务](#软件服务)
|
||||
- [开发套件](#开发套件)
|
||||
- [客户端](#客户端)
|
||||
- [服务器](#服务器)
|
||||
- [开发者社区](#开发者社区)
|
||||
- [软件架构]](#软件架构)
|
||||
- [贡献代码](#贡献代码)
|
||||
- [安全](#安全)
|
||||
- [订阅我们](#订阅我们)
|
||||
- [版权说明](#版权说明)
|
||||
|
||||
## 安装
|
||||
|
||||
Appwrite 的容器化服务器只需要一行指令就可以运行。您可以使用 docker-compose 在本地主机上运行 Appwrite,也可以在任何其他容器化工具(如 Kubernetes、Docker Swarm 或 Rancher)上运行 Appwrite。
|
||||
|
||||
开始运行 Appwrite 服务器的最简单方法是运行我们的 docker-compose 文件。在运行安装命令之前,请确保您的机器上安装了 [Docker](https://dockerdocs.cn/get-docker/index.html):
|
||||
|
||||
### Unix
|
||||
|
||||
```bash
|
||||
docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:0.12.2
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
#### CMD
|
||||
|
||||
```cmd
|
||||
docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:0.12.2
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
||||
```powershell
|
||||
docker run -it --rm ,
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock ,
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
|
||||
--entrypoint="install" ,
|
||||
appwrite/appwrite:0.12.2
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
|
||||
需要自定义容器构架,请查看我们的 Docker [环境变量](https://appwrite.io/docs/environment-variables) 文档。您还可以参考我们的 [docker-compose.yml](https://gist.github.com/eldadfux/977869ff6bdd7312adfd4e629ee15cc5#file-docker-compose-yml) 文件手动设置环境。
|
||||
|
||||
### 从旧版本升级
|
||||
|
||||
如果您从旧版本升级 Appwrite 服务器,则应在设置完成后使用 Appwrite 迁移工具。有关这方面的更多信息,请查看 [安装文档](https://appwrite.io/docs/installation)。
|
||||
|
||||
## 入门
|
||||
|
||||
开始使用 Appwrite 只需要在控制台创建一个新项目,选择开发平台,然后抓取我们的开发套件。您可以从以下的教程中找到你喜欢的平台开始使用 Appwrite。
|
||||
|
||||
* [开始使用 Web](https://appwrite.io/docs/getting-started-for-web)
|
||||
* [开始使用 Flutter](https://appwrite.io/docs/getting-started-for-flutter)
|
||||
* [开始使用 Apple](https://appwrite.io/docs/getting-started-for-apple)
|
||||
* [开始使用 Android](https://appwrite.io/docs/getting-started-for-android)
|
||||
* [开始使用 Server](https://appwrite.io/docs/getting-started-for-server)
|
||||
* [开始使用 CLI](https://appwrite.io/docs/command-line)
|
||||
|
||||
### 软件服务
|
||||
|
||||
* [**帐户**](https://appwrite.io/docs/client/account) -管理当前用户的帐户和登录方式。跟踪和管理用户 Session,登录设备,登录方法和查看相关记录。
|
||||
* [**用户**](https://appwrite.io/docs/server/users) - 在以管理员模式登录时管理和列出所有用户。
|
||||
* [**团队**](https://appwrite.io/docs/client/teams) - 管理用户分组。邀请成员,管理团队中的用户权限和用户角色。
|
||||
* [**数据库**](https://appwrite.io/docs/client/database) - 管理数据库文档和文档集。用检索界面来对文档和文档集进行读取,创建,更新,和删除。
|
||||
* [**贮存**](https://appwrite.io/docs/client/storage) - 管理文件的阅读、创建、删除和预览。设置文件的预览来满足程序的个性化需求。所有文件都由 ClamAV 扫描并安全存储和加密。
|
||||
* [**云函数**](https://appwrite.io/docs/server/functions) - 在安全,隔离的环境中运行自定义代码。这些代码可以被事件,CRON,或者手动操作触发。
|
||||
* [**语言适配**](https://appwrite.io/docs/client/locale) - 根据用户所在的的国家和地区做出合适的语言适配。
|
||||
* [**头像**](https://appwrite.io/docs/client/avatars) -管理用户头像、国家旗帜、浏览器图标、信用卡符号,和生成二维码。
|
||||
如需完整的 API 界面文档,请访问 [https://appwrite.io/docs](https://appwrite.io/docs)。如需更多教程、新闻和公告,请订阅我们的 [博客](https://medium.com/appwrite-io) 和 加入我们的[Discord 社区](https://discord.gg/GSeTUeA)。
|
||||
|
||||
### 开发套件
|
||||
|
||||
以下是当前支持的平台和语言列表。如果您想帮助我们为您选择的平台添加支持,您可以访问我们的 [SDK 生成器](https://github.com/appwrite/sdk-generator) 项目并查看我们的 [贡献指南]( https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md)。
|
||||
|
||||
#### 客户端
|
||||
* ✅ [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护)
|
||||
* ✅ [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护)
|
||||
* ✅ [Apple](https://github.com/appwrite/sdk-for-apple) - **公测** (由 Appwrite 团队维护)
|
||||
* ✅ [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护)
|
||||
|
||||
#### 服务器
|
||||
* ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
|
||||
* ✅ [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwr实验 团队维护)
|
||||
* ✅ [Dart](https://github.com/appwrite/sdk-for-dart) - (由 Appwrite 团队维护)
|
||||
* ✅ [Deno](https://github.com/appwrite/sdk-for-deno) - **公测** (由 Appwrite 团队维护)
|
||||
* ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
|
||||
* ✅ [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护)
|
||||
* ✅ [Kotlin](https://github.com/appwrite/sdk-for-kotlin) - **公测** (由 Appwrite 团队维护)
|
||||
* ✅ [Apple](https://github.com/appwrite/sdk-for-apple) - **公测** (由 Appwrite 团队维护)
|
||||
* ✅ [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护)
|
||||
|
||||
#### 开发者社区
|
||||
* ✅ [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/))
|
||||
* ✅ [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub))
|
||||
|
||||
找不到需要的的 SDK? - 欢迎通过发起PR来帮助我们完善Appwrite的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)!
|
||||
|
||||
|
||||
## 软件架构
|
||||
|
||||

|
||||
|
||||
Appwrite 使用高拓展性的微服务架构。此外,Appwrite 支持多种 API(REST、WebSocket 和 即将推出的 GraphQL),来迎合您的个性化开发习惯。
|
||||
|
||||
Appwrite API 界面层利用后台缓存和任务委派来提供极速的响应时间。后台的 Worker 代理还允许您使用消息队列来处理负载,并精确控制硬件合理分配和成本。您可以在 [贡献指南](CONTRIBUTING.md#architecture-1) 中了解有关我们架构的更多信息。
|
||||
|
||||
## 贡献代码
|
||||
|
||||
所有代码贡献 - 包括来自具有直接提交更改权限的贡献者 - 都必须提交PR请求并在合并分支之前得到核心开发人员的批准。这是为了确保正确审查所有代码。
|
||||
|
||||
我们欢迎所有人提交PR!如果您愿意提供帮助,可以在 [贡献指南](CONTRIBUTING.md) 中了解有关如何为项目做出贡献的更多信息。
|
||||
|
||||
## 安全
|
||||
|
||||
为了保护您的隐私,请避免在GitHub 上发布安全问题。发送问题至 security@appwrite.io,我们将为您做更细致的解答。
|
||||
|
||||
## 订阅我们
|
||||
|
||||
加入我们在世界各地不断发展的社区!请参阅我们的官方 [博客](https://medium.com/appwrite-io)。在 [Twitter](https://twitter.com/appwrite)、[Facebook 页面](https://www.facebook.com/appwrite.io)、[Facebook 群组](https://www.facebook)、[开发者社区](https://dev.to/appwrite) 等平台订阅我们或加入我们的 [Discord 社区](https://discord.gg/GSeTUeA) 以获得更多帮助,想法和讨论。
|
||||
|
||||
## 版权说明
|
||||
|
||||
版权详情,访问 [BSD 3-Clause License](./LICENSE)。
|
||||
@@ -9,13 +9,16 @@
|
||||
</p>
|
||||
|
||||
<!-- [](https://hacktoberfest.appwrite.io) -->
|
||||
<!-- [](https://travis-ci.com/appwrite/appwrite) -->
|
||||
[](https://appwrite.io/discord?r=Github)
|
||||
[](https://hub.docker.com/r/appwrite/appwrite)
|
||||
[](https://travis-ci.com/appwrite/appwrite)
|
||||
[](https://github.com/appwrite/appwrite/actions)
|
||||
[](https://twitter.com/appwrite)
|
||||
[](docs/tutorials/add-translations.md)
|
||||
[](https://store.appwrite.io)
|
||||
|
||||
English | [简体中文](README-CN.md)
|
||||
|
||||
[**Appwrite 0.12 has been released! Learn what's new!**](https://dev.to/appwrite/its-here-announcing-the-release-of-appwrite-012-5c8b)
|
||||
|
||||
Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker<nobr> microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.
|
||||
@@ -59,7 +62,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:0.12.1
|
||||
appwrite/appwrite:0.12.2
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -71,7 +74,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:0.12.1
|
||||
appwrite/appwrite:0.12.2
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -81,7 +84,7 @@ docker run -it --rm ,
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock ,
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
|
||||
--entrypoint="install" ,
|
||||
appwrite/appwrite:0.12.1
|
||||
appwrite/appwrite:0.12.2
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
+4
-5
@@ -4,11 +4,10 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| < 0.5 | :x: |
|
||||
| 0.6.x | :white_check_mark: |
|
||||
| 0.7.x | :white_check_mark: |
|
||||
| 0.8.0 | :white_check_mark: |
|
||||
| <= 0.10 | :x: |
|
||||
| 0.11.x | :white_check_mark: |
|
||||
| 0.12.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
For security issues, kindly email us at security@appwrite.io instead of posting a public issue in GitHub.
|
||||
For security issues, kindly email us at security@appwrite.io instead of posting a public issue in GitHub.
|
||||
|
||||
+5
-5
@@ -7,35 +7,35 @@ return [
|
||||
'name' => 'Email/Password',
|
||||
'key' => 'emailPassword',
|
||||
'icon' => '/images/users/email.png',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateSession',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateSession',
|
||||
'enabled' => true,
|
||||
],
|
||||
'magic-url' => [
|
||||
'name' => 'Magic URL',
|
||||
'key' => 'usersAuthMagicURL',
|
||||
'icon' => '/images/users/magic-url.png',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateMagicURLSession',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateMagicURLSession',
|
||||
'enabled' => true,
|
||||
],
|
||||
'anonymous' => [
|
||||
'name' => 'Anonymous',
|
||||
'key' => 'anonymous',
|
||||
'icon' => '/images/users/anonymous.png',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateAnonymousSession',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateAnonymousSession',
|
||||
'enabled' => true,
|
||||
],
|
||||
'invites' => [
|
||||
'name' => 'Invites',
|
||||
'key' => 'invites',
|
||||
'icon' => '/images/users/invites.png',
|
||||
'docs' => 'https://appwrite.io/docs/client/teams?sdk=web#teamsCreateMembership',
|
||||
'docs' => 'https://appwrite.io/docs/client/teams?sdk=web-default#teamsCreateMembership',
|
||||
'enabled' => true,
|
||||
],
|
||||
'jwt' => [
|
||||
'name' => 'JWT',
|
||||
'key' => 'JWT',
|
||||
'icon' => '/images/users/jwt.png',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateJWT',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateJWT',
|
||||
'enabled' => true,
|
||||
],
|
||||
'phone' => [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+418
-208
@@ -6,6 +6,15 @@ use Utopia\Database\Database;
|
||||
$providers = Config::getParam('providers', []);
|
||||
$auth = Config::getParam('auth', []);
|
||||
|
||||
/**
|
||||
* $collection => id of the parent collection where this will be inserted
|
||||
* $id => id of this collection
|
||||
* name => name of this collection
|
||||
* project => whether or not this collection should be created per project
|
||||
* attributes => list of attributes
|
||||
* indexes => list of indexes
|
||||
*/
|
||||
|
||||
$collections = [
|
||||
'collections' => [
|
||||
'$collection' => Database::METADATA,
|
||||
@@ -99,8 +108,8 @@ $collections = [
|
||||
'$id' => '_fulltext_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [1024],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -574,8 +583,8 @@ $collections = [
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -1106,8 +1115,8 @@ $collections = [
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_deleted_email',
|
||||
@@ -1158,7 +1167,29 @@ $collections = [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'providerToken',
|
||||
'$id' => 'providerAccessToken',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'providerAccessTokenExpiry',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'providerRefreshToken',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
@@ -1422,8 +1453,8 @@ $collections = [
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -1536,196 +1567,6 @@ $collections = [
|
||||
],
|
||||
],
|
||||
|
||||
'files' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'files',
|
||||
'name' => 'Files',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'dateCreated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => 'bucketId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'signature',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'mimeType',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 127, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeOriginal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeActual',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'algorithm',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 255,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'comment',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLVersion',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLCipher',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLTag',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLIV',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_bucket',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['bucketId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'functions' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'functions',
|
||||
@@ -1892,9 +1733,9 @@ $collections = [
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
]
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1981,6 +1822,28 @@ $collections = [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksTotal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksUploaded',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
@@ -2023,8 +1886,8 @@ $collections = [
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -2296,8 +2159,8 @@ $collections = [
|
||||
'$id' => '_fulltext_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [16384],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -2388,6 +2251,131 @@ $collections = [
|
||||
],
|
||||
],
|
||||
|
||||
'buckets' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'buckets',
|
||||
'name' => 'Buckets',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'dateCreated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'signed' => false,
|
||||
'size' => 0,
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'dateUpdated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'enabled',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => 128,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'permission',
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'maximumFileSize',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'signed' => false,
|
||||
'size' => 8,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'allowedFileExtensions',
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => 64,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => true,
|
||||
],
|
||||
[
|
||||
'$id' => 'encryption',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'antivirus',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_fulltext_name',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['name'],
|
||||
'lengths' => [1024],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
|
||||
'stats' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'stats',
|
||||
@@ -2473,7 +2461,7 @@ $collections = [
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
'realtime' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'realtime',
|
||||
@@ -2523,6 +2511,228 @@ $collections = [
|
||||
],
|
||||
]
|
||||
],
|
||||
'files' => [
|
||||
'$collection' => 'buckets',
|
||||
'$id' => 'files',
|
||||
'$name' => 'Files',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'dateCreated',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => 'bucketId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'signature',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'mimeType',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 127, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'metadata',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeOriginal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 8,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'sizeActual',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 8,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'algorithm',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 255,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'comment',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLVersion',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLCipher',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLTag',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'openSSLIV',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksTotal',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'chunksUploaded',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_bucket',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['bucketId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
return $collections;
|
||||
return $collections;
|
||||
|
||||
@@ -82,6 +82,11 @@ return [
|
||||
'model' => Response::MODEL_SESSION,
|
||||
'note' => '',
|
||||
],
|
||||
'account.sessions.update' => [
|
||||
'description' => 'This event triggers when the account session is updated.',
|
||||
'model' => Response::MODEL_SESSION,
|
||||
'note' => '',
|
||||
],
|
||||
'database.collections.create' => [
|
||||
'description' => 'This event triggers when a database collection is created.',
|
||||
'model' => Response::MODEL_COLLECTION,
|
||||
@@ -187,6 +192,21 @@ return [
|
||||
'model' => Response::MODEL_FILE,
|
||||
'note' => '',
|
||||
],
|
||||
'storage.buckets.create' => [
|
||||
'description' => 'This event triggers when a storage bucket is created.',
|
||||
'model' => Response::MODEL_BUCKET,
|
||||
'note' => '',
|
||||
],
|
||||
'storage.buckets.update' => [
|
||||
'description' => 'This event triggers when a storage bucket is updated.',
|
||||
'model' => Response::MODEL_BUCKET,
|
||||
'note' => '',
|
||||
],
|
||||
'storage.buckets.delete' => [
|
||||
'description' => 'This event triggers when a storage bucket is deleted.',
|
||||
'model' => Response::MODEL_BUCKET,
|
||||
'note' => '',
|
||||
],
|
||||
'users.create' => [
|
||||
'description' => 'This event triggers when a user is created from the users API.',
|
||||
'model' => Response::MODEL_USER,
|
||||
|
||||
@@ -116,7 +116,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '0.3.1',
|
||||
'version' => '0.3.3',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
@@ -370,7 +370,7 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '0.2.2',
|
||||
'version' => '0.2.5',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -127,7 +127,7 @@ return [ // Ordered by ABC.
|
||||
'icon' => 'icon-windows',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'form' => 'microsoft.phtml',
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
@@ -287,6 +287,16 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'stripe' => [
|
||||
'name' => 'Stripe',
|
||||
'developers' => 'https://stripe.com/docs/api',
|
||||
'icon' => 'icon-stripe',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
// Keep Last
|
||||
'mock' => [
|
||||
'name' => 'Mock',
|
||||
|
||||
@@ -28,6 +28,8 @@ $admins = [
|
||||
'documents.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
'buckets.read',
|
||||
'buckets.write',
|
||||
'users.read',
|
||||
'users.write',
|
||||
'collections.read',
|
||||
|
||||
@@ -43,6 +43,12 @@ return [ // List of publicly visible scopes
|
||||
'files.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s storage files',
|
||||
],
|
||||
'buckets.read' => [
|
||||
'description' => 'Access to read your project\'s storage buckets',
|
||||
],
|
||||
'buckets.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s storage buckets',
|
||||
],
|
||||
'functions.read' => [
|
||||
'description' => 'Access to read your project\'s functions and code deployments',
|
||||
],
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,6 +2,7 @@
|
||||
|
||||
return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
||||
'default' => __DIR__.'/logos/none.png',
|
||||
'default_image' => __DIR__.'/logos/image.png',
|
||||
|
||||
// Video Files
|
||||
'video/mp4' => __DIR__.'/logos/video.png',
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
@@ -152,7 +152,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_LOGGING_PROVIDER',
|
||||
'description' => 'This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of \'sentry\', \'raygun\', \'appsignal\'',
|
||||
'description' => 'This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of \'sentry\', \'raygun\', \'appsignal\', \'logowl\'',
|
||||
'introduction' => '0.12.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -161,7 +161,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_LOGGING_CONFIG',
|
||||
'description' => 'This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be \'SENTRY_API_KEY;SENTRY_APP_ID\'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key.',
|
||||
'description' => 'This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be \'SENTRY_API_KEY;SENTRY_APP_ID\'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket.',
|
||||
'introduction' => '0.12.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -420,6 +420,78 @@ return [
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE',
|
||||
'description' => 'Select default storage device. The default value is \'Local\'. List of supported adapters are \'Local\', \'S3\' and \'DOSpaces\'.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'Local',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_ACCESS_KEY',
|
||||
'description' => 'AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_SECRET',
|
||||
'description' => 'AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_REGION',
|
||||
'description' => 'AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'us-eas-1',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_S3_BUCKET',
|
||||
'description' => 'AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY',
|
||||
'description' => 'DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_SECRET',
|
||||
'description' => 'DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_REGION',
|
||||
'description' => 'DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'us-eas-1',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE_DO_SPACES_BUCKET',
|
||||
'description' => 'DigitalOcean spaces bucket. Required when storage adapter is set to DOSpaces. You can create spaces in your DigitalOcean console.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
use Appwrite\Database\Validator\CustomId;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Host;
|
||||
@@ -12,6 +11,7 @@ use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\URL\URL as URLParser;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Config\Config;
|
||||
@@ -23,6 +23,7 @@ use Utopia\Database\Validator\UID;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
@@ -315,7 +316,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->label('docs', false)
|
||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('code', '', new Text(2048), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -342,7 +343,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->label('docs', false)
|
||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('code', '', new Text(2048), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -370,7 +371,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->label('docs', false)
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('code', '', new Text(2048), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -430,7 +431,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
}
|
||||
|
||||
$state['failure'] = null;
|
||||
|
||||
$accessToken = $oauth2->getAccessToken($code);
|
||||
$refreshToken =$oauth2->getRefreshToken($code);
|
||||
$accessTokenExpiry = $oauth2->getAccessTokenExpiry($code);
|
||||
|
||||
if (empty($accessToken)) {
|
||||
if (!empty($state['failure'])) {
|
||||
@@ -528,7 +532,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
'userId' => $user->getId(),
|
||||
'provider' => $provider,
|
||||
'providerUid' => $oauth2ID,
|
||||
'providerToken' => $accessToken,
|
||||
'providerAccessToken' => $accessToken,
|
||||
'providerRefreshToken' => $refreshToken,
|
||||
'providerAccessTokenExpiry' => \time() + (int) $accessTokenExpiry,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'expire' => $expiry,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
@@ -1198,6 +1204,7 @@ App::get('/v1/account/logs')
|
||||
'account.update.password',
|
||||
'account.update.prefs',
|
||||
'account.sessions.create',
|
||||
'account.sessions.update',
|
||||
'account.sessions.delete',
|
||||
'account.recovery.create',
|
||||
'account.recovery.update',
|
||||
@@ -1593,8 +1600,8 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
$sessionId = ($sessionId === 'current')
|
||||
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
|
||||
: $sessionId;
|
||||
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
|
||||
: $sessionId;
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
||||
@@ -1647,6 +1654,108 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
throw new Exception('Session not found', 404);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/sessions/:sessionId')
|
||||
->desc('Update Session (Refresh Tokens)')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'account.sessions.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateSession')
|
||||
->label('sdk.description', '/docs/references/account/update-session.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SESSION)
|
||||
->label('abuse-limit', 10)
|
||||
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to update the current device session.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('locale')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($sessionId, $request, $response, $user, $dbForProject, $project, $locale, $audits, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var boolean $force */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
$sessionId = ($sessionId === 'current')
|
||||
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
|
||||
: $sessionId;
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
||||
foreach ($sessions as $key => $session) {/** @var Document $session */
|
||||
if ($sessionId == $session->getId()) {
|
||||
|
||||
// Comment below would skip re-generation if token is still valid
|
||||
// We decided to not include this because developer can get expiration date from the session
|
||||
// I kept code in comment because it might become relevant in the future
|
||||
|
||||
// $expireAt = (int) $session->getAttribute('providerAccessTokenExpiry');
|
||||
// if(\time() < $expireAt - 5) { // 5 seconds time-sync and networking gap, to be safe
|
||||
// return $response->noContent();
|
||||
// }
|
||||
|
||||
$provider = $session->getAttribute('provider');
|
||||
$refreshToken = $session->getAttribute('providerRefreshToken');
|
||||
|
||||
$appId = $project->getAttribute('providers', [])[$provider.'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('providers', [])[$provider.'Secret'] ?? '{}';
|
||||
|
||||
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
|
||||
|
||||
if (!\class_exists($className)) {
|
||||
throw new Exception('Provider is not supported', 501);
|
||||
}
|
||||
|
||||
$oauth2 = new $className($appId, $appSecret, '', [], []);
|
||||
|
||||
$oauth2->refreshTokens($refreshToken);
|
||||
|
||||
$session
|
||||
->setAttribute('providerAccessToken', $oauth2->getAccessToken(''))
|
||||
->setAttribute('providerRefreshToken', $oauth2->getRefreshToken(''))
|
||||
->setAttribute('providerAccessTokenExpiry', \time() + (int) $oauth2->getAccessTokenExpiry(''))
|
||||
;
|
||||
|
||||
$dbForProject->updateDocument('sessions', $sessionId, $session);
|
||||
|
||||
$user->setAttribute("sessions", $sessions);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$audits
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('event', 'account.sessions.update')
|
||||
->setParam('resource', 'user/' . $user->getId())
|
||||
;
|
||||
|
||||
$events
|
||||
->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
|
||||
;
|
||||
|
||||
$usage
|
||||
->setParam('users.sessions.update', 1)
|
||||
->setParam('users.update', 1)
|
||||
;
|
||||
|
||||
return $response->dynamic($session, Response::MODEL_SESSION);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('Session not found', 404);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/sessions')
|
||||
->desc('Delete All Account Sessions')
|
||||
->groups(['api', 'account'])
|
||||
|
||||
@@ -27,10 +27,10 @@ use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Limit as LimitException;
|
||||
use Utopia\Database\Exception\Structure as StructureException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Database\Validator\CustomId;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\IP;
|
||||
use Appwrite\Network\Validator\URL;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -166,8 +166,6 @@ App::post('/v1/database/collections')
|
||||
$collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId;
|
||||
|
||||
try {
|
||||
$dbForProject->createCollection('collection_' . $collectionId);
|
||||
|
||||
$collection = $dbForProject->createDocument('collections', new Document([
|
||||
'$id' => $collectionId,
|
||||
'$read' => $read ?? [], // Collection permissions for collection documents (based on permission model)
|
||||
@@ -179,8 +177,12 @@ App::post('/v1/database/collections')
|
||||
'name' => $name,
|
||||
'search' => implode(' ', [$collectionId, $name]),
|
||||
]));
|
||||
|
||||
$dbForProject->createCollection('collection_' . $collectionId);
|
||||
} catch (DuplicateException $th) {
|
||||
throw new Exception('Collection already exists', 409);
|
||||
} catch (LimitException $th) {
|
||||
throw new Exception('Collection limit exceeded', 400);
|
||||
}
|
||||
|
||||
$audits
|
||||
@@ -2009,14 +2011,18 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
if(!is_null($read)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
if(!is_null($write)) {
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Database\Validator\CustomId;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Storage\Validator\File;
|
||||
use Utopia\Storage\Validator\FileExt;
|
||||
use Utopia\Storage\Validator\FileSize;
|
||||
@@ -50,7 +49,7 @@ App::post('/v1/functions')
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
|
||||
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
|
||||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForProject) {
|
||||
@@ -298,7 +297,7 @@ App::put('/v1/functions/:functionId')
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
|
||||
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
->param('timeout', 15, new Range(1, 900), 'Maximum execution time in seconds.', true)
|
||||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
@@ -470,13 +469,17 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
->inject('usage')
|
||||
->inject('user')
|
||||
->inject('project')
|
||||
->action(function ($functionId, $entrypoint, $file, $activate, $request, $response, $dbForProject, $usage, $user, $project) {
|
||||
->inject('deviceFunctions')
|
||||
->inject('deviceLocal')
|
||||
->action(function ($functionId, $entrypoint, $file, $activate, $request, $response, $dbForProject, $usage, $user, $project, $deviceFunctions, $deviceLocal) {
|
||||
/** @var Utopia\Swoole\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $usage */
|
||||
/** @var Appwrite\Auth\User $user */
|
||||
/** @var Appwrite\Database\Document $project */
|
||||
/** @var Utopia\Storage\Device $deviceFunctions */
|
||||
/** @var Utopia\Storage\Device $deviceLocal */
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
@@ -485,9 +488,8 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
}
|
||||
|
||||
$file = $request->getFiles('code');
|
||||
$device = Storage::getDevice('functions');
|
||||
$fileExt = new FileExt([FileExt::TYPE_GZIP]);
|
||||
$fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
|
||||
$fileSizeValidator = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
|
||||
$upload = new Upload();
|
||||
|
||||
if (empty($file)) {
|
||||
@@ -495,27 +497,62 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
}
|
||||
|
||||
// Make sure we handle a single file and multiple files the same way
|
||||
$file['name'] = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
|
||||
$file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
|
||||
$file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
$fileName = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
|
||||
$fileTmpName = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
|
||||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
|
||||
throw new Exception('File type not allowed', 400);
|
||||
}
|
||||
|
||||
if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit
|
||||
$contentRange = $request->getHeader('content-range');
|
||||
$deploymentId = $dbForProject->getId();
|
||||
$chunk = 1;
|
||||
$chunks = 1;
|
||||
|
||||
if (!empty($contentRange)) {
|
||||
$start = $request->getContentRangeStart();
|
||||
$end = $request->getContentRangeEnd();
|
||||
$fileSize = $request->getContentRangeSize();
|
||||
$deploymentId = $request->getHeader('x-appwrite-id', $deploymentId);
|
||||
if(is_null($start) || is_null($end) || is_null($fileSize)) {
|
||||
throw new Exception('Invalid content-range header', 400);
|
||||
}
|
||||
|
||||
if ($end === $fileSize) {
|
||||
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to notify it's last chunk
|
||||
$chunks = $chunk = -1;
|
||||
} else {
|
||||
// Calculate total number of chunks based on the chunk size i.e ($rangeEnd - $rangeStart)
|
||||
$chunks = (int) ceil($fileSize / ($end + 1 - $start));
|
||||
$chunk = (int) ($start / ($end + 1 - $start)) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
|
||||
throw new Exception('File size not allowed', 400);
|
||||
}
|
||||
|
||||
if (!$upload->isValid($file['tmp_name'])) {
|
||||
if (!$upload->isValid($fileTmpName)) {
|
||||
throw new Exception('Invalid file', 403);
|
||||
}
|
||||
|
||||
// Save to storage
|
||||
$size = $device->getFileSize($file['tmp_name']);
|
||||
$path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
$fileSize ??= $deviceLocal->getFileSize($fileTmpName);
|
||||
$path = $deviceFunctions->getPath($deploymentId.'.'.\pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move'
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
if(!$deployment->isEmpty()) {
|
||||
$chunks = $deployment->getAttribute('chunksTotal', 1);
|
||||
if($chunk == -1) {
|
||||
$chunk = $chunks;
|
||||
}
|
||||
}
|
||||
|
||||
$chunksUploaded = $deviceFunctions->upload($fileTmpName, $path, $chunk, $chunks);
|
||||
|
||||
if (empty($chunksUploaded)) {
|
||||
throw new Exception('Failed moving file', 500);
|
||||
}
|
||||
|
||||
@@ -533,25 +570,50 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
}
|
||||
}
|
||||
|
||||
$deploymentId = $dbForProject->getId();
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceType' => 'functions',
|
||||
'dateCreated' => time(),
|
||||
'entrypoint' => $entrypoint,
|
||||
'path' => $path,
|
||||
'size' => $size,
|
||||
'search' => implode(' ', [$deploymentId, $entrypoint]),
|
||||
'activate' => ((bool) $activate === true),
|
||||
]));
|
||||
if($chunksUploaded === $chunks) {
|
||||
$fileSize = $deviceFunctions->getFileSize($path);
|
||||
|
||||
if ($deployment->isEmpty()) {
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'resourceId' => $function->getId(),
|
||||
'dateCreated' => time(),
|
||||
'entrypoint' => $entrypoint,
|
||||
'path' => $path,
|
||||
'size' => $fileSize,
|
||||
'search' => implode(' ', [$deploymentId, $entrypoint]),
|
||||
'activate' => ((bool) $activate === true),
|
||||
]));
|
||||
} else {
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('size', $fileSize));
|
||||
}
|
||||
} else {
|
||||
if($deployment->isEmpty()) {
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'resourceId' => $function->getId(),
|
||||
'dateCreated' => time(),
|
||||
'entrypoint' => $entrypoint,
|
||||
'path' => $path,
|
||||
'size' => 0,
|
||||
'chunksTotal' => $chunks,
|
||||
'chunksUploaded' => $chunksUploaded,
|
||||
'search' => implode(' ', [$deploymentId, $entrypoint]),
|
||||
'activate' => ((bool) $activate === true),
|
||||
]));
|
||||
} else {
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('chunksUploaded', $chunksUploaded));
|
||||
}
|
||||
}
|
||||
|
||||
// Enqueue a message to start the build
|
||||
Resque::enqueue(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME, [
|
||||
'projectId' => $project->getId(),
|
||||
'functionId' => $function->getId(),
|
||||
'resourceId' => $function->getId(),
|
||||
'deploymentId' => $deploymentId,
|
||||
'type' => BUILD_TYPE_DEPLOYMENT
|
||||
]);
|
||||
@@ -683,11 +745,13 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('deletes')
|
||||
->action(function ($functionId, $deploymentId, $response, $dbForProject, $usage, $deletes) {
|
||||
->inject('deviceFunctions')
|
||||
->action(function ($functionId, $deploymentId, $response, $dbForProject, $usage, $deletes, $deviceFunctions) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $usage */
|
||||
/** @var Appwrite\Event\Event $deletes */
|
||||
/** @var Utopia\Storage\Device $deviceFunctions */
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
if ($function->isEmpty()) {
|
||||
@@ -703,8 +767,10 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
throw new Exception('Deployment not found', 404);
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteDocument('deployments', $deployment->getId())) {
|
||||
throw new Exception('Failed to remove deployment from DB', 500);
|
||||
if ($deviceFunctions->delete($deployment->getAttribute('path', ''))) {
|
||||
if (!$dbForProject->deleteDocument('deployments', $deployment->getId())) {
|
||||
throw new Exception('Failed to remove deployment from DB', 500);
|
||||
}
|
||||
}
|
||||
|
||||
if($function->getAttribute('deployment') === $deployment->getId()) { // Reset function deployment
|
||||
|
||||
@@ -358,11 +358,12 @@ App::get('/v1/health/stats') // Currently only used internally
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->inject('register')
|
||||
->action(function ($response, $register) {
|
||||
->inject('deviceFiles')
|
||||
->action(function ($response, $register, $deviceFiles) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
/** @var Utopia\Storage\Device $deviceFiles */
|
||||
|
||||
$device = Storage::getDevice('files');
|
||||
$cache = $register->get('cache');
|
||||
|
||||
$cacheStats = $cache->info();
|
||||
@@ -370,9 +371,9 @@ App::get('/v1/health/stats') // Currently only used internally
|
||||
$response
|
||||
->json([
|
||||
'storage' => [
|
||||
'used' => Storage::human($device->getDirectorySize($device->getRoot().'/')),
|
||||
'partitionTotal' => Storage::human($device->getPartitionTotalSpace()),
|
||||
'partitionFree' => Storage::human($device->getPartitionFreeSpace()),
|
||||
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot().'/')),
|
||||
'partitionTotal' => Storage::human($deviceFiles->getPartitionTotalSpace()),
|
||||
'partitionFree' => Storage::human($deviceFiles->getPartitionFreeSpace()),
|
||||
],
|
||||
'cache' => [
|
||||
'uptime' => $cacheStats['uptime_in_seconds'] ?? 0,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
use Appwrite\Database\Validator\CustomId;
|
||||
use Appwrite\Network\Validator\CNAME;
|
||||
use Appwrite\Network\Validator\Domain as DomainValidator;
|
||||
use Appwrite\Network\Validator\URL;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\App;
|
||||
@@ -114,6 +114,9 @@ App::post('/v1/projects')
|
||||
$adapter->setup();
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if(($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
|
||||
@@ -537,7 +540,7 @@ App::delete('/v1/projects/:projectId')
|
||||
->inject('deletes')
|
||||
->action(function ($projectId, $password, $response, $user, $dbForConsole, $deletes) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Database\Document $user */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForConsole */
|
||||
/** @var Appwrite\Event\Event $deletes */
|
||||
|
||||
|
||||
+1102
-235
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Database\Validator\CustomId;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Host;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Authorization as AuthorizationException;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::post('/v1/teams')
|
||||
->desc('Create Team')
|
||||
@@ -761,7 +762,11 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteDocument('memberships', $membership->getId())) {
|
||||
try {
|
||||
$dbForProject->deleteDocument('memberships', $membership->getId());
|
||||
} catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception('Failed to remove membership from DB', 500);
|
||||
}
|
||||
|
||||
@@ -782,7 +787,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
|
||||
if ($membership->getAttribute('confirm')) { // Count only confirmed members
|
||||
$team->setAttribute('sum', \max($team->getAttribute('sum', 0) - 1, 0));
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), $team);
|
||||
Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
|
||||
}
|
||||
|
||||
$audits
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Database\Validator\CustomId;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
App::post('/v1/users')
|
||||
->desc('Create User')
|
||||
@@ -289,6 +289,7 @@ App::get('/v1/users/:userId/logs')
|
||||
'account.update.password',
|
||||
'account.update.prefs',
|
||||
'account.sessions.create',
|
||||
'account.sessions.update',
|
||||
'account.sessions.delete',
|
||||
'account.recovery.create',
|
||||
'account.recovery.update',
|
||||
|
||||
+23
-16
@@ -13,15 +13,13 @@ use Utopia\Config\Config;
|
||||
use Utopia\Domains\Domain;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Utopia\Response\Filters\V06;
|
||||
use Appwrite\Utopia\Response\Filters\V07;
|
||||
use Appwrite\Utopia\Response\Filters\V08;
|
||||
use Appwrite\Utopia\Response\Filters\V11;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Appwrite\Utopia\Request\Filters\V12;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
Config::setParam('domainVerification', false);
|
||||
Config::setParam('cookieDomain', 'localhost');
|
||||
@@ -159,15 +157,6 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
||||
$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;
|
||||
case version_compare ($responseFormat , '0.7.2', '<=') :
|
||||
Response::setFilter(new V07());
|
||||
break;
|
||||
case version_compare ($responseFormat , '0.8.0', '<=') :
|
||||
Response::setFilter(new V08());
|
||||
break;
|
||||
case version_compare ($responseFormat , '0.11.0', '<=') :
|
||||
Response::setFilter(new V11());
|
||||
break;
|
||||
@@ -196,7 +185,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
||||
->addHeader('Server', 'Appwrite')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, X-Appwrite-ID, Content-Range, Range, Cache-Control, Expires, Pragma')
|
||||
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Origin', $refDomain)
|
||||
->addHeader('Access-Control-Allow-Credentials', 'true')
|
||||
@@ -314,7 +303,7 @@ App::options(function ($request, $response) {
|
||||
$response
|
||||
->addHeader('Server', 'Appwrite')
|
||||
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, X-Appwrite-ID, Content-Range, Range, Cache-Control, Expires, Pragma, X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Origin', $origin)
|
||||
->addHeader('Access-Control-Allow-Credentials', 'true')
|
||||
@@ -337,8 +326,8 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
||||
if($logger) {
|
||||
if($error->getCode() >= 500 || $error->getCode() === 0) {
|
||||
try {
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$user = $utopia->getResource('user');
|
||||
/** @var Appwrite\Database\Document $user */
|
||||
} catch(\Throwable $th) {
|
||||
// All good, user is optional information for logger
|
||||
}
|
||||
@@ -411,6 +400,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
||||
case 404: // Error allowed publicly
|
||||
case 409: // Error allowed publicly
|
||||
case 412: // Error allowed publicly
|
||||
case 416: // Error allowed publicly
|
||||
case 429: // Error allowed publicly
|
||||
case 501: // Error allowed publicly
|
||||
case 503: // Error allowed publicly
|
||||
@@ -525,8 +515,25 @@ App::get('/.well-known/acme-challenge')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function ($request, $response) {
|
||||
$uriChunks = \explode('/', $request->getURI());
|
||||
$token = $uriChunks[\count($uriChunks) - 1];
|
||||
|
||||
$validator = new Text(100, [
|
||||
...Text::NUMBERS,
|
||||
...Text::ALPHABET_LOWER,
|
||||
...Text::ALPHABET_UPPER,
|
||||
'-',
|
||||
'_'
|
||||
]);
|
||||
|
||||
if (!$validator->isValid($token) || \count($uriChunks) !== 4) {
|
||||
throw new Exception('Invalid challenge token.', 400);
|
||||
}
|
||||
|
||||
$filePath = '/.well-known/acme-challenge' . $token;
|
||||
|
||||
$base = \realpath(APP_STORAGE_CERTIFICATES);
|
||||
$path = \str_replace('/.well-known/acme-challenge/', '', $request->getURI());
|
||||
$path = \str_replace('/.well-known/acme-challenge/', '', $filePath);
|
||||
$absolute = \realpath($base.'/.well-known/acme-challenge/'.$path);
|
||||
|
||||
if (!$base) {
|
||||
|
||||
@@ -10,6 +10,7 @@ use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Storage\Validator\File;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::get('/v1/mock/tests/foo')
|
||||
->desc('Get Foo')
|
||||
@@ -478,11 +479,13 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
||||
->label('docs', false)
|
||||
->label('sdk.mock', true)
|
||||
->param('client_id', '', new Text(100), 'OAuth2 Client ID.')
|
||||
->param('redirect_uri', '', new Host(['localhost']), 'OAuth2 Redirect URI.')
|
||||
->param('client_secret', '', new Text(100), 'OAuth2 scope list.')
|
||||
->param('code', '', new Text(100), 'OAuth2 state.')
|
||||
->param('grant_type', 'authorization_code', new WhiteList(['refresh_token', 'authorization_code']), 'OAuth2 Grant Type.', true)
|
||||
->param('redirect_uri', '', new Host(['localhost']), 'OAuth2 Redirect URI.', true)
|
||||
->param('code', '', new Text(100), 'OAuth2 state.', true)
|
||||
->param('refresh_token', '', new Text(100), 'OAuth2 refresh token.', true)
|
||||
->inject('response')
|
||||
->action(function ($client_id, $redirectURI, $client_secret, $code, $response) {
|
||||
->action(function ($client_id, $client_secret, $grantType, $redirectURI, $code, $refreshToken, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
|
||||
if ($client_id != '1') {
|
||||
@@ -493,11 +496,27 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
||||
throw new Exception('Invalid client secret');
|
||||
}
|
||||
|
||||
if ($code != 'abcdef') {
|
||||
throw new Exception('Invalid token');
|
||||
}
|
||||
$responseJson = [
|
||||
'access_token' => '123456',
|
||||
'refresh_token' => 'tuvwxyz',
|
||||
'expires_in' => 14400
|
||||
];
|
||||
|
||||
$response->json(['access_token' => '123456']);
|
||||
if($grantType === 'authorization_code') {
|
||||
if ($code !== 'abcdef') {
|
||||
throw new Exception('Invalid token');
|
||||
}
|
||||
|
||||
$response->json($responseJson);
|
||||
} else if($grantType === 'refresh_token') {
|
||||
if ($refreshToken !== 'tuvwxyz') {
|
||||
throw new Exception('Invalid refresh token');
|
||||
}
|
||||
|
||||
$response->json($responseJson);
|
||||
} else {
|
||||
throw new Exception('Invalid grant type');
|
||||
}
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/user')
|
||||
@@ -517,7 +536,7 @@ App::get('/v1/mock/tests/general/oauth2/user')
|
||||
$response->json([
|
||||
'id' => 1,
|
||||
'name' => 'User Name',
|
||||
'email' => 'user@localhost.test',
|
||||
'email' => 'useroauth@localhost.test',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Database\Validator\Authorization;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $usage, $deletes, $database, $dbForProject, $mode) {
|
||||
@@ -26,10 +28,6 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
||||
/** @var Appwrite\Event\Event $functions */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
|
||||
Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
|
||||
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId()));
|
||||
Storage::setDevice('builds', new Local(APP_STORAGE_BUILDS.'/app-'.$project->getId()));
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope
|
||||
@@ -219,12 +217,14 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
|
||||
if ($project->getId() !== 'console') {
|
||||
$payload = new Document($response->getPayload());
|
||||
$collection = new Document($events->getParam('collection') ?? []);
|
||||
$bucket = new Document($events->getParam('bucket') ?? []);
|
||||
|
||||
$target = Realtime::fromPayload(
|
||||
event: $events->getParam('event'),
|
||||
payload: $payload,
|
||||
project: $project,
|
||||
collection: $collection
|
||||
collection: $collection,
|
||||
bucket: $bucket,
|
||||
);
|
||||
|
||||
Realtime::send(
|
||||
|
||||
@@ -315,6 +315,36 @@ App::get('/console/storage')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/storage/bucket')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('id', '', new UID(), 'Bucket unique ID.')
|
||||
->inject('response')
|
||||
->inject('layout')
|
||||
->action(function ($id, $response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/storage/bucket.phtml');
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', 0))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Storage Buckets')
|
||||
->setParam('body', $page)
|
||||
;
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Expires', 0)
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/console/users')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
|
||||
+60
-4
@@ -13,6 +13,7 @@ use Utopia\Config\Config;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Swoole\Files;
|
||||
use Appwrite\Utopia\Request;
|
||||
@@ -21,7 +22,7 @@ use Utopia\Logger\Log\User;
|
||||
|
||||
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
|
||||
|
||||
$payloadSize = max(4000000 /* 4mb */, App::getEnv('_APP_STORAGE_LIMIT', 10000000 /* 10mb */));
|
||||
$payloadSize = 6 * (1024 * 1024); // 6MB
|
||||
|
||||
$http
|
||||
->set([
|
||||
@@ -109,10 +110,12 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
}
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if(($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
if(!$dbForConsole->getCollection($key)->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
|
||||
|
||||
$attributes = [];
|
||||
@@ -141,8 +144,61 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
}
|
||||
|
||||
$dbForConsole->createCollection($key, $attributes, $indexes);
|
||||
|
||||
}
|
||||
|
||||
if($dbForConsole->getDocument('buckets', 'default')->isEmpty()) {
|
||||
Console::success('[Setup] - Creating default bucket...');
|
||||
$dbForConsole->createDocument('buckets', new Document([
|
||||
'$id' => 'default',
|
||||
'$collection' => 'buckets',
|
||||
'dateCreated' => \time(),
|
||||
'dateUpdated' => \time(),
|
||||
'name' => 'Default',
|
||||
'permission' => 'file',
|
||||
'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
|
||||
'allowedFileExtensions' => [],
|
||||
'enabled' => true,
|
||||
'encryption' => true,
|
||||
'antivirus' => true,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'search' => 'buckets Default',
|
||||
]));
|
||||
|
||||
Console::success('[Setup] - Creating files collection for default bucket...');
|
||||
$files = $collections['files'] ?? [];
|
||||
if(empty($files)) {
|
||||
throw new Exception('Files collection is not configured.');
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
|
||||
foreach ($files['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
'signed' => $attribute['signed'],
|
||||
'array' => $attribute['array'],
|
||||
'filters' => $attribute['filters'],
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($files['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
'orders' => $index['orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
$dbForConsole->createCollection('bucket_' . 'default', $attributes, $indexes);
|
||||
}
|
||||
|
||||
Console::success('[Setup] - Server database init completed...');
|
||||
});
|
||||
|
||||
@@ -193,8 +249,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
||||
$logger = $app->getResource("logger");
|
||||
if($logger) {
|
||||
try {
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$user = $app->getResource('user');
|
||||
/** @var Appwrite\Database\Document $user */
|
||||
} catch(\Throwable $_th) {
|
||||
// All good, user is optional information for logger
|
||||
}
|
||||
|
||||
+56
-40
@@ -21,7 +21,6 @@ use Appwrite\Extend\PDO;
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Database\Database as DatabaseOld;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\IP;
|
||||
@@ -50,6 +49,10 @@ use Swoole\Database\PDOPool;
|
||||
use Swoole\Database\RedisConfig;
|
||||
use Swoole\Database\RedisPool;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
|
||||
const APP_NAME = 'Appwrite';
|
||||
const APP_DOMAIN = 'appwrite.io';
|
||||
@@ -61,8 +64,12 @@ const APP_MODE_ADMIN = 'admin';
|
||||
const APP_PAGING_LIMIT = 12;
|
||||
const APP_LIMIT_COUNT = 5000;
|
||||
const APP_LIMIT_USERS = 10000;
|
||||
const APP_LIMIT_ANTIVIRUS = 20000000; //20MB
|
||||
const APP_LIMIT_ENCRYPTION = 20000000; //20MB
|
||||
const APP_LIMIT_COMPRESSION = 20000000; //20MB
|
||||
const APP_LIMIT_PREVIEW = 10000000; //10MB file size limit for preview endpoint
|
||||
const APP_CACHE_BUSTER = 201;
|
||||
const APP_VERSION_STABLE = '0.12.1';
|
||||
const APP_VERSION_STABLE = '0.13.0';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
@@ -76,6 +83,7 @@ const APP_STORAGE_BUILDS = '/storage/builds';
|
||||
const APP_STORAGE_CACHE = '/storage/cache';
|
||||
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||
const APP_STORAGE_CONFIG = '/storage/config';
|
||||
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
|
||||
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
|
||||
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
|
||||
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
|
||||
@@ -87,6 +95,9 @@ const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
|
||||
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
||||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||
// Database Worker Types
|
||||
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
|
||||
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
|
||||
@@ -109,6 +120,7 @@ const DELETE_TYPE_ABUSE = 'abuse';
|
||||
const DELETE_TYPE_CERTIFICATES = 'certificates';
|
||||
const DELETE_TYPE_USAGE = 'usage';
|
||||
const DELETE_TYPE_REALTIME = 'realtime';
|
||||
const DELETE_TYPE_BUCKETS = 'buckets';
|
||||
// Mail Types
|
||||
const MAIL_TYPE_VERIFICATION = 'verification';
|
||||
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
|
||||
@@ -119,6 +131,8 @@ const APP_AUTH_TYPE_SESSION = 'Session';
|
||||
const APP_AUTH_TYPE_JWT = 'JWT';
|
||||
const APP_AUTH_TYPE_KEY = 'Key';
|
||||
const APP_AUTH_TYPE_ADMIN = 'Admin';
|
||||
// Response related
|
||||
const MAX_OUTPUT_CHUNK_SIZE = 2*1024*1024; // 2MB
|
||||
|
||||
$register = new Registry();
|
||||
|
||||
@@ -160,43 +174,6 @@ if(!empty($user) || !empty($pass)) {
|
||||
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Old DB Filters
|
||||
*/
|
||||
DatabaseOld::addFilter('json',
|
||||
function($value) {
|
||||
if(!is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return json_encode($value);
|
||||
},
|
||||
function($value) {
|
||||
return json_decode($value, true);
|
||||
}
|
||||
);
|
||||
|
||||
DatabaseOld::addFilter('encrypt',
|
||||
function($value) {
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
|
||||
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||
$tag = null;
|
||||
|
||||
return json_encode([
|
||||
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'iv' => bin2hex($iv),
|
||||
'tag' => bin2hex($tag),
|
||||
'version' => '1',
|
||||
]);
|
||||
},
|
||||
function($value) {
|
||||
$value = json_decode($value, true);
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V'.$value['version']);
|
||||
|
||||
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* New DB Filters
|
||||
*/
|
||||
@@ -493,11 +470,12 @@ $register->set('geodb', function () {
|
||||
});
|
||||
$register->set('db', function () { // This is usually for our workers or CLI commands scope
|
||||
$dbHost = App::getEnv('_APP_DB_HOST', '');
|
||||
$dbPort = App::getEnv('_APP_DB_PORT', '');
|
||||
$dbUser = App::getEnv('_APP_DB_USER', '');
|
||||
$dbPass = App::getEnv('_APP_DB_PASS', '');
|
||||
$dbScheme = App::getEnv('_APP_DB_SCHEMA', '');
|
||||
|
||||
$pdo = new PDO("mysql:host={$dbHost};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
|
||||
$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
|
||||
PDO::ATTR_TIMEOUT => 3, // Seconds
|
||||
PDO::ATTR_PERSISTENT => true,
|
||||
@@ -820,6 +798,44 @@ App::setResource('dbForConsole', function($db, $cache) {
|
||||
return $database;
|
||||
}, ['db', 'cache']);
|
||||
|
||||
|
||||
App::setResource('deviceLocal', function() {
|
||||
return new Local();
|
||||
});
|
||||
|
||||
App::setResource('deviceFiles', function($project) {
|
||||
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('deviceFunctions', function($project) {
|
||||
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('deviceBuilds', function($project) {
|
||||
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
function getDevice($root) {
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_LOCAL:default:
|
||||
return new Local($root);
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_DEVICE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_DEVICE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_DEVICE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_DEVICE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
}
|
||||
}
|
||||
|
||||
App::setResource('mode', function($request) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
|
||||
+26
-6
@@ -91,13 +91,32 @@ $server->error($logError);
|
||||
|
||||
function getDatabase(Registry &$register, string $namespace)
|
||||
{
|
||||
$db = $register->get('dbPool')->get();
|
||||
$redis = $register->get('redisPool')->get();
|
||||
$attempts = 0;
|
||||
|
||||
$cache = new Cache(new RedisCache($redis));
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace($namespace);
|
||||
do {
|
||||
try {
|
||||
$attempts++;
|
||||
|
||||
$db = $register->get('dbPool')->get();
|
||||
$redis = $register->get('redisPool')->get();
|
||||
|
||||
$cache = new Cache(new RedisCache($redis));
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace($namespace);
|
||||
|
||||
if (!$database->exists($database->getDefaultDatabase(), 'realtime')) {
|
||||
throw new Exception('Collection not ready');
|
||||
}
|
||||
break; // leave loop if successful
|
||||
} catch(\Exception $e) {
|
||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) {
|
||||
throw new \Exception('Failed to connect to database: '. $e->getMessage());
|
||||
}
|
||||
sleep(DATABASE_RECONNECT_SLEEP);
|
||||
}
|
||||
} while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS);
|
||||
|
||||
return [
|
||||
$database,
|
||||
@@ -106,6 +125,7 @@ function getDatabase(Registry &$register, string $namespace)
|
||||
$register->get('redisPool')->put($redis);
|
||||
}
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) {
|
||||
|
||||
+22
-82
@@ -1,18 +1,17 @@
|
||||
<?php
|
||||
|
||||
global $cli, $register, $projectDB, $console;
|
||||
global $cli, $register;
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\CLI\Console;
|
||||
use Appwrite\Database\Database;
|
||||
use Appwrite\Database\Validator\Authorization;
|
||||
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
|
||||
use Appwrite\Database\Adapter\Redis as RedisAdapter;
|
||||
use Appwrite\Migration\Migration;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
Config::load('collections.old', __DIR__ . '/../config/collections.old.php');
|
||||
|
||||
$cli
|
||||
->task('migrate')
|
||||
->param('version', APP_VERSION_STABLE, new Text(8), 'Version to migrate to.', true)
|
||||
@@ -23,86 +22,36 @@ $cli
|
||||
Console::exit(1);
|
||||
return;
|
||||
}
|
||||
$options = [];
|
||||
if (str_starts_with($version, '0.12.')) {
|
||||
Console::error('--------------------');
|
||||
Console::error('WARNING');
|
||||
Console::error('--------------------');
|
||||
Console::warning('Migrating to Version 0.12.x introduces a major breaking change within the Database Service!');
|
||||
Console::warning('Before migrating, please read about the breaking changes here:');
|
||||
Console::info('https://dev.to/appwrite/appwrite-012-migration-post-3cha');
|
||||
$confirm = Console::confirm("If you want to proceed, type 'yes':");
|
||||
if ($confirm != 'yes') {
|
||||
Console::exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
Console::log('');
|
||||
Console::log('Collections');
|
||||
Console::log('--------------------');
|
||||
Console::warning('Be aware that following actions will happen during the migration:');
|
||||
Console::warning('- Nested Document rules will be migrated to String attributes');
|
||||
Console::warning('- Numeric rules will be migrated to float attributes');
|
||||
Console::warning('- Wildcard and Markdown rules will be converted to string attributes');
|
||||
Console::info("Do you want to migrate your Database Collections?");
|
||||
$options['migrateCollections'] = Console::confirm("Type 'yes' or 'no':");
|
||||
|
||||
if ($options['migrateCollections'] === 'yes') {
|
||||
Console::log('');
|
||||
Console::log('Documents');
|
||||
Console::log('------------------');
|
||||
Console::warning('Be aware that following actions will happen during the migration:');
|
||||
Console::warning('- Nested Documents will be stored as JSON values');
|
||||
Console::warning('- All Numeric values will be converted to float');
|
||||
Console::warning('- All Wildcard and Markdown values will be converted to string');
|
||||
Console::info("Do you want to migrate your Database Documents?");
|
||||
$options['migrateDocuments'] = Console::confirm("Type 'yes' or 'no':");
|
||||
} else {
|
||||
$options['migrateDocuments'] = 'no';
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
!in_array($options['migrateDocuments'], ['yes', 'no'])
|
||||
|| !in_array($options['migrateCollections'], ['yes', 'no'])
|
||||
) {
|
||||
Console::error("You must reply with 'yes' or 'no'!");
|
||||
Console::exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Config::load('collectionsold', __DIR__ . '/../config/collections.old.php');
|
||||
$app = new App('UTC');
|
||||
|
||||
Console::success('Starting Data Migration to version ' . $version);
|
||||
|
||||
$db = $register->get('db', true);
|
||||
$cache = $register->get('cache', true);
|
||||
$cache->flushAll();
|
||||
|
||||
$consoleDB = new Database();
|
||||
$consoleDB
|
||||
->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache))
|
||||
->setNamespace('app_console') // Main DB
|
||||
->setMocks(Config::getParam('collectionsold', []));
|
||||
$cache = new Cache(new RedisCache($cache));
|
||||
|
||||
$projectDB = new Database();
|
||||
$projectDB
|
||||
->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache))
|
||||
->setMocks(Config::getParam('collectionsold', []));
|
||||
$projectDB = new Database(new MariaDB($db), $cache);
|
||||
$projectDB->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
|
||||
$console = $consoleDB->getDocument('console');
|
||||
$consoleDB = new Database(new MariaDB($db), $cache);
|
||||
$consoleDB->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$consoleDB->setNamespace('_project_console');
|
||||
|
||||
$console = $app->getResource('console');
|
||||
|
||||
$limit = 30;
|
||||
$sum = 30;
|
||||
$offset = 0;
|
||||
$projects = [$console];
|
||||
$count = 0;
|
||||
$totalProjects = $consoleDB->count('projects') + 1;
|
||||
|
||||
$class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version];
|
||||
$migration = new $class($register->get('db'), $register->get('cache'), $options);
|
||||
$migration = new $class();
|
||||
|
||||
while ($sum > 0) {
|
||||
while (!empty($projects)) {
|
||||
foreach ($projects as $project) {
|
||||
try {
|
||||
$migration
|
||||
@@ -114,23 +63,14 @@ $cli
|
||||
}
|
||||
}
|
||||
|
||||
$projects = $consoleDB->getCollection([
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'filters' => [
|
||||
'$collection=' . Database::SYSTEM_COLLECTION_PROJECTS,
|
||||
],
|
||||
]);
|
||||
|
||||
$sum = \count($projects);
|
||||
$projects = $consoleDB->find('projects', limit: $limit, offset: $offset);
|
||||
|
||||
$offset = $offset + $limit;
|
||||
$count = $count + $sum;
|
||||
|
||||
if ($sum > 0) {
|
||||
Console::log('Fetched ' . $count . '/' . $consoleDB->getSum() . ' projects...');
|
||||
}
|
||||
Console::log('Migrated ' . $count . '/' . $totalProjects . ' projects...');
|
||||
}
|
||||
$cache->flushAll();
|
||||
|
||||
Swoole\Event::wait(); // Wait for Coroutines to finish
|
||||
Console::success('Data Migration Completed');
|
||||
|
||||
+150
-14
@@ -36,7 +36,15 @@ use Utopia\Database\Validator\Authorization;
|
||||
* database.collections.{collectionId}.documents.delete
|
||||
*
|
||||
* Storage
|
||||
*
|
||||
*
|
||||
* storage.buckets.create
|
||||
* storage.buckets.read
|
||||
* storage.buckets.update
|
||||
* storage.buckets.delete
|
||||
* storage.files.create
|
||||
* storage.files.read
|
||||
* storage.files.update
|
||||
* storage.files.delete
|
||||
* storage.buckets.{bucketId}.files.create
|
||||
* storage.buckets.{bucketId}.files.read
|
||||
* storage.buckets.{bucketId}.files.update
|
||||
@@ -61,7 +69,9 @@ use Utopia\Database\Validator\Authorization;
|
||||
* Counters
|
||||
*
|
||||
* users.count
|
||||
* storage.buckets.count
|
||||
* storage.files.count
|
||||
* storage.buckets.{bucketId}.files.count
|
||||
* database.collections.count
|
||||
* database.documents.count
|
||||
* database.collections.{collectionId}.documents.count
|
||||
@@ -142,6 +152,30 @@ $cli
|
||||
'table' => 'appwrite_usage_database_documents_delete',
|
||||
'groupBy' => 'collectionId',
|
||||
],
|
||||
'storage.buckets.create' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_create',
|
||||
],
|
||||
'storage.buckets.read' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_read',
|
||||
],
|
||||
'storage.buckets.update' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_update',
|
||||
],
|
||||
'storage.buckets.delete' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_delete',
|
||||
],
|
||||
'storage.files.create' => [
|
||||
'table' => 'appwrite_usage_storage_files_create',
|
||||
],
|
||||
'storage.files.read' => [
|
||||
'table' => 'appwrite_usage_storage_files_read',
|
||||
],
|
||||
'storage.files.update' => [
|
||||
'table' => 'appwrite_usage_storage_files_update',
|
||||
],
|
||||
'storage.files.delete' => [
|
||||
'table' => 'appwrite_usage_storage_files_delete',
|
||||
],
|
||||
'storage.buckets.bucketId.files.create' => [
|
||||
'table' => 'appwrite_usage_storage_files_create',
|
||||
'groupBy' => 'bucketId',
|
||||
@@ -370,17 +404,17 @@ $cli
|
||||
|
||||
// Get total storage
|
||||
$dbForProject->setNamespace('_project_' . $projectId);
|
||||
$storageTotal = $dbForProject->sum('files', 'sizeOriginal') + $dbForProject->sum('deployments', 'size');
|
||||
$storageTotal = $dbForProject->sum('deployments', 'size');
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_storage.total'); //Construct unique id for each metric using time, period and metric
|
||||
$id = \md5($time . '_30m_storage.tags.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => '30m',
|
||||
'time' => $time,
|
||||
'metric' => 'storage.total',
|
||||
'metric' => 'storage.tags.total',
|
||||
'value' => $storageTotal,
|
||||
'type' => 1,
|
||||
]));
|
||||
@@ -393,14 +427,14 @@ $cli
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_storage.total'); //Construct unique id for each metric using time, period and metric
|
||||
$id = \md5($time . '_1d_storage.tags.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => '1d',
|
||||
'time' => $time,
|
||||
'metric' => 'storage.total',
|
||||
'metric' => 'storage.tags.total',
|
||||
'value' => $storageTotal,
|
||||
'type' => 1,
|
||||
]));
|
||||
@@ -414,21 +448,30 @@ $cli
|
||||
|
||||
$collections = [
|
||||
'users' => [
|
||||
'namespace' => 'internal',
|
||||
'namespace' => '',
|
||||
],
|
||||
'collections' => [
|
||||
'metricPrefix' => 'database',
|
||||
'namespace' => 'internal',
|
||||
'namespace' => '',
|
||||
'subCollections' => [ // Some collections, like collections and later buckets have child collections that need counting
|
||||
'documents' => [
|
||||
'namespace' => 'external',
|
||||
'namespace' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
'files' => [
|
||||
'buckets' => [
|
||||
'metricPrefix' => 'storage',
|
||||
'namespace' => 'internal',
|
||||
],
|
||||
'namespace' => '',
|
||||
'subCollections' => [
|
||||
'files' => [
|
||||
'namespace' => '',
|
||||
'collectionPrefix' => 'bucket_',
|
||||
'sum' => [
|
||||
'field' => 'sizeOriginal'
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($collections as $collection => $options) {
|
||||
@@ -486,6 +529,7 @@ $cli
|
||||
|
||||
$latestParent = null;
|
||||
$subCollectionCounts = []; //total project level count of sub collections
|
||||
$subCollectionTotals = []; //total project level sum of sub collections
|
||||
|
||||
do { // Loop over all the parent collection document for each sub collection
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
@@ -500,7 +544,7 @@ $cli
|
||||
foreach ($parents as $parent) {
|
||||
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$count = $dbForProject->count($parent->getId());
|
||||
$count = $dbForProject->count(($subOptions['collectionPrefix'] ?? '') . $parent->getId());
|
||||
|
||||
$subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count
|
||||
|
||||
@@ -546,6 +590,55 @@ $cli
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
// check if sum calculation is required
|
||||
$sum = $subOptions['sum'] ?? [];
|
||||
if(empty($sum)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$total = (int) $dbForProject->sum(($subOptions['collectionPrefix'] ?? '') . $parent->getId(), $sum['field']);
|
||||
|
||||
$subCollectionTotals[$subCollection] = ($ssubCollectionTotals[$subCollection] ?? 0) + $total; // Project level sum for sub collections like storage.total
|
||||
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.total" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.total";
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $total,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $total));
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $total,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $total));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} while (!empty($parents));
|
||||
@@ -598,7 +691,50 @@ $cli
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
|
||||
/**
|
||||
* Inserting project level sums for sub collections like storage.total
|
||||
*/
|
||||
foreach ($subCollectionTotals as $subCollection => $count) {
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$subCollection}.total" : "{$metricPrefix}.{$subCollection}.total";
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '30m',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'time' => $time,
|
||||
'period' => '1d',
|
||||
'metric' => $metric,
|
||||
'value' => $count,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
}
|
||||
}
|
||||
} catch (\Exception$e) {
|
||||
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,15 +527,6 @@ $logs = $this->getParam('logs', null);
|
||||
|
||||
<hr class="margin-top-small" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="document" type="radio" class="margin-top-no" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Document Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and collection permissions are ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="collection" type="radio" class="margin-top-tiny" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
@@ -554,6 +545,15 @@ $logs = $this->getParam('logs', null);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="document" type="radio" class="margin-top-no" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Document Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and collection permissions are ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="margin-top-no" />
|
||||
|
||||
<button>Update</button>
|
||||
@@ -880,10 +880,10 @@ $logs = $this->getParam('logs', null);
|
||||
|
||||
<div class="margin-bottom-large">
|
||||
<template x-if="!(array || required)">
|
||||
<input name="xdefault" class="button switch" type="checkbox" />
|
||||
<input name="xdefault" class="button switch" type="checkbox" />
|
||||
</template>
|
||||
<template x-if="(array || required)">
|
||||
<input name="xdefault" class="button switch" type="checkbox" disabled />
|
||||
<input name="" class="button switch" type="checkbox" disabled />
|
||||
</template>
|
||||
Default Value <span class="tooltip" data-tooltip="Whether this attribute is set to true or false on creation"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
@@ -169,8 +169,11 @@ $logs = $this->getParam('logs', null);
|
||||
</template>
|
||||
<template x-if="attr.format === 'enum'">
|
||||
<select
|
||||
:required="attr.required"
|
||||
:name="attr.key"
|
||||
data-cast-to="string">
|
||||
<option :disabled="attr.required" selected label=" "></option>
|
||||
|
||||
<template x-for="element in attr.elements">
|
||||
<option
|
||||
:value="element"
|
||||
@@ -261,9 +264,12 @@ $logs = $this->getParam('logs', null);
|
||||
</template>
|
||||
<template x-if="attr.format === 'enum'">
|
||||
<select
|
||||
:required="attr.required"
|
||||
:name="attr.key"
|
||||
data-cast-to="string">
|
||||
<template x-for="element in attr.elements">
|
||||
<option :disabled="attr.required" selected label=" "></option>
|
||||
|
||||
<option
|
||||
:value="element"
|
||||
x-text="element"
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
<label for="collection-name">Name</label>
|
||||
<input type="text" class="full-width" id="collection-name" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
<input type="hidden" id="collection-permission" name="permission" required value="document" />
|
||||
<input type="hidden" id="collection-permission" name="permission" required value="collection" />
|
||||
<input type="hidden" id="collection-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="collection-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="margin-top-small"><i class="icon-info-circled"></i> Data is aggregated and updated every 15 minutes</p>
|
||||
<p class="margin-top-small text-size-small"><i class="icon-info-circled"></i> Data is aggregated and updated every 15 minutes</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,497 @@
|
||||
<?php
|
||||
$home = $this->getParam('home', '');
|
||||
$fileLimit = $this->getParam('fileLimit', 0);
|
||||
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
||||
?>
|
||||
|
||||
<div
|
||||
data-service="storage.getBucket"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-scope="sdk"
|
||||
data-event="load,storage.updateBucket"
|
||||
data-name="project-bucket">
|
||||
|
||||
<div class="cover">
|
||||
<h1 class="zone xl margin-bottom-large">
|
||||
<a data-ls-attrs="href=/console/storage?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Storage</a>
|
||||
|
||||
<br />
|
||||
|
||||
<span data-ls-bind="{{project-bucket.name}}"> </span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h2>JSON View</h2>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input type="hidden" data-ls-bind="{{project-bucket}}" data-forms-code />
|
||||
</div>
|
||||
|
||||
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</div>
|
||||
|
||||
<div class="zone xl">
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/storage/bucket?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<h2 class="margin-bottom">Files</h2>
|
||||
|
||||
<form class="box padding-small margin-bottom search"
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset=""
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<div class="row thin responsive">
|
||||
<div class="col span-10">
|
||||
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
|
||||
</div>
|
||||
<div class="col span-2 desktops-only">
|
||||
<button class="fill" title="Search" aria-label="Search">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div
|
||||
data-service="storage.listFiles"
|
||||
data-event="load,storage.createFile,storage.updateFile,storage.deleteFile"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files">
|
||||
|
||||
<div data-ls-if="0 == {{project-files.sum}}" class="box margin-bottom">
|
||||
<h3 class="margin-bottom-small text-bold">No Files Found</h3>
|
||||
|
||||
<p class="margin-bottom-no">Upload your first file to get started</p>
|
||||
</div>
|
||||
|
||||
<div data-ls-if="0 != {{project-files.sum}}">
|
||||
<div class="margin-bottom-small text-align-end text-size-small text-fade"><span data-ls-bind="{{project-files.sum}}"></span> files found</div>
|
||||
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40"></th>
|
||||
<th>Filename</th>
|
||||
<th width="140">Type</th>
|
||||
<th width="100">Size</th>
|
||||
<th width="120">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="project-files.files" data-ls-as="file">
|
||||
<tr>
|
||||
<td class="hide">
|
||||
<img src="" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" />
|
||||
</td>
|
||||
<td data-title="Name: " class="text-one-liner" data-ls-attrs="title={{file.name}}" >
|
||||
<div data-ui-modal class="box modal sticky-footer width-large close" data-button-text="{{file.name}}" data-button-class="link" data-button-element="span">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Update File</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="row responsive modalize">
|
||||
<div class="col span-8">
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Update Storage File"
|
||||
data-service="storage.updateFile"
|
||||
data-event="file-update-{{file.$id}}"
|
||||
data-scope="sdk"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="File updated successfully"
|
||||
data-success-param-trigger-events="storage.updateFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<label for="files-fileId">File ID</label>
|
||||
<div class="input-copy">
|
||||
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
|
||||
</div>
|
||||
<input type="hidden" data-ls-attrs="id=file-bucketId-{{file.$id}}" name="bucketId" data-ls-bind="{{file.bucketId}}">
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</form>
|
||||
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Delete File"
|
||||
data-service="storage.deleteFile"
|
||||
data-scope="sdk"
|
||||
data-event="file-delete-{{file.$id}}"
|
||||
data-confirm="Are you sure you want to delete this file?"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Deleted file successfully"
|
||||
data-success-param-trigger-events="storage.deleteFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to delete file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<input type="hidden" name="bucketId" data-ls-bind="{{file.bucketId}}" />
|
||||
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
|
||||
</form>
|
||||
</div>
|
||||
<div data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" class="col span-4 text-size-small">
|
||||
<div class="margin-bottom-small">File Preview</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<img src="" class="file-preview" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="225" height="160" />
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Type: <span data-ls-bind="{{file.mimeType}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Size: <span data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
|
||||
<button type="button" data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="Type: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.mimeType}}"></span>
|
||||
</td>
|
||||
<td data-title="Size: ">
|
||||
<div data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" >
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</div>
|
||||
<span class="text-fade text-size-small" data-ls-if="{{file.chunksTotal}} != {{file.chunksUploaded}}">incomplete</span>
|
||||
</td>
|
||||
<td data-title="Created: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-files.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="box modal sticky-footer close" data-button-text="Add File">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Upload File</h1>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Create Storage File"
|
||||
data-service="storage.createFile"
|
||||
data-event="submit"
|
||||
data-scope="sdk"
|
||||
data-loading="Uploading File..."
|
||||
data-success="alert,trigger,reset"
|
||||
data-success-param-alert-text="File uploaded successfully"
|
||||
data-success-param-trigger-events="storage.createFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to upload file"
|
||||
data-failure-param-alert-classname="error">
|
||||
<input type="hidden" name="bucketId" id="files-bucketId" data-ls-bind="{{router.params.id}}">
|
||||
|
||||
<label for="fileId">File ID</label>
|
||||
<input
|
||||
type="hidden"
|
||||
data-custom-id
|
||||
data-id-type="auto"
|
||||
data-validator="storage.getFile"
|
||||
required
|
||||
maxlength="36"
|
||||
name="fileId"
|
||||
id="fileId" />
|
||||
|
||||
<label for="file-read">File</label>
|
||||
<input type="file" name="file" id="file-file" size="1" required>
|
||||
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-read" name="read" data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['role:all'])); ?>" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-write" name="write" data-forms-tags data-cast-to="json" value="" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</footer>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/storage/bucket/usage?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="storage.getBucketUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="storage.getBucketUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-bucket-id="{{router.params.id}}">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="storage.getBucketUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<div data-service="storage.getBucketUsage" data-event="load" data-name="usage" data-param-bucket-id="{{router.params.id}}">
|
||||
<h3 class="margin-bottom-tiny">Files</h3>
|
||||
<p class="text-fade">Count of files over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-show-y-axis="true" data-forms-chart="Files=filesCount" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Files </li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Operations</h3>
|
||||
<p class="text-fade">Count of files create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-show-y-axis="true" data-forms-chart="Create=filesCreate,Read=filesRead,Updated=filesUpdate,Deleted=filesDelete" data-colors="create,read,update,delete" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes crud margin-bottom-large">
|
||||
<li>Create</li>
|
||||
<li>Read</li>
|
||||
<li>Update</li>
|
||||
<li>Delete</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/storage/bucket/settings?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<h2>Settings</h2>
|
||||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Update Storage Bucket"
|
||||
data-service="storage.updateBucket"
|
||||
data-scope="sdk"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Updated bucket successfully"
|
||||
data-success-param-trigger-events="storage.updateBucket"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update bucket"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<label> </label>
|
||||
|
||||
<div class="box">
|
||||
<label for="bucket-name">Name</label>
|
||||
<input name="name" id="bucket-name" type="text" autocomplete="off" data-ls-bind="{{project-bucket.name}}" data-forms-text-direction required placeholder="Bucket Name" maxlength="128" />
|
||||
|
||||
<label for="bucket-maximum-file-size">Maximum File Size (bytes) <span class="tooltip small" data-tooltip="Limit file size allowed in the bucket."><i class="icon-info-circled"></i></span></label>
|
||||
<input name="maximumFileSize" id="bucket-maximum-file-size" type="number" autocomplete="off" data-ls-bind="{{project-bucket.maximumFileSize}}" min="1" data-cast-to="integer" />
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input name="enabled" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.enabled}}" /> Enabled <span class="tooltip" data-tooltip="Mark whether bucket is enabled"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input name="encryption" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.encryption}}" /> Encryption <span class="tooltip" data-tooltip="Mark whether bucket is encrypted"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input name="antivirus" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.antivirus}}" /> Anti Virus <span class="tooltip" data-tooltip="Mark whether anti virus scanning is enabled"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<label for="bucket-allowedFileExtensions">Allowed File Extensions</label>
|
||||
<input type="hidden" id="bucket-allowedFileExtensions" name="allowedFileExtensions" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.allowedFileExtensions}}" placeholder="Allowed file extensions (pdf, mp4)" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Leave empty to allow all.</div>
|
||||
|
||||
|
||||
<label class="margin-bottom-small">Permissions</label>
|
||||
|
||||
<p class="text-fade text-size-small">Choose the permissions model for this bucket.</p>
|
||||
|
||||
<hr class="margin-top-small" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="bucket" type="radio" class="margin-top-tiny" data-ls-bind="{{project-bucket.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Bucket Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Bucket Level permissions, you assign permissions only once in the bucket.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level permissions assigned to bucket takes the precedence and file permissions are ignored</p>
|
||||
<div data-ls-if="{{project-bucket.permission}} == 'bucket'">
|
||||
|
||||
<label for="bucket-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="bucket-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$permissions.read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="bucket-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="bucket-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$permissions.write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="file" type="radio" class="margin-top-no" data-ls-bind="{{project-bucket.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>File Level</b>
|
||||
<p class="text-fade margin-top-tiny">With File Level permissions, you have granular access control over every file. Users will only be able to access files for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level file permissions take precedence and bucket permissions are ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="margin-top-no" />
|
||||
|
||||
<button>Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col span-4 sticky-top">
|
||||
<label>Bucket ID</label>
|
||||
<div class="input-copy margin-bottom">
|
||||
<input id="id" type="text" autocomplete="off" placeholder="" data-ls-bind="{{project-bucket.$id}}" disabled data-forms-copy class="margin-bottom-no" />
|
||||
</div>
|
||||
|
||||
<ul class="margin-bottom-large text-fade text-size-small">
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-bucket.dateUpdated|dateText}}"></span></li>
|
||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-bucket.dateCreated|dateText}}"></span></li>
|
||||
</ul>
|
||||
|
||||
<form name="storage.deleteBucket" class="margin-bottom"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Delete Storage Bucket"
|
||||
data-service="storage.deleteBucket"
|
||||
data-event="submit"
|
||||
data-param-bucket-id="{{router.params.id}}"
|
||||
data-confirm="Are you sure you want to delete this bucket?"
|
||||
data-success="alert,trigger,redirect"
|
||||
data-success-param-alert-text="Bucket deleted successfully"
|
||||
data-success-param-trigger-events="storage.deleteBucket"
|
||||
data-success-param-redirect-url="/console/storage?project={{router.params.project}}"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to delete bucket"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<button type="submit" class="danger fill">Delete Bucket</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
<?php
|
||||
$home = $this->getParam('home', '');
|
||||
$fileLimit = $this->getParam('fileLimit', 0);
|
||||
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
||||
?>
|
||||
<div class="cover">
|
||||
<h1 class="zone xl margin-bottom-large">
|
||||
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
|
||||
@@ -15,320 +10,204 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
||||
<div class="zone xl">
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/storage?project={{router.params.project}}">
|
||||
<h2 class="margin-bottom">Files</h2>
|
||||
<h2>Buckets</h2>
|
||||
|
||||
<form class="box padding-small margin-bottom search"
|
||||
data-service="storage.listFiles"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset=""
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<div class="row thin responsive">
|
||||
<div class="col span-10">
|
||||
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
|
||||
</div>
|
||||
<div class="col span-2 desktops-only">
|
||||
<button class="fill" title="Search" aria-label="Search">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div
|
||||
data-service="storage.listFiles"
|
||||
data-event="load,storage.createFile,storage.updateFile,storage.deleteFile"
|
||||
<div class="margin-top"
|
||||
data-service="storage.listBuckets"
|
||||
data-event="load,storage.createBucket,storage.updateBucket,storage.deleteBucket"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files">
|
||||
data-name="project-buckets">
|
||||
|
||||
<div data-ls-if="0 == {{project-files.sum}}" class="box margin-bottom">
|
||||
<h3 class="margin-bottom-small text-bold">No Files Found</h3>
|
||||
<div data-ls-if="(!{{project-buckets.sum}})" class="box dashboard margin-bottom">
|
||||
<div class="margin-bottom-small margin-top-small margin-end margin-start">
|
||||
<h3 class="margin-bottom-small text-bold">No Buckets Found</h3>
|
||||
|
||||
<p class="margin-bottom-no">Upload your first file to get started</p>
|
||||
<p class="margin-bottom-no">You haven't created any buckets for your project yet.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-ls-if="0 != {{project-files.sum}}">
|
||||
<div class="margin-bottom-small text-align-end text-size-small text-fade"><span data-ls-bind="{{project-files.sum}}"></span> files found</div>
|
||||
<div data-ls-if="0 != {{project-buckets.sum}}">
|
||||
<ul data-ls-loop="project-buckets.buckets" data-ls-as="bucket" data-ls-append="" class="tiles cell-3 margin-bottom-small">
|
||||
<li class="margin-bottom">
|
||||
<a data-ls-attrs="href=/console/storage/bucket?id={{bucket.$id}}&project={{router.params.project}}" class="box">
|
||||
<div data-ls-bind="{{bucket.name}}" class="text-one-liner margin-bottom text-bold"> </div>
|
||||
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40"></th>
|
||||
<th>Filename</th>
|
||||
<th width="140">Type</th>
|
||||
<th width="100">Size</th>
|
||||
<th width="120">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="project-files.files" data-ls-as="file">
|
||||
<tr>
|
||||
<td class="hide">
|
||||
<img src="" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" />
|
||||
</td>
|
||||
<td data-title="Name: " class="text-one-liner" data-ls-attrs="title={{file.name}}" >
|
||||
<div data-ui-modal class="box modal sticky-footer width-large close" data-button-text="{{file.name}}" data-button-class="link" data-button-element="span">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Update File</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="row responsive modalize">
|
||||
<div class="col span-8">
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Update Storage File"
|
||||
data-service="storage.updateFile"
|
||||
data-event="file-update-{{file.$id}}"
|
||||
data-scope="sdk"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="File updated successfully"
|
||||
data-success-param-trigger-events="storage.updateFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<label for="files-fileId">File ID</label>
|
||||
<div class="input-copy">
|
||||
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
|
||||
</div>
|
||||
<input type="hidden" data-ls-attrs="id=file-folderId-{{file.$id}}" name="folderId" data-cast-to="integer" value="1">
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</form>
|
||||
|
||||
<form class="strip"
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Delete File"
|
||||
data-service="storage.deleteFile"
|
||||
data-scope="sdk"
|
||||
data-event="file-delete-{{file.$id}}"
|
||||
data-confirm="Are you sure you want to delete this file?"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Deleted file successfully"
|
||||
data-success-param-trigger-events="storage.deleteFile"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to delete file"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="col span-4 text-size-small">
|
||||
<div class="margin-bottom-small">File Preview</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<img src="" class="file-preview" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="225" height="160" />
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-small">
|
||||
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Type: <span data-ls-bind="{{file.mimeType}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom-tiny">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Size: <span data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</div>
|
||||
<div class="margin-bottom">
|
||||
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
|
||||
<button type="button" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="Type: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.mimeType}}"></span>
|
||||
</td>
|
||||
<td data-title="Size: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
|
||||
</td>
|
||||
<td data-title="Created: ">
|
||||
<span class="text-fade text-size-small" data-ls-bind="{{file.dateCreated|dateText}}"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<i class="icon-right-open"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-service="storage.listBuckets"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-name="project-buckets"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-buckets.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-files.sum|pageTotal}}"></span>
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{project-buckets.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="storage.listFiles"
|
||||
data-service="storage.listBuckets"
|
||||
data-event="submit"
|
||||
data-param-search="{{router.params.search}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-files"
|
||||
data-name="project-buckets"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-files.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-buckets.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div data-ui-modal class="box modal sticky-footer close" data-button-text="Add File">
|
||||
<div data-ui-modal class="modal close box sticky-footer" data-button-text="Add Bucket">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Upload File</h1>
|
||||
<h1>New Bucket</h1>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
data-analytics-label="Create Storage File"
|
||||
data-service="storage.createFile"
|
||||
data-analytics-label="Create Storage Bucket"
|
||||
data-service="storage.createBucket"
|
||||
data-event="submit"
|
||||
data-scope="sdk"
|
||||
data-loading="Uploading File..."
|
||||
data-success="alert,trigger,reset"
|
||||
data-success-param-alert-text="File uploaded successfully"
|
||||
data-success-param-trigger-events="storage.createFile"
|
||||
data-success="alert,reset,redirect,trigger"
|
||||
data-success-param-alert-text="Bucket created successfully"
|
||||
data-success-param-redirect-url="/console/storage/bucket/settings?id={{serviceData.$id}}&project={{router.params.project}}"
|
||||
data-success-param-trigger-events="storage.createBucket"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to upload file"
|
||||
data-failure-param-alert-text="Failed to create bucket"
|
||||
data-failure-param-alert-classname="error">
|
||||
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="integer" value="1">
|
||||
|
||||
<label for="fileId">File ID</label>
|
||||
<label for="bucket-id">Bucket ID</label>
|
||||
<input
|
||||
type="hidden"
|
||||
data-custom-id
|
||||
data-id-type="auto"
|
||||
data-validator="storage.getFile"
|
||||
data-validator="storage.getBucket"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9._-]{1,36}$"
|
||||
name="fileId"
|
||||
id="fileId" />
|
||||
name="bucketId"
|
||||
id="bucketId" />
|
||||
|
||||
<label for="file-read">File</label>
|
||||
<input type="file" name="file" id="file-file" size="1" required>
|
||||
<label for="bucket-name">Name</label>
|
||||
<input type="text" class="full-width" id="bucket-name" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
|
||||
<input type="hidden" id="bucket-permission" name="permission" required value="bucket" />
|
||||
<input type="hidden" id="bucket-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="bucket-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
|
||||
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-read" name="read" data-forms-tags data-cast-to="json" value="<?php echo htmlentities(json_encode(['role:all'])); ?>" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
<hr />
|
||||
|
||||
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="file-write" name="write" data-forms-tags data-cast-to="json" value="" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/storage/usage?project={{router.params.project}}">
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
|
||||
</li>
|
||||
|
||||
<li data-state="/console/storage/usage?project={{router.params.project}}">
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h2>Usage</h2>
|
||||
|
||||
<div
|
||||
data-service="storage.getUsage"
|
||||
data-event="load"
|
||||
data-name="usage">
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Total Files=files" data-height="140" data-show-y-axis="true" />
|
||||
<div
|
||||
data-service="storage.getUsage"
|
||||
data-event="load"
|
||||
data-name="usage">
|
||||
<h3 class="margin-bottom-tiny">Objects</h3>
|
||||
<p class="text-fade">Count of buckets, files and total storage used over time</p>
|
||||
<div class="box">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-show-y-axis="true" data-forms-chart="Total Files=filesCount,Total Buckets=bucketsCount" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Files <span data-ls-bind="({{usage.files|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Total Storage=storage" data-height="140" data-show-y-axis="true" />
|
||||
<ul class="chart-notes margin-top-small margin-bottom-large">
|
||||
<li>Total Files</li>
|
||||
<li>Total Buckets</li>
|
||||
<!-- <li>Total Storage <span data-ls-bind="({{usage.filesStorage|statsGetLast|statsTotal}})"></span></li> -->
|
||||
</ul>
|
||||
|
||||
<!-- CRUDS class for graph fixed colors, use color codes from Docs for CRUD operations -->
|
||||
<h3 class="margin-bottom-tiny">Buckets</h3>
|
||||
<p class="text-fade">Count of bucket create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no crud margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Buckets create=bucketsCreate,Buckets read=bucketsRead,Buckets update=bucketsUpdate,Buckets delete=bucketsDelete" data-show-y-axis="true" data-colors="create,read,update,delete" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Storage (<span data-ls-bind="{{usage.storage|statsGetLast|humanFileSize}}"></span> <span data-ls-bind="{{usage.storage|statsGetLast|humanFileUnit}}"></span>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="chart-notes crud margin-bottom-large">
|
||||
<li class="green">Create</li>
|
||||
<li class="green">Read</li>
|
||||
<li class="green">Update</li>
|
||||
<li class="green">Delete</li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Files</h3>
|
||||
<p class="text-fade">Count of file create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart background-image-no border-no crud margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Files create=filesCreate,Files read=filesRead,Files update=filesUpdate,Files delete=filesDelete" data-show-y-axis="true" data-colors="create,read,update,delete" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes crud margin-bottom-large">
|
||||
<li class="green">Create</li>
|
||||
<li class="green">Read</li>
|
||||
<li class="green">Update</li>
|
||||
<li class="green">Delete</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
use Appwrite\Utopia\View;
|
||||
|
||||
$providers = $this->getParam('providers', []);
|
||||
$auth = $this->getParam('auth', []);
|
||||
$smtpEnabled = $this->getParam('smtpEnabled', false);
|
||||
@@ -475,9 +477,12 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret">App Secret</label>
|
||||
<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.provider<?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.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="com.company.appname" />
|
||||
<input name="secret" data-forms-oauth-apple id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
||||
<?php
|
||||
$form = new View(__DIR__.'/oauth/'.$this->escape($form));
|
||||
echo $form
|
||||
->setParam("provider", $provider)
|
||||
->render();
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="info row thin margin-bottom margin-top">
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
$provider = $this->getParam('provider', '');
|
||||
?>
|
||||
|
||||
<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.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="com.company.appname" />
|
||||
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
||||
<div>
|
||||
<div class="row thin">
|
||||
<div class="col span-6"><label>Key ID</label><input id="oauth2AppleKeyId" type="text" placeholder="SHAB13ROFN"></div>
|
||||
<div class="col span-6"><label>Team ID</label><input id="oauth2AppleTeamId" type="text" placeholder="ELA2CD3AED"></div>
|
||||
</div><label>P8 File</label><textarea id="oauth2AppleP8" class="margin-bottom-no"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
$provider = $this->getParam('provider', '');
|
||||
?>
|
||||
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">Application (Client) ID<span class="tooltip" data-tooltip="Provided by AzureAD"><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.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="Application ID" />
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret">Client Secret <span class="tooltip" data-tooltip="Created by you in AzureAD Portal"><i class="icon-info-circled"></i></span></label>
|
||||
<input name="appSecret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret" type="password" autocomplete="off" placeholder="Client Secret" />
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>TenantId">Target Tenant<span class="tooltip" data-tooltip="'common', 'organizations', 'consumers' or your TenantId"><i class="icon-info-circled"></i></span><a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints">More info</a></label>
|
||||
<input name="appSecret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>TenantId" type="text" autocomplete="off" placeholder="'common', 'organizations', 'consumers' or your TenantId" />
|
||||
<?php /*Hidden input for the final secret. Gets filled with a JSON via JS. */ ?>
|
||||
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
||||
@@ -475,6 +475,10 @@ services:
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
|
||||
appwrite-schedule:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
@@ -7,6 +8,9 @@ use Utopia\Database\Validator\Authorization;
|
||||
use Appwrite\Resque\Worker;
|
||||
use Executor\Executor;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\App;
|
||||
@@ -63,6 +67,9 @@ class DeletesV1 extends Worker
|
||||
case DELETE_TYPE_TEAMS:
|
||||
$this->deleteMemberships($document, $projectId);
|
||||
break;
|
||||
case DELETE_TYPE_BUCKETS:
|
||||
$this->deleteBucket($document, $projectId);
|
||||
break;
|
||||
default:
|
||||
Console::error('No lazy delete operation available for document of type: ' . $document->getCollection());
|
||||
break;
|
||||
@@ -532,4 +539,34 @@ class DeletesV1 extends Worker
|
||||
Console::info("No certificate files found for {$domain}");
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteBucket(Document $document, string $projectId)
|
||||
{
|
||||
$bucketId = $document->getId();
|
||||
$dbForProject = $this->getProjectDB($projectId);
|
||||
$dbForProject->deleteCollection('bucket_' . $bucketId);
|
||||
|
||||
$device = new Local(APP_STORAGE_UPLOADS.'/app-'.$projectId);
|
||||
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_DEVICE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_DEVICE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_DEVICE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_DEVICE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
$device = new S3(APP_STORAGE_UPLOADS . '/app-' . $projectId, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
break;
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
$device = new DOSpaces(APP_STORAGE_UPLOADS . '/app-' . $projectId, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
break;
|
||||
}
|
||||
|
||||
$device->deletePath($bucketId);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-8
@@ -21,12 +21,6 @@
|
||||
"Appwrite\\Tests\\": "tests/extensions"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"type": "git"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.0.0",
|
||||
"ext-curl": "*",
|
||||
@@ -57,7 +51,7 @@
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/domains": "1.1.*",
|
||||
"utopia-php/swoole": "0.3.*",
|
||||
"utopia-php/storage": "0.5.*",
|
||||
"utopia-php/storage": "0.7.*",
|
||||
"utopia-php/websocket": "0.1.0",
|
||||
"utopia-php/image": "0.5.*",
|
||||
"utopia-php/orchestration": "0.3.*",
|
||||
@@ -70,8 +64,18 @@
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"slickdeals/statsd": "3.1.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"type": "git"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"appwrite/sdk-generator": "0.17.1",
|
||||
"appwrite/sdk-generator": "dev-feat-preps-for-0.13",
|
||||
"phpunit/phpunit": "9.5.10",
|
||||
"swoole/ide-helper": "4.8.5",
|
||||
"textalk/websocket": "1.5.5",
|
||||
|
||||
Generated
+100
-185
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1a5d84f96eb76e59f7ad0ff7bcd4a8d8",
|
||||
"content-hash": "be98fbe479fad1146fb54411ebfc9a57",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -119,7 +119,7 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"reference": "991a8674c75f78644b557c9723869c370b3290b5"
|
||||
"reference": "85053974a0d17415c0bd955ac8d6758c0d186d2b"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
@@ -160,7 +160,7 @@
|
||||
"php",
|
||||
"runtimes"
|
||||
],
|
||||
"time": "2022-02-01T10:58:43+00:00"
|
||||
"time": "2022-02-09T12:31:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "chillerlan/php-qrcode",
|
||||
@@ -525,12 +525,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -1027,12 +1027,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MongoDB\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"MongoDB\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -1760,12 +1760,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php80\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php80\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
@@ -2252,16 +2252,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/framework",
|
||||
"version": "0.19.5",
|
||||
"version": "0.19.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/framework.git",
|
||||
"reference": "1c28ba9a5b491cf7c90c535fefee5832c7133623"
|
||||
"reference": "7d9b28365fb794001cb34dd028659452d4e71b7d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/framework/zipball/1c28ba9a5b491cf7c90c535fefee5832c7133623",
|
||||
"reference": "1c28ba9a5b491cf7c90c535fefee5832c7133623",
|
||||
"url": "https://api.github.com/repos/utopia-php/framework/zipball/7d9b28365fb794001cb34dd028659452d4e71b7d",
|
||||
"reference": "7d9b28365fb794001cb34dd028659452d4e71b7d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2295,9 +2295,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/framework/issues",
|
||||
"source": "https://github.com/utopia-php/framework/tree/0.19.5"
|
||||
"source": "https://github.com/utopia-php/framework/tree/0.19.6"
|
||||
},
|
||||
"time": "2022-01-04T14:40:23+00:00"
|
||||
"time": "2022-02-10T17:05:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/image",
|
||||
@@ -2630,20 +2630,20 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/storage",
|
||||
"version": "0.5.1",
|
||||
"version": "0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage.git",
|
||||
"reference": "e672aa3fc2a8ba689aff65f68ff29f1d608223b8"
|
||||
"reference": "de12487f77346475e2deed82212c6f842c9ae4a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/e672aa3fc2a8ba689aff65f68ff29f1d608223b8",
|
||||
"reference": "e672aa3fc2a8ba689aff65f68ff29f1d608223b8",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/de12487f77346475e2deed82212c6f842c9ae4a8",
|
||||
"reference": "de12487f77346475e2deed82212c6f842c9ae4a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/framework": "0.*.*"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -2676,22 +2676,22 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/storage/issues",
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.5.1"
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.7.0"
|
||||
},
|
||||
"time": "2021-12-13T15:17:14+00:00"
|
||||
"time": "2022-02-09T13:02:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/swoole.git",
|
||||
"reference": "2b714eddf77cd5eda1889219c9656d7c0a63ce73"
|
||||
"reference": "8312df69233b5dcd3992de88f131f238002749de"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/2b714eddf77cd5eda1889219c9656d7c0a63ce73",
|
||||
"reference": "2b714eddf77cd5eda1889219c9656d7c0a63ce73",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/8312df69233b5dcd3992de88f131f238002749de",
|
||||
"reference": "8312df69233b5dcd3992de88f131f238002749de",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2732,9 +2732,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/swoole/issues",
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.3.2"
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.3.3"
|
||||
},
|
||||
"time": "2021-12-13T15:37:41+00:00"
|
||||
"time": "2022-01-20T09:58:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/system",
|
||||
@@ -3031,12 +3031,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Amp\\ByteStream\\": "lib"
|
||||
},
|
||||
"files": [
|
||||
"lib/functions.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Amp\\ByteStream\\": "lib"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -3077,17 +3077,11 @@
|
||||
},
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.17.1",
|
||||
"version": "dev-feat-preps-for-0.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "3542c6ed0f808b6a9f6735a8aad7ccda961bea29"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/3542c6ed0f808b6a9f6735a8aad7ccda961bea29",
|
||||
"reference": "3542c6ed0f808b6a9f6735a8aad7ccda961bea29",
|
||||
"shasum": ""
|
||||
"url": "https://github.com/appwrite/sdk-generator",
|
||||
"reference": "b977fcf357a267f41299539ac9095aa7bbcc2600"
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
@@ -3095,10 +3089,10 @@
|
||||
"ext-mbstring": "*",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"php": ">=7.0.0",
|
||||
"twig/twig": "^2.14"
|
||||
"twig/twig": "^3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^9.5.13"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -3107,7 +3101,11 @@
|
||||
"Appwrite\\Spec\\": "src/Spec"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -3118,11 +3116,7 @@
|
||||
}
|
||||
],
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.17.1"
|
||||
},
|
||||
"time": "2022-01-07T12:55:37+00:00"
|
||||
"time": "2022-02-15T11:09:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
@@ -3197,23 +3191,23 @@
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
"version": "3.2.7",
|
||||
"version": "3.2.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/semver.git",
|
||||
"reference": "deac27056b57e46faf136fae7b449eeaa71661ee"
|
||||
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/deac27056b57e46faf136fae7b449eeaa71661ee",
|
||||
"reference": "deac27056b57e46faf136fae7b449eeaa71661ee",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/a951f614bd64dcd26137bc9b7b2637ddcfc57649",
|
||||
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^0.12.54",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"symfony/phpunit-bridge": "^4.2 || ^5"
|
||||
},
|
||||
"type": "library",
|
||||
@@ -3258,7 +3252,7 @@
|
||||
"support": {
|
||||
"irc": "irc://irc.freenode.org/composer",
|
||||
"issues": "https://github.com/composer/semver/issues",
|
||||
"source": "https://github.com/composer/semver/tree/3.2.7"
|
||||
"source": "https://github.com/composer/semver/tree/3.2.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3274,7 +3268,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-04T09:57:54+00:00"
|
||||
"time": "2022-02-04T13:58:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/xdebug-handler",
|
||||
@@ -3704,12 +3698,12 @@
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "src/DeepCopy/"
|
||||
},
|
||||
"files": [
|
||||
"src/DeepCopy/deep_copy.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "src/DeepCopy/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -3957,16 +3951,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phar-io/version",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phar-io/version.git",
|
||||
"reference": "bae7c545bef187884426f042434e561ab1ddb182"
|
||||
"reference": "15a90844ad40f127afd244c0cad228de2a80052a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182",
|
||||
"reference": "bae7c545bef187884426f042434e561ab1ddb182",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/15a90844ad40f127afd244c0cad228de2a80052a",
|
||||
"reference": "15a90844ad40f127afd244c0cad228de2a80052a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4002,9 +3996,9 @@
|
||||
"description": "Library for handling version information and constraints",
|
||||
"support": {
|
||||
"issues": "https://github.com/phar-io/version/issues",
|
||||
"source": "https://github.com/phar-io/version/tree/3.1.0"
|
||||
"source": "https://github.com/phar-io/version/tree/3.1.1"
|
||||
},
|
||||
"time": "2021-02-23T14:00:09+00:00"
|
||||
"time": "2022-02-07T21:56:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
@@ -4613,11 +4607,11 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
],
|
||||
"files": [
|
||||
"src/Framework/Assert/Functions.php"
|
||||
],
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -5213,16 +5207,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
"version": "5.0.3",
|
||||
"version": "5.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/global-state.git",
|
||||
"reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49"
|
||||
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49",
|
||||
"reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
|
||||
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5265,7 +5259,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/global-state/issues",
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3"
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5273,7 +5267,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-11T13:31:12+00:00"
|
||||
"time": "2022-02-14T08:28:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
@@ -5839,12 +5833,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -5920,12 +5914,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
@@ -6007,12 +6001,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -6056,82 +6050,6 @@
|
||||
],
|
||||
"time": "2021-11-30T18:21:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php72",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||
"reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
|
||||
"reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php72\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php72/tree/v1.24.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-27T09:17:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.0.0",
|
||||
@@ -6400,23 +6318,22 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v2.14.10",
|
||||
"version": "v3.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "95fb194cd4dd6ac373a27af2bde2bad5d3f27aba"
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/95fb194cd4dd6ac373a27af2bde2bad5d3f27aba",
|
||||
"reference": "95fb194cd4dd6ac373a27af2bde2bad5d3f27aba",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-php72": "^1.8"
|
||||
"symfony/polyfill-mbstring": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/container": "^1.0",
|
||||
@@ -6425,13 +6342,10 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.14-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Twig_": "lib/"
|
||||
},
|
||||
"psr-4": {
|
||||
"Twig\\": "src/"
|
||||
}
|
||||
@@ -6464,7 +6378,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v2.14.10"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -6476,7 +6390,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-03T21:13:26+00:00"
|
||||
"time": "2022-02-04T06:59:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vimeo/psalm",
|
||||
@@ -6555,13 +6469,13 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psalm\\": "src/Psalm/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php",
|
||||
"src/spl_object_id.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Psalm\\": "src/Psalm/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -6639,7 +6553,8 @@
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"appwrite/php-runtimes": 20
|
||||
"appwrite/php-runtimes": 20,
|
||||
"appwrite/sdk-generator": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
|
||||
@@ -111,6 +111,15 @@ services:
|
||||
- _APP_STORAGE_ANTIVIRUS
|
||||
- _APP_STORAGE_ANTIVIRUS_HOST
|
||||
- _APP_STORAGE_ANTIVIRUS_PORT
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_DEVICE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_S3_SECRET
|
||||
- _APP_STORAGE_DEVICE_S3_REGION
|
||||
- _APP_STORAGE_DEVICE_S3_BUCKET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_SECRET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_REGION
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_BUCKET
|
||||
- _APP_SMTP_HOST
|
||||
- _APP_SMTP_PORT
|
||||
- _APP_SMTP_SECURE
|
||||
@@ -256,6 +265,15 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_DEVICE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_S3_SECRET
|
||||
- _APP_STORAGE_DEVICE_S3_REGION
|
||||
- _APP_STORAGE_DEVICE_S3_BUCKET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_SECRET
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_REGION
|
||||
- _APP_STORAGE_DEVICE_DO_SPACES_BUCKET
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
@@ -497,6 +515,10 @@ services:
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
- _APP_USAGE_SYNC_INTERVAL
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
|
||||
appwrite-schedule:
|
||||
entrypoint: schedule
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createAnonymousSession(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createJWT(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createMagicURLSession(
|
||||
"[USER_ID]",
|
||||
"email@example.com",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createOAuth2Session(
|
||||
this,
|
||||
"amazon",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createRecovery(
|
||||
"email@example.com",
|
||||
"https://example.com"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createSession(
|
||||
"email@example.com",
|
||||
"password"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.createVerification(
|
||||
"https://example.com"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.create(
|
||||
"[USER_ID]",
|
||||
"email@example.com",
|
||||
"password",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.deleteSession(
|
||||
"[SESSION_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.deleteSessions(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.delete(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getLogs(
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getPrefs(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getSession(
|
||||
"[SESSION_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.getSessions(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.get(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateEmail(
|
||||
"email@example.com",
|
||||
"password"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateMagicURLSession(
|
||||
"[USER_ID]",
|
||||
"[SECRET]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateName(
|
||||
"[NAME]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updatePassword(
|
||||
"password",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updatePrefs(
|
||||
mapOf( "a" to "b" )
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateRecovery(
|
||||
"[USER_ID]",
|
||||
"[SECRET]",
|
||||
"password",
|
||||
"password"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.updateVerification(
|
||||
"[USER_ID]",
|
||||
"[SECRET]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getBrowser(
|
||||
"aa",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getCreditCard(
|
||||
"amex",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getFavicon(
|
||||
"https://example.com"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getFlag(
|
||||
"af",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getImage(
|
||||
"https://example.com",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getInitials(
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getQR(
|
||||
"[TEXT]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.createDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]",
|
||||
mapOf( "a" to "b" ),
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.deleteDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.getDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.listDocuments(
|
||||
"[COLLECTION_ID]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Database
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Database database = new Database(client);
|
||||
|
||||
database.updateDocument(
|
||||
"[COLLECTION_ID]",
|
||||
"[DOCUMENT_ID]",
|
||||
mapOf( "a" to "b" ),
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Functions
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Functions functions = new Functions(client);
|
||||
|
||||
functions.createExecution(
|
||||
"[FUNCTION_ID]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Functions
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Functions functions = new Functions(client);
|
||||
|
||||
functions.getExecution(
|
||||
"[FUNCTION_ID]",
|
||||
"[EXECUTION_ID]"
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Functions
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Functions functions = new Functions(client);
|
||||
|
||||
functions.listExecutions(
|
||||
"[FUNCTION_ID]",
|
||||
new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.services.Locale
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Locale locale = new Locale(client);
|
||||
|
||||
locale.getContinents(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
||||
return EmptyCoroutineContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeWith(@NotNull Object o) {
|
||||
String json = "";
|
||||
try {
|
||||
if (o instanceof Result.Failure) {
|
||||
Result.Failure failure = (Result.Failure) o;
|
||||
throw failure.exception;
|
||||
} else {
|
||||
Response response = (Response) o;
|
||||
json = response.body().string();
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Log.e("ERROR", th.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user