Merge branch 'master' of https://github.com/appwrite/appwrite into feat-new-router

This commit is contained in:
Torsten Dittmann
2023-07-18 13:18:17 +02:00
5989 changed files with 9357 additions and 6398 deletions
+53
View File
@@ -0,0 +1,53 @@
name: "Release"
on:
release:
types: [published]
jobs:
tests:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# Fetch submodules
submodules: recursive
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: appwrite/appwrite
tags: |
type=semver,pattern={{major}}.{{minor}}.{{patch}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
build-args: |
VERSION=${{ steps.meta.outputs.version }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+1 -1
View File
@@ -1,4 +1,4 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
branch = 2.3.3
branch = 2.3.4
+42 -2
View File
@@ -1,12 +1,51 @@
# Version 1.3.7
## Bugs
- Fix the routing for the default OAuth2 pages [#5640](https://github.com/appwrite/appwrite/pull/5640) [#5648](https://github.com/appwrite/appwrite/pull/5648)
- Add support for trailing slashes in Routes and URLs [#5647](https://github.com/appwrite/appwrite/pull/5647) [#5648](https://github.com/appwrite/appwrite/pull/5648)
# Version 1.3.6
## Bugs
- Fix Console deep linking to result in a 404 [#5632](https://github.com/appwrite/appwrite/pull/5632)
- Fix ACME HTTP Challenge [#5632](https://github.com/appwrite/appwrite/pull/5632)
# Version 1.3.5
## Bugs
- Fix minimum length for string attribute default values [#5606](https://github.com/appwrite/appwrite/pull/5606), [#5602](https://github.com/appwrite/appwrite/pull/5602)
- Update framework to fix route mismatches [#5603](https://github.com/appwrite/appwrite/pull/5603)
# Version 1.3.4
## Bugs
- Update migration to properly migrate bucket permissions [#5497](https://github.com/appwrite/appwrite/pull/5497)
# Version 1.3.3
## Bugs
- Fixed migration resetting some data [#5455](https://github.com/appwrite/appwrite/pull/5455)
# Version 1.3.2
## Bugs
- Fixed auto-setting custom ID on nested documents [#5363](https://github.com/appwrite/appwrite/pull/5363)
- Fixed listDocuments not returning all the documents [#5395](https://github.com/appwrite/appwrite/pull/5395)
- Fixed deleting keys, webhooks, platforms and domains after deleting project [#5395](https://github.com/appwrite/appwrite/pull/5395)
- Fixed empty team prefs returning as JSON object rather array [#5361](https://github.com/appwrite/appwrite/pull/5361)
# Version 1.3.1
## Bugs
- Fixed Migration issue regarding 500 error [4906](https://github.com/appwrite/appwrite/pull/5356)
- Fixed Migration issue regarding 500 error [#5356](https://github.com/appwrite/appwrite/pull/5356)
# Version 1.3.0
## Features
- Password dictionary setting allows to compare user's password against command password database [4906](https://github.com/appwrite/appwrite/pull/4906)
- Password dictionary setting allows to compare user's password against command password database [#4906](https://github.com/appwrite/appwrite/pull/4906)
- Password history setting allows to save user's last used password so that it may not be used again. Maximum number of history saved is 20, which can be configured. Minimum is 0 which means disabled. [#4866](https://github.com/appwrite/appwrite/pull/4866)
- Update APIs to check X-Appwrite-Timestamp header [#5024](https://github.com/appwrite/appwrite/pull/5024)
- Database relationships [#5238](https://github.com/appwrite/appwrite/pull/5238)
@@ -57,6 +96,7 @@
## Changes
- Released `appwrite/console` [2.0.2](https://github.com/appwrite/console/releases/tag/2.0.2)
- Make `region` parameter optional with default for project create [#4763](https://github.com/appwrite/appwrite/pull/4763)
- Add security headers to the console endpoint [#4758](https://github.com/appwrite/appwrite/pull/4758)
## Bugs
- Fix default oauth paths [#4725](https://github.com/appwrite/appwrite/pull/4725)
+1 -1
View File
@@ -321,7 +321,7 @@ The Runtimes for all supported cloud functions (multicore builds) can be found a
The following steps are used to generate a new console SDK:
1. Update the console spec file located at `app/config/specs/swagger2-<version-number>.console.json` using Appwrite Tasks. Run the `php app/cli.php specs <version-number> normal` command in a running `appwrite/appwrite` container.
1. Update the console spec file located at `app/config/specs/swagger2-<version-number>.console.json` using Appwrite Tasks. Run the `php app/cli.php specs version=<version-number> mode=normal` command in a running `appwrite/appwrite` container.
2. Generate a new SDK using the command `php app/cli.php sdks`.
3. Change your working dir using `cd app/sdks/console-web`.
4. Build the new SDK `npm run build`.
+1 -1
View File
@@ -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.14.2-alpine3.15 as node
FROM --platform=$BUILDPLATFORM node:16.14.2-alpine3.15 as node
COPY app/console /usr/local/src/console
+5 -6
View File
@@ -1,5 +1,4 @@
> 准备好迎接狂风暴'云'了吗? 🌩 ☂️
> Appwrite Cloud即将到来!你能够通过https://appwrite.io/cloud了解更多的资讯, 注册即可领取试用额度哦
> 好消息!Appwrite 云现已进入公开测试版!立即访问 cloud.appwrite.io 注册,体验无忧的托管服务。今天就加入我们的云端吧!☁️🎉
<br />
<p align="center">
@@ -24,7 +23,7 @@
[English](README.md) | 简体中文
[**宣布带有数据库关系的 Appwrite 1.3!了解最新消息!**](https://dev.to/appwrite/join-celebrations-appwrite-13-ships-relationships-57fc)
[**Appwrite 云公开测试版!立即注册!**](https://cloud.appwrite.io)
Appwrite是一个基于Docker的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面简化了从零开始编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
@@ -67,7 +66,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:1.3.1
appwrite/appwrite:1.3.7
```
### Windows
@@ -79,7 +78,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:1.3.1
appwrite/appwrite:1.3.7
```
#### PowerShell
@@ -89,7 +88,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:1.3.1
appwrite/appwrite:1.3.7
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
+7 -8
View File
@@ -1,12 +1,10 @@
> It's going to get cloudy! 🌩 ☂️
> The Appwrite Cloud is coming soon! You can learn more about our upcoming hosted solution and signup for free credits at: https://appwrite.io/cloud
> Great news! Appwrite Cloud is now in public beta! Sign up at [cloud.appwrite.io](https://cloud.appwrite.io) for a hassle-free, hosted experience. Join us in the Cloud today! ☁️🎉
<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>A complete backend solution for your [Flutter / Vue / Angular / React / iOS / Android / *ANY OTHER*] app</b>
<b>Appwrite is a backend platform for developing Web, Mobile, and Flutter applications. Built with the open source community and optimized for developer experience in the coding languages you love.</b>
<br />
<br />
</p>
@@ -26,7 +24,7 @@
English | [简体中文](README-CN.md)
[**Announcing Appwrite 1.3 with Database Relationships! Learn what's new!**](https://dev.to/appwrite/join-celebrations-appwrite-13-ships-relationships-57fc)
[**Announcing Appwrite Cloud Public Beta! Sign up today!**](https://cloud.appwrite.io)
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.
@@ -51,6 +49,7 @@ Table of Contents:
- [CMD](#cmd)
- [PowerShell](#powershell)
- [Upgrade from an Older Version](#upgrade-from-an-older-version)
- [One-Click Setups](#one-click-setups)
- [Getting Started](#getting-started)
- [Services](#services)
- [SDKs](#sdks)
@@ -76,7 +75,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.3.1
appwrite/appwrite:1.3.7
```
### Windows
@@ -88,7 +87,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:1.3.1
appwrite/appwrite:1.3.7
```
#### PowerShell
@@ -98,7 +97,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:1.3.1
appwrite/appwrite:1.3.7
```
Once the Docker installation is complete, 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 completing the installation.
+11
View File
@@ -32,6 +32,17 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('enabled'),
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => false,
'default' => true,
'array' => false,
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
+18 -3
View File
@@ -88,6 +88,11 @@ return [
'description' => 'The request cannot be fulfilled with the current protocol. Please check the value of the _APP_OPTIONS_FORCE_HTTPS environment variable.',
'code' => 500,
],
Exception::GENERAL_USAGE_DISABLED => [
'name' => Exception::GENERAL_USAGE_DISABLED,
'description' => 'Usage stats is not configured. Please check the value of the _APP_USAGE_STATS environment variable of your Appwrite server.',
'code' => 501,
],
/** User Errors */
Exception::USER_COUNT_EXCEEDED => [
@@ -102,12 +107,12 @@ return [
],
Exception::USER_ALREADY_EXISTS => [
'name' => Exception::USER_ALREADY_EXISTS,
'description' => 'A user with the same email already exists in your project.',
'description' => 'A user with the same id, email, or phone already exists in your project.',
'code' => 409,
],
Exception::USER_BLOCKED => [
'name' => Exception::USER_BLOCKED,
'description' => 'The current user has been blocked. You can unblock the user from the Appwrite console.',
'description' => 'The current user has been blocked.',
'code' => 401,
],
Exception::USER_INVALID_TOKEN => [
@@ -398,9 +403,14 @@ return [
'description' => 'The document structure is invalid. Please ensure the attributes match the collection definition.',
'code' => 400,
],
Exception::DOCUMENT_MISSING_DATA => [
'name' => Exception::DOCUMENT_MISSING_DATA,
'description' => 'The document data is missing. You must provide the document data.',
'code' => 400,
],
Exception::DOCUMENT_MISSING_PAYLOAD => [
'name' => Exception::DOCUMENT_MISSING_PAYLOAD,
'description' => 'The document payload is missing.',
'description' => 'The document data and permissions are missing. You must provide either the document data or permissions to be updated.',
'code' => 400,
],
Exception::DOCUMENT_ALREADY_EXISTS => [
@@ -489,6 +499,11 @@ return [
'description' => 'Project with the requested ID could not be found. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
'code' => 404,
],
Exception::PROJECT_ALREADY_EXISTS => [
'name' => Exception::PROJECT_ALREADY_EXISTS,
'description' => 'Project with the requested ID already exists.',
'code' => 409,
],
Exception::PROJECT_UNKNOWN => [
'name' => Exception::PROJECT_UNKNOWN,
'description' => 'The project ID is either missing or not valid. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
+23 -23
View File
@@ -28,7 +28,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-web.git',
'gitRepoName' => 'sdk-for-web',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
'demos' => [
[
'icon' => 'react.svg',
@@ -76,7 +76,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-flutter.git',
'gitRepoName' => 'sdk-for-flutter',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'apple',
@@ -94,7 +94,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-apple.git',
'gitRepoName' => 'sdk-for-apple',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'objective-c',
@@ -111,7 +111,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-objective-c.git',
'gitRepoName' => 'sdk-for-objective-c',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'android',
@@ -129,7 +129,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-android.git',
'gitRepoName' => 'sdk-for-android',
'gitUserName' => 'appwrite',
'gitBranch' => 'main',
'gitBranch' => 'dev',
'docDirectories' => [
'Kotlin' => 'kotlin',
'Java' => 'java',
@@ -196,14 +196,14 @@ return [
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/console-web'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git',
'gitBranch' => 'main',
'gitBranch' => 'dev',
'gitRepoName' => 'sdk-for-console',
'gitUserName' => 'appwrite',
],
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '2.0.0',
'version' => '2.0.2',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -216,7 +216,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-cli.git',
'gitRepoName' => 'sdk-for-cli',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
],
],
@@ -244,7 +244,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-node.git',
'gitRepoName' => 'sdk-for-node',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'deno',
@@ -262,7 +262,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-deno.git',
'gitRepoName' => 'sdk-for-deno',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'php',
@@ -280,12 +280,12 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-php.git',
'gitRepoName' => 'sdk-for-php',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'python',
'name' => 'Python',
'version' => '2.0.0',
'version' => '2.0.2',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -298,7 +298,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-python.git',
'gitRepoName' => 'sdk-for-python',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'ruby',
@@ -316,7 +316,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-ruby.git',
'gitRepoName' => 'sdk-for-ruby',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'go',
@@ -334,7 +334,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-go.git',
'gitRepoName' => 'sdk-for-go',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'java',
@@ -352,17 +352,17 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-java.git',
'gitRepoName' => 'sdk-for-java',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '2.0.0',
'version' => '0.4.2',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => false,
'enabled' => true,
'beta' => true,
'dev' => true,
'dev' => false,
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'csharp',
@@ -370,7 +370,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-dotnet.git',
'gitRepoName' => 'sdk-for-dotnet',
'gitUserName' => 'appwrite',
'gitBranch' => 'main',
'gitBranch' => 'dev',
],
[
'key' => 'dart',
@@ -388,7 +388,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-dart.git',
'gitRepoName' => 'sdk-for-dart',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
'gitBranch' => 'dev',
],
[
'key' => 'kotlin',
@@ -406,7 +406,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-kotlin.git',
'gitRepoName' => 'sdk-for-kotlin',
'gitUserName' => 'appwrite',
'gitBranch' => 'main',
'gitBranch' => 'dev',
'docDirectories' => [
'Kotlin' => 'kotlin',
'Java' => 'java',
@@ -428,7 +428,7 @@ return [
'gitUrl' => 'git@github.com:appwrite/sdk-for-swift.git',
'gitRepoName' => 'sdk-for-swift',
'gitUserName' => 'appwrite',
'gitBranch' => 'main',
'gitBranch' => 'dev',
],
[
'key' => 'graphql',
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 -2
View File
@@ -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\', \'logowl\'',
'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,
@@ -764,7 +764,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_INACTIVE_THRESHOLD',
'description' => 'The minimum time a function can be inactive before it\'s container is shutdown and put to sleep. The default value is 60 seconds',
'description' => 'The minimum time a function must be inactive before it can be shut down and cleaned up. This feature is intended to clean up unused containers. Containers may remain active for longer than the interval before being shut down, as Appwrite only cleans up unused containers every hour. If no value is provided, the default is 60 seconds.',
'introduction' => '0.13.0',
'default' => '60',
'required' => false,
+8 -1
View File
@@ -1762,11 +1762,12 @@ App::patch('/v1/account/status')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->inject('requestTimestamp')
->inject('request')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('events')
->action(function (?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $events) {
->action(function (?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Event $events) {
$user->setAttribute('status', false);
@@ -1780,6 +1781,12 @@ App::patch('/v1/account/status')
$response->addHeader('X-Fallback-Cookies', \json_encode([]));
}
$protocol = $request->getProtocol();
$response
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
;
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
+171 -163
View File
@@ -327,22 +327,22 @@ function updateAttribute(
}
if ($type === Database::VAR_RELATIONSHIP) {
$options = \array_merge($attribute->getAttribute('options', []), $options);
$attribute->setAttribute('options', $options);
$primaryDocumentOptions = \array_merge($attribute->getAttribute('options', []), $options);
$attribute->setAttribute('options', $primaryDocumentOptions);
$dbForProject->updateRelationship(
collection: $collectionId,
id: $key,
onDelete: $options['onDelete'],
onDelete: $primaryDocumentOptions['onDelete'],
);
if ($options['twoWay']) {
$relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']);
$relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
if ($primaryDocumentOptions['twoWay']) {
$relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryDocumentOptions['relatedCollection']);
$relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']);
$relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options);
$relatedAttribute->setAttribute('options', $relatedOptions);
$dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey'], $relatedAttribute);
$dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute);
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
}
} else {
@@ -384,11 +384,12 @@ App::post('/v1/databases')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DATABASE) // Model for database needs to be created
->param('databaseId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
->param('name', '', new Text(128), 'Database name. Max length: 128 chars.')
->param('enabled', true, new Boolean(), 'Is database enabled?', true)
->inject('response')
->inject('dbForProject')
->inject('events')
->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Event $events) {
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $events) {
$databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId;
@@ -396,6 +397,7 @@ App::post('/v1/databases')
$dbForProject->createDocument('databases', new Document([
'$id' => $databaseId,
'name' => $name,
'enabled' => $enabled,
'search' => implode(' ', [$databaseId, $name]),
]));
$database = $dbForProject->getDocument('databases', $databaseId);
@@ -507,7 +509,7 @@ App::get('/v1/databases/:databaseId')
->inject('dbForProject')
->action(function (string $databaseId, Response $response, Database $dbForProject) {
$database = $dbForProject->getDocument('databases', $databaseId);
$database = $dbForProject->getDocument('databases', $databaseId);
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -619,12 +621,13 @@ App::put('/v1/databases/:databaseId')
->label('sdk.response.model', Response::MODEL_DATABASE)
->param('databaseId', '', new UID(), 'Database ID.')
->param('name', null, new Text(128), 'Database name. Max length: 128 chars.')
->param('enabled', true, new Boolean(), 'Is database enabled?', true)
->inject('response')
->inject('dbForProject')
->inject('events')
->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Event $events) {
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $events) {
$database = $dbForProject->getDocument('databases', $databaseId);
$database = $dbForProject->getDocument('databases', $databaseId);
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -633,6 +636,7 @@ App::put('/v1/databases/:databaseId')
try {
$database = $dbForProject->updateDocument('databases', $databaseId, $database
->setAttribute('name', $name)
->setAttribute('enabled', $enabled)
->setAttribute('search', implode(' ', [$databaseId, $name])));
} catch (AuthorizationException $exception) {
throw new Exception(Exception::USER_UNAUTHORIZED);
@@ -713,14 +717,16 @@ App::post('/v1/databases/:databaseId/collections')
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](/docs/permissions).', true)
->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](/docs/permissions).', true)
->param('enabled', true, new Boolean(), 'Is collection enabled?', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->inject('events')
->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, Response $response, Database $dbForProject, Event $events) {
->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $events) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -736,13 +742,13 @@ App::post('/v1/databases/:databaseId/collections')
'databaseId' => $databaseId,
'$permissions' => $permissions ?? [],
'documentSecurity' => $documentSecurity,
'enabled' => true,
'enabled' => $enabled,
'name' => $name,
'search' => implode(' ', [$collectionId, $name]),
]));
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), permissions: $permissions ?? [], documentSecurity: $documentSecurity);
} catch (DuplicateException) {
throw new Exception(Exception::COLLECTION_ALREADY_EXISTS);
} catch (LimitException) {
@@ -777,11 +783,12 @@ App::get('/v1/databases/:databaseId/collections')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject) {
->inject('mode')
->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -831,11 +838,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId')
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject) {
->inject('mode')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -870,7 +878,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
->inject('geodb')
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -967,12 +975,13 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
->param('enabled', true, new Boolean(), 'Is collection enabled?', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->inject('events')
->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, Event $events) {
->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $events) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -996,6 +1005,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
->setAttribute('documentSecurity', $documentSecurity)
->setAttribute('enabled', $enabled)
->setAttribute('search', implode(' ', [$collectionId, $name])));
$dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (StructureException $exception) {
@@ -1029,13 +1039,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForProject')
->inject('mode')
->inject('events')
->inject('deletes')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, Event $events, Delete $deletes) {
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode, Event $events, Delete $deletes) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -1053,15 +1064,13 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
$deletes
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($collection)
;
->setDocument($collection);
$events
->setContext('database', $database)
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
->setPayload($response->output($collection, Response::MODEL_COLLECTION))
;
->setPayload($response->output($collection, Response::MODEL_COLLECTION));
$response->noContent();
});
@@ -1087,7 +1096,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
->param('key', '', new Key(), 'Attribute Key.')
->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('default', null, new Text(0, 0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
->inject('response')
->inject('dbForProject')
@@ -1096,7 +1105,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
// Ensure attribute default is within required size
$validator = new Text($size);
$validator = new Text($size, 0);
if (!is_null($default) && !$validator->isValid($default)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
}
@@ -1576,7 +1585,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
Database $dbForProject,
EventDatabase $database,
Event $events
) {
) {
$key ??= $relatedCollectionId;
$twoWayKey ??= $collectionId;
@@ -1635,7 +1644,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
->inject('dbForProject')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -1685,7 +1694,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key')
->inject('dbForProject')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -1750,7 +1759,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->inject('response')
->inject('dbForProject')
->inject('events')
@@ -2158,7 +2167,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
$events,
type: Database::VAR_RELATIONSHIP,
required: false,
options : [
options: [
'onDelete' => $onDelete
]
);
@@ -2198,7 +2207,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->inject('events')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -2251,8 +2260,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->setType(DATABASE_TYPE_DELETE_ATTRIBUTE)
->setCollection($collection)
->setDatabase($db)
->setDocument($attribute)
;
->setDocument($attribute);
// Select response model based on type and format
$type = $attribute->getAttribute('type');
@@ -2280,8 +2288,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->setParam('attributeId', $attribute->getId())
->setContext('collection', $collection)
->setContext('database', $db)
->setPayload($response->output($attribute, $model))
;
->setPayload($response->output($attribute, $model));
$response->noContent();
});
@@ -2314,7 +2321,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
->inject('events')
->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -2337,7 +2344,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
}
// Convert Document[] to array of attribute metadata
$oldAttributes = \array_map(fn ($a) => $a->getArrayCopy(), $collection->getAttribute('attributes'));
$oldAttributes = \array_map(fn($a) => $a->getArrayCopy(), $collection->getAttribute('attributes'));
$oldAttributes[] = [
'key' => '$id',
@@ -2423,16 +2430,14 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
->setType(DATABASE_TYPE_CREATE_INDEX)
->setDatabase($db)
->setCollection($collection)
->setDocument($index)
;
->setDocument($index);
$events
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
->setParam('indexId', $index->getId())
->setContext('collection', $collection)
->setContext('database', $db)
;
->setContext('database', $db);
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
@@ -2458,7 +2463,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
->inject('dbForProject')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -2497,7 +2502,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->inject('dbForProject')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -2547,7 +2552,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->inject('events')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -2575,8 +2580,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->setType(DATABASE_TYPE_DELETE_INDEX)
->setDatabase($db)
->setCollection($collection)
->setDocument($index)
;
->setDocument($index);
$events
->setParam('databaseId', $databaseId)
@@ -2584,8 +2588,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->setParam('indexId', $index->getId())
->setContext('collection', $collection)
->setContext('database', $db)
->setPayload($response->output($index, Response::MODEL_INDEX))
;
->setPayload($response->output($index, Response::MODEL_INDEX));
$response->noContent();
});
@@ -2596,7 +2599,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create')
->label('scope', 'documents.write')
->label('audits.event', 'document.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}')
->label('usage.metric', 'documents.{scope}.requests.create')
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
@@ -2626,7 +2629,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data)) {
throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD);
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
if (isset($data['$id'])) {
@@ -2635,7 +2638,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -2710,7 +2713,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -2719,8 +2722,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
if (empty($related)) {
continue;
}
if (!\is_array($related)) {
$related = [$related];
$isList = \is_array($related) && \array_values($related) === $related;
if ($isList) {
$relations = $related;
} else {
$relations = [$related];
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
@@ -2728,7 +2736,15 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)
);
foreach ($related as $relation) {
foreach ($relations as &$relation) {
if (
\is_array($relation)
&& \array_values($relation) !== $relation
&& !isset($relation['$id'])
) {
$relation['$id'] = ID::unique();
$relation = new Document($relation);
}
if ($relation instanceof Document) {
$current = Authorization::skip(
fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId())
@@ -2737,7 +2753,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
if ($current->isEmpty()) {
$type = Database::PERMISSION_CREATE;
if (!isset($relation['$id']) || $relation['$id'] === 'unique()') {
if (isset($relation['$id']) && $relation['$id'] === 'unique()') {
$relation['$id'] = ID::unique();
}
} else {
@@ -2750,6 +2766,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$checkPermissions($relatedCollection, $relation, $type);
}
}
if ($isList) {
$document->setAttribute($relationship->getAttribute('key'), \array_values($relations));
} else {
$document->setAttribute($relationship->getAttribute('key'), \reset($relations));
}
}
};
@@ -2770,7 +2792,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -2803,8 +2825,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->setParam('collectionId', $collection->getId())
->setParam('documentId', $document->getId())
->setContext('collection', $collection)
->setContext('database', $database)
;
->setContext('database', $database);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -2833,20 +2854,27 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->inject('mode')
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
if (!$collection->getAttribute('documentSecurity', false)) {
$validator = new Authorization(Database::PERMISSION_READ);
if (!$validator->isValid($collection->getRead())) {
$collection = new Document();
}
}
}
if ($collection->isEmpty() || !$collection->getAttribute('enabled')) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
// Validate queries
$queriesValidator = new Documents($collection->getAttribute('attributes'), $collection->getAttribute('indexes'));
$validQueries = $queriesValidator->isValid($queries);
@@ -2873,40 +2901,22 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$filterQueries = Query::groupByType($queries)['filters'];
$documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries));
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($collection->getRead());
if (!$valid) {
$total = $documentSecurity
? $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT)
: 0;
} else {
$total = Authorization::skip(fn() => $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
}
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
// Add $collectionId and $databaseId for all documents
$processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database): bool {
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($collection->getRead());
if (!$documentSecurity && !$valid) {
return false;
}
$valid = $valid || $validator->isValid($document->getRead());
if ($documentSecurity && !$valid) {
if ($document->isEmpty()) {
return false;
}
$document->removeAttribute('$collection');
$document->setAttribute('$databaseId', $database->getId());
$document->setAttribute('$collectionId', $collection->getId());
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -2922,8 +2932,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = Authorization::skip(fn() =>
$dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId));
$relatedCollection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId));
foreach ($relations as $index => $doc) {
if ($doc instanceof Document) {
@@ -2943,19 +2952,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
return true;
};
// The linter is forcing this indentation
foreach ($documents as $index => $document) {
if (!$processDocument($collection, $document)) {
unset($documents[$index]);
if ($valid) {
$total--;
}
}
// The linter is forcing this indentation
foreach ($documents as $document) {
$processDocument($collection, $document);
}
$documents = \array_values($documents);
$response->dynamic(new Document([
'total' => $total,
'documents' => $documents,
@@ -2986,9 +2987,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->inject('mode')
->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -3009,7 +3010,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
$queries = Query::parseQueries($queries);
$document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries));
$document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries);
if ($document->isEmpty()) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
@@ -3017,17 +3018,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
// Add $collectionId and $databaseId for all documents
$processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) {
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($collection->getRead());
if (!$documentSecurity && !$valid) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
}
$valid = $valid || $validator->isValid($document->getRead());
if ($documentSecurity && !$valid) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
if ($document->isEmpty()) {
return;
}
$document->setAttribute('$databaseId', $database->getId());
@@ -3035,7 +3027,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -3089,7 +3081,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->inject('geodb')
->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
@@ -3205,9 +3197,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD);
}
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -3259,7 +3251,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$permissions = $document->getPermissions() ?? [];
}
$data = \array_merge($document->getArrayCopy(), $data);
$data = \array_merge($document->getArrayCopy(), $data); // Merge existing data with new data
$data['$collection'] = $collection->getId(); // Make sure user doesn't switch collectionID
$data['$createdAt'] = $document->getCreatedAt(); // Make sure user doesn't switch createdAt
$data['$id'] = $document->getId(); // Make sure user doesn't switch document unique ID
@@ -3284,7 +3276,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -3293,8 +3285,13 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
if (empty($related)) {
continue;
}
if (!\is_array($related)) {
$related = [$related];
$isList = \is_array($related) && \array_values($related) === $related;
if ($isList) {
$relations = $related;
} else {
$relations = [$related];
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
@@ -3302,7 +3299,15 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)
);
foreach ($related as $relation) {
foreach ($relations as &$relation) {
if (
\is_array($relation)
&& \array_values($relation) !== $relation
&& !isset($relation['$id'])
) {
$relation['$id'] = ID::unique();
$relation = new Document($relation);
}
if ($relation instanceof Document) {
$oldDocument = Authorization::skip(fn() => $dbForProject->getDocument(
'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(),
@@ -3312,7 +3317,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
if ($oldDocument->isEmpty()) {
$type = Database::PERMISSION_CREATE;
if (!isset($relation['$id']) || $relation['$id'] === 'unique()') {
if (isset($relation['$id']) && $relation['$id'] === 'unique()') {
$relation['$id'] = ID::unique();
}
} else {
@@ -3325,20 +3330,26 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$checkPermissions($relatedCollection, $relation, $oldDocument, $type);
}
}
if ($isList) {
$document->setAttribute($relationship->getAttribute('key'), \array_values($relations));
} else {
$document->setAttribute($relationship->getAttribute('key'), \reset($relations));
}
}
};
$checkPermissions($collection, $newDocument, $document, Database::PERMISSION_UPDATE);
try {
$document = Authorization::skip(fn() => $dbForProject->withRequestTimestamp(
$document = $dbForProject->withRequestTimestamp(
$requestTimestamp,
fn () => $dbForProject->updateDocument(
fn() => $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$document->getId(),
$newDocument
)
));
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (DuplicateException) {
@@ -3354,7 +3365,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -3387,8 +3398,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->setParam('collectionId', $collection->getId())
->setParam('documentId', $document->getId())
->setContext('collection', $collection)
->setContext('database', $database)
;
->setContext('database', $database);
$response->dynamic($document, Response::MODEL_DOCUMENT);
});
@@ -3424,9 +3434,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->inject('mode')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $events, Delete $deletes, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
@@ -3461,7 +3471,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -3492,7 +3502,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$checkPermissions($collection, $document);
Authorization::skip(fn () => $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) {
Authorization::skip(fn() => $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) {
try {
$dbForProject->deleteDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
@@ -3515,7 +3525,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
@@ -3545,8 +3555,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$deletes
->setType(DELETE_TYPE_AUDIT)
->setDocument($document)
;
->setDocument($document);
$events
->setParam('databaseId', $databaseId)
@@ -3554,15 +3563,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->setParam('documentId', $document->getId())
->setContext('collection', $collection)
->setContext('database', $database)
->setPayload($response->output($document, Response::MODEL_DOCUMENT))
;
->setPayload($response->output($document, Response::MODEL_DOCUMENT));
$response->noContent();
});
App::get('/v1/databases/usage')
->desc('Get usage stats for the database')
->groups(['api', 'database'])
->groups(['api', 'database', 'usage'])
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
@@ -3660,16 +3668,16 @@ App::get('/v1/databases/usage')
'databasesCount' => $stats['databases.$all.count.total'] ?? [],
'documentsCount' => $stats['documents.$all.count.total'] ?? [],
'collectionsCount' => $stats['collections.$all.count.total'] ?? [],
'documentsCreate' => $stats['documents.$all.requests.create'] ?? [],
'documentsRead' => $stats['documents.$all.requests.read'] ?? [],
'documentsCreate' => $stats['documents.$all.requests.create'] ?? [],
'documentsRead' => $stats['documents.$all.requests.read'] ?? [],
'documentsUpdate' => $stats['documents.$all.requests.update'] ?? [],
'documentsDelete' => $stats['documents.$all.requests.delete'] ?? [],
'collectionsCreate' => $stats['collections.$all.requests.create'] ?? [],
'collectionsRead' => $stats['collections.$all.requests.read'] ?? [],
'collectionsRead' => $stats['collections.$all.requests.read'] ?? [],
'collectionsUpdate' => $stats['collections.$all.requests.update'] ?? [],
'collectionsDelete' => $stats['collections.$all.requests.delete'] ?? [],
'databasesCreate' => $stats['databases.$all.requests.create'] ?? [],
'databasesRead' => $stats['databases.$all.requests.read'] ?? [],
'databasesRead' => $stats['databases.$all.requests.read'] ?? [],
'databasesUpdate' => $stats['databases.$all.requests.update'] ?? [],
'databasesDelete' => $stats['databases.$all.requests.delete'] ?? [],
]);
@@ -3680,7 +3688,7 @@ App::get('/v1/databases/usage')
App::get('/v1/databases/:databaseId/usage')
->desc('Get usage stats for the database')
->groups(['api', 'database'])
->groups(['api', 'database', 'usage'])
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
@@ -3773,12 +3781,12 @@ App::get('/v1/databases/:databaseId/usage')
'range' => $range,
'collectionsCount' => $stats["collections.{$databaseId}.count.total"] ?? [],
'collectionsCreate' => $stats["collections.{$databaseId}.requests.create"] ?? [],
'collectionsRead' => $stats["collections.{$databaseId}.requests.read"] ?? [],
'collectionsRead' => $stats["collections.{$databaseId}.requests.read"] ?? [],
'collectionsUpdate' => $stats["collections.{$databaseId}.requests.update"] ?? [],
'collectionsDelete' => $stats["collections.{$databaseId}.requests.delete"] ?? [],
'documentsCount' => $stats["documents.{$databaseId}.count.total"] ?? [],
'documentsCreate' => $stats["documents.{$databaseId}.requests.create"] ?? [],
'documentsRead' => $stats["documents.{$databaseId}.requests.read"] ?? [],
'documentsCreate' => $stats["documents.{$databaseId}.requests.create"] ?? [],
'documentsRead' => $stats["documents.{$databaseId}.requests.read"] ?? [],
'documentsUpdate' => $stats["documents.{$databaseId}.requests.update"] ?? [],
'documentsDelete' => $stats["documents.{$databaseId}.requests.delete"] ?? [],
]);
@@ -3789,7 +3797,7 @@ App::get('/v1/databases/:databaseId/usage')
App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
->desc('Get usage stats for a collection')
->groups(['api', 'database'])
->groups(['api', 'database', 'usage'])
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
@@ -3887,8 +3895,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
'documentsCount' => $stats["documents.{$databaseId}/{$collectionId}.count.total"] ?? [],
'documentsCreate' => $stats["documents.{$databaseId}/{$collectionId}.requests.create"] ?? [],
'documentsRead' => $stats["documents.{$databaseId}/{$collectionId}.requests.read"] ?? [],
'documentsUpdate' => $stats["documents.{$databaseId}/{$collectionId}.requests.update"] ?? [],
'documentsDelete' => $stats["documents.{$databaseId}/{$collectionId}.requests.delete" ?? []]
'documentsUpdate' => $stats["documents.{$databaseId}/{$collectionId}.requests.update"] ?? [],
'documentsDelete' => $stats["documents.{$databaseId}/{$collectionId}.requests.delete" ?? []]
]);
}
+6 -4
View File
@@ -211,7 +211,7 @@ App::get('/v1/functions/:functionId')
App::get('/v1/functions/:functionId/usage')
->desc('Get Function Usage')
->groups(['api', 'functions'])
->groups(['api', 'functions', 'usage'])
->label('scope', 'functions.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'functions')
@@ -321,7 +321,7 @@ App::get('/v1/functions/:functionId/usage')
App::get('/v1/functions/usage')
->desc('Get Functions Usage')
->groups(['api', 'functions'])
->groups(['api', 'functions', 'usage'])
->label('scope', 'functions.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'functions')
@@ -650,11 +650,13 @@ App::post('/v1/functions/:functionId/deployments')
$end = $request->getContentRangeEnd();
$fileSize = $request->getContentRangeSize();
$deploymentId = $request->getHeader('x-appwrite-id', $deploymentId);
if (is_null($start) || is_null($end) || is_null($fileSize)) {
// TODO make `end >= $fileSize` in next breaking version
if (is_null($start) || is_null($end) || is_null($fileSize) || $end > $fileSize) {
throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
}
if ($end === $fileSize) {
// TODO remove the condition that checks `$end === $fileSize` in next breaking version
if ($end === $fileSize - 1 || $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 {
+41 -41
View File
@@ -29,6 +29,7 @@ use Utopia\Domains\Domain;
use Utopia\Registry\Registry;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Utopia\Database\Exception\Duplicate;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Hostname;
@@ -91,38 +92,43 @@ App::post('/v1/projects')
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
}
$project = $dbForConsole->createDocument('projects', new Document([
'$id' => $projectId,
'$permissions' => [
Permission::read(Role::team(ID::custom($teamId))),
Permission::update(Role::team(ID::custom($teamId), 'owner')),
Permission::update(Role::team(ID::custom($teamId), 'developer')),
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
],
'name' => $name,
'teamInternalId' => $team->getInternalId(),
'teamId' => $team->getId(),
'region' => $region,
'description' => $description,
'logo' => $logo,
'url' => $url,
'version' => APP_VERSION_STABLE,
'legalName' => $legalName,
'legalCountry' => $legalCountry,
'legalState' => $legalState,
'legalCity' => $legalCity,
'legalAddress' => $legalAddress,
'legalTaxId' => ID::custom($legalTaxId),
'services' => new stdClass(),
'platforms' => null,
'authProviders' => [],
'webhooks' => null,
'keys' => null,
'domains' => null,
'auths' => $auths,
'search' => implode(' ', [$projectId, $name]),
]));
try {
$project = $dbForConsole->createDocument('projects', new Document([
'$id' => $projectId,
'$permissions' => [
Permission::read(Role::team(ID::custom($teamId))),
Permission::update(Role::team(ID::custom($teamId), 'owner')),
Permission::update(Role::team(ID::custom($teamId), 'developer')),
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
],
'name' => $name,
'teamInternalId' => $team->getInternalId(),
'teamId' => $team->getId(),
'region' => $region,
'description' => $description,
'logo' => $logo,
'url' => $url,
'version' => APP_VERSION_STABLE,
'legalName' => $legalName,
'legalCountry' => $legalCountry,
'legalState' => $legalState,
'legalCity' => $legalCity,
'legalAddress' => $legalAddress,
'legalTaxId' => ID::custom($legalTaxId),
'services' => new stdClass(),
'platforms' => null,
'authProviders' => [],
'webhooks' => null,
'keys' => null,
'domains' => null,
'auths' => $auths,
'search' => implode(' ', [$projectId, $name]),
]));
} catch (Duplicate $th) {
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
}
/** @var array $collections */
$collections = Config::getParam('collections', []);
@@ -245,7 +251,7 @@ App::get('/v1/projects/:projectId')
App::get('/v1/projects/:projectId/usage')
->desc('Get usage stats for a project')
->groups(['api', 'projects'])
->groups(['api', 'projects', 'usage'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
@@ -678,17 +684,11 @@ App::delete('/v1/projects/:projectId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('password', '', new Password(), 'Your user password for confirmation. Must be at least 8 chars.')
->inject('response')
->inject('user')
->inject('dbForConsole')
->inject('deletes')
->action(function (string $projectId, string $password, Response $response, Document $user, Database $dbForConsole, Delete $deletes) {
if (!Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
}
->action(function (string $projectId, Response $response, Document $user, Database $dbForConsole, Delete $deletes) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1237,7 +1237,7 @@ App::get('/v1/projects/:projectId/platforms')
}
$platforms = $dbForConsole->find('platforms', [
Query::equal('projectId', [$project->getId()]),
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::limit(5000),
]);
+9 -6
View File
@@ -128,7 +128,7 @@ App::post('/v1/storage/buckets')
$bucket = $dbForProject->getDocument('buckets', $bucketId);
$dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
$dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity);
} catch (Duplicate) {
throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS);
}
@@ -274,6 +274,7 @@ App::put('/v1/storage/buckets/:bucketId')
->setAttribute('encryption', $encryption)
->setAttribute('compression', $compression)
->setAttribute('antivirus', $antivirus));
$dbForProject->updateCollection('bucket_' . $bucket->getInternalId(), $permissions, $fileSecurity);
$events
->setParam('bucketId', $bucket->getId())
@@ -443,11 +444,13 @@ App::post('/v1/storage/buckets/:bucketId/files')
$end = $request->getContentRangeEnd();
$fileSize = $request->getContentRangeSize();
$fileId = $request->getHeader('x-appwrite-id', $fileId);
if (is_null($start) || is_null($end) || is_null($fileSize)) {
// TODO make `end >= $fileSize` in next breaking version
if (is_null($start) || is_null($end) || is_null($fileSize) || $end > $fileSize) {
throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
}
if ($end === $fileSize) {
// TODO remove the condition that checks `$end === $fileSize` in next breaking version
if ($end === $fileSize - 1 || $end === $fileSize) {
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to -1 notify it's last chunk
$chunks = $chunk = -1;
} else {
@@ -513,6 +516,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
$mimeType = $deviceFiles->getFileMimeType($path); // Get mime-type before compression and encryption
$fileHash = $deviceFiles->getFileHash($path); // Get file hash before compression and encryption
$data = '';
// Compression
$algorithm = $bucket->getAttribute('compression', COMPRESSION_TYPE_NONE);
@@ -546,7 +550,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
$sizeActual = $deviceFiles->getFileSize($path);
$fileHash = $deviceFiles->getFileHash($path);
$openSSLVersion = null;
$openSSLCipher = null;
@@ -1439,7 +1442,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
App::get('/v1/storage/usage')
->desc('Get usage stats for storage')
->groups(['api', 'storage'])
->groups(['api', 'storage', 'usage'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'storage')
@@ -1549,7 +1552,7 @@ App::get('/v1/storage/usage')
App::get('/v1/storage/:bucketId/usage')
->desc('Get usage stats for a storage bucket')
->groups(['api', 'storage'])
->groups(['api', 'storage', 'usage'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'storage')
+1 -1
View File
@@ -1093,7 +1093,7 @@ App::delete('/v1/users/:userId')
App::get('/v1/users/usage')
->desc('Get usage stats for the users API')
->groups(['api', 'users'])
->groups(['api', 'users', 'usage'])
->label('scope', 'users.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'users')
+1
View File
@@ -41,6 +41,7 @@ Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
App::init()
->groups(['api'])
->inject('utopia')
->inject('request')
->inject('response')
+6 -6
View File
@@ -286,7 +286,7 @@ App::post('/v1/mock/tests/general/upload')
$id = $request->getHeader('x-appwrite-id', '');
$file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size'];
if (is_null($start) || is_null($end) || is_null($size)) {
if (is_null($start) || is_null($end) || is_null($size) || $end >= $size) {
throw new Exception(Exception::GENERAL_MOCK, 'Invalid content-range header');
}
@@ -302,11 +302,11 @@ App::post('/v1/mock/tests/general/upload')
throw new Exception(Exception::GENERAL_MOCK, 'All chunked request must have id header (except first)');
}
if ($end !== $size && $end - $start + 1 !== $chunkSize) {
if ($end !== $size - 1 && $end - $start + 1 !== $chunkSize) {
throw new Exception(Exception::GENERAL_MOCK, 'Chunk size must be 5MB (except last chunk)');
}
if ($end !== $size && $file['size'] !== $chunkSize) {
if ($end !== $size - 1 && $file['size'] !== $chunkSize) {
throw new Exception(Exception::GENERAL_MOCK, 'Wrong chunk size');
}
@@ -314,11 +314,11 @@ App::post('/v1/mock/tests/general/upload')
throw new Exception(Exception::GENERAL_MOCK, 'Chunk size must be 5MB or less');
}
if ($end !== $size) {
if ($end !== $size - 1) {
$response->json([
'$id' => ID::custom('newfileid'),
'chunksTotal' => $file['size'] / $chunkSize,
'chunksUploaded' => $start / $chunkSize
'chunksTotal' => (int) ceil($size / ($end + 1 - $start)),
'chunksUploaded' => ceil($start / $chunkSize) + 1
]);
}
} else {
+8
View File
@@ -552,3 +552,11 @@ App::shutdown()
->submit();
}
});
App::init()
->groups(['usage'])
->action(function () {
if (App::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
throw new Exception(Exception::GENERAL_USAGE_DISABLED);
}
});
+16 -2
View File
@@ -1,14 +1,28 @@
<?php
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\App;
App::get('/console')
App::init()
->groups(['web'])
->inject('request')
->inject('response')
->action(function (Request $request, Response $response) {
$response
->addHeader('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url=' . \urlencode($request->getURI()))
->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode
;
});
App::get('/console/*')
->alias('/')
->alias('auth/*')
->alias('/invite')
->alias('/login')
->alias('/recover')
->alias('/register')
->alias('/register/*')
->groups(['web'])
->label('permission', 'public')
->label('scope', 'home')
+1 -1
View File
@@ -6,7 +6,7 @@ use Utopia\Config\Config;
App::get('/versions')
->desc('Get Version')
->groups(['web', 'home'])
->groups(['home'])
->label('scope', 'public')
->inject('response')
->action(function (Response $response) {
-6
View File
@@ -115,12 +115,6 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
if (!$dbForConsole->getCollection($key)->isEmpty()) {
continue;
}
/**
* Skip to prevent 0.16 migration issues.
*/
if (in_array($key, ['cache', 'variables']) && $dbForConsole->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), 'bucket_1')) {
continue;
}
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
+3 -3
View File
@@ -100,8 +100,8 @@ const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate pe
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 503;
const APP_VERSION_STABLE = '1.3.1';
const APP_CACHE_BUSTER = 506;
const APP_VERSION_STABLE = '1.3.7';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@@ -903,7 +903,7 @@ App::setResource('project', function ($dbForConsole, $request, $console) {
/** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Document $console */
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', 'console'));
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', ''));
if ($projectId === 'console') {
return $console;
+7 -7
View File
@@ -95,9 +95,9 @@ $cli
if (is_null($value)) {
continue;
}
foreach ($vars as &$var) {
foreach ($vars as $i => $var) {
if ($var['name'] === $key) {
$var['default'] = $value;
$vars[$i]['default'] = $value;
}
}
}
@@ -114,9 +114,9 @@ $cli
if (is_null($value)) {
continue;
}
foreach ($vars as &$var) {
foreach ($vars as $i => $var) {
if ($var['name'] === $key) {
$var['default'] = $value;
$vars[$i]['default'] = $value;
}
}
}
@@ -146,7 +146,7 @@ $cli
$input = [];
foreach ($vars as $key => $var) {
foreach ($vars as $var) {
if (!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) {
if ($data && $var['default'] !== null) {
$input[$var['name']] = $var['default'];
@@ -224,9 +224,9 @@ $cli
}
}
Console::log("Running \"docker compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\"");
Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\"");
$exit = Console::execute("${env} docker compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
$exit = Console::execute("${env} docker compose --project-directory {$path} up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
if ($exit !== 0) {
$message = 'Failed to install Appwrite dockers';
+2 -2
View File
@@ -239,8 +239,8 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
cd ' . $target . ' && \
git init --initial-branch=' . $gitBranch . ' && \
git remote add origin ' . $gitUrl . ' && \
git fetch && \
git pull ' . $gitUrl . ' && \
git fetch origin ' . $gitBranch . ' && \
git pull origin ' . $gitBranch . ' && \
rm -rf ' . $target . '/* && \
cp -r ' . $result . '/* ' . $target . '/ && \
git add . && \
+23 -3
View File
@@ -9,7 +9,6 @@ use Utopia\Database\Document;
use Utopia\Database\Query;
use Appwrite\Resque\Worker;
use Executor\Executor;
use Utopia\Storage\Device\Local;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\CLI\Console;
@@ -291,12 +290,13 @@ class DeletesV1 extends Worker
protected function deleteProject(Document $document): void
{
$projectId = $document->getId();
$projectInternalId = $document->getInternalId();
// Delete project domains and certificates
// Delete project certificates
$dbForConsole = $this->getConsoleDB();
$domains = $dbForConsole->find('domains', [
Query::equal('projectInternalId', [$document->getInternalId()])
Query::equal('projectInternalId', [$projectInternalId])
]);
foreach ($domains as $domain) {
@@ -318,6 +318,26 @@ class DeletesV1 extends Worker
}
}
// Delete Platforms
$this->deleteByGroup('platforms', [
Query::equal('projectInternalId', [$projectInternalId])
], $dbForConsole);
// Delete Domains
$this->deleteByGroup('domains', [
Query::equal('projectInternalId', [$projectInternalId])
], $dbForConsole);
// Delete Keys
$this->deleteByGroup('keys', [
Query::equal('projectInternalId', [$projectInternalId])
], $dbForConsole);
// Delete Webhooks
$this->deleteByGroup('webhooks', [
Query::equal('projectInternalId', [$projectInternalId])
], $dbForConsole);
// Delete metadata tables
try {
$dbForProject->deleteCollection('_metadata');
+15 -11
View File
@@ -82,18 +82,22 @@ class FunctionsV1 extends Worker
Console::success('Iterating function: ' . $function->getAttribute('name'));
$this->execute(
project: $project,
function: $function,
dbForProject: $database,
trigger: 'event',
// Pass first, most verbose event pattern
event: $events[0],
eventData: $payload,
user: $user
);
try {
$this->execute(
project: $project,
function: $function,
dbForProject: $database,
trigger: 'event',
// Pass first, most verbose event pattern
event: $events[0],
eventData: $payload,
user: $user
);
Console::success('Triggered function: ' . $events[0]);
Console::success('Triggered function: ' . $events[0]);
} catch (\Throwable $th) {
Console::error("Failed to execute " . $function->getId() . " with error: " . $th->getMessage());
}
}
}
+4 -4
View File
@@ -43,13 +43,13 @@
"ext-sockets": "*",
"appwrite/php-clamav": "1.1.*",
"appwrite/php-runtimes": "0.11.*",
"utopia-php/abuse": "0.24.*",
"utopia-php/abuse": "0.25.*",
"utopia-php/analytics": "0.2.*",
"utopia-php/audit": "0.25.*",
"utopia-php/audit": "0.26.*",
"utopia-php/cache": "0.8.*",
"utopia-php/cli": "0.13.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.35.*",
"utopia-php/database": "0.36.*",
"utopia-php/domains": "1.1.*",
"utopia-php/framework": "dev-feat-routing-v2 as 0.28.9",
"utopia-php/image": "0.5.*",
@@ -79,7 +79,7 @@
}
],
"require-dev": {
"appwrite/sdk-generator": "0.32.*",
"appwrite/sdk-generator": "0.33.*",
"ext-fileinfo": "*",
"phpunit/phpunit": "9.5.20",
"squizlabs/php_codesniffer": "^3.6",
Generated
+144 -149
View File
@@ -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": "0a5009fd8aef51c8bdfcd37842d16d50",
"content-hash": "2e4922f6477f9180a18d9dcce13c0a1b",
"packages": [
{
"name": "adhocore/jwt",
@@ -300,16 +300,16 @@
},
{
"name": "colinmollenhour/credis",
"version": "v1.14.0",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/colinmollenhour/credis.git",
"reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc"
"reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/colinmollenhour/credis/zipball/dccc8a46586475075fbb012d8bd523b8a938c2dc",
"reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc",
"url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c",
"reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c",
"shasum": ""
},
"require": {
@@ -341,9 +341,9 @@
"homepage": "https://github.com/colinmollenhour/credis",
"support": {
"issues": "https://github.com/colinmollenhour/credis/issues",
"source": "https://github.com/colinmollenhour/credis/tree/v1.14.0"
"source": "https://github.com/colinmollenhour/credis/tree/v1.15.0"
},
"time": "2022-11-09T01:18:39+00:00"
"time": "2023-04-18T15:34:23+00:00"
},
{
"name": "composer/package-versions-deprecated",
@@ -481,22 +481,22 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.5.0",
"version": "7.7.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
"reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5",
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5",
"guzzlehttp/psr7": "^1.9 || ^2.4",
"guzzlehttp/promises": "^1.5.3 || ^2.0",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -507,7 +507,8 @@
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1",
"ext-curl": "*",
"php-http/client-integration-tests": "^3.0",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
@@ -521,9 +522,6 @@
"bamarni-bin": {
"bin-links": true,
"forward-command": false
},
"branch-alias": {
"dev-master": "7.5-dev"
}
},
"autoload": {
@@ -589,7 +587,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.5.0"
"source": "https://github.com/guzzle/guzzle/tree/7.7.0"
},
"funding": [
{
@@ -605,38 +603,37 @@
"type": "tidelift"
}
],
"time": "2022-08-28T15:39:27+00:00"
"time": "2023-05-21T14:04:53+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "1.5.2",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "b94b2807d85443f9719887892882d0329d1e2598"
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
"reference": "b94b2807d85443f9719887892882d0329d1e2598",
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"shasum": ""
},
"require": {
"php": ">=5.5"
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4 || ^5.1"
"bamarni/composer-bin-plugin": "^1.8.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
@@ -673,7 +670,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/1.5.2"
"source": "https://github.com/guzzle/promises/tree/2.0.0"
},
"funding": [
{
@@ -689,26 +686,26 @@
"type": "tidelift"
}
],
"time": "2022-08-28T14:55:35+00:00"
"time": "2023-05-21T13:50:22+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.4.4",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf"
"reference": "b635f279edd83fc275f822a1188157ffea568ff6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
"reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
"reference": "b635f279edd83fc275f822a1188157ffea568ff6",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
@@ -728,9 +725,6 @@
"bamarni-bin": {
"bin-links": true,
"forward-command": false
},
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
@@ -792,7 +786,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.4.4"
"source": "https://github.com/guzzle/psr7/tree/2.5.0"
},
"funding": [
{
@@ -808,7 +802,7 @@
"type": "tidelift"
}
],
"time": "2023-03-09T13:19:02+00:00"
"time": "2023-04-17T16:11:26+00:00"
},
{
"name": "influxdb/influxdb-php",
@@ -1372,16 +1366,16 @@
},
{
"name": "psr/http-message",
"version": "1.1",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": ""
},
"require": {
@@ -1390,7 +1384,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
"dev-master": "2.0.x-dev"
}
},
"autoload": {
@@ -1405,7 +1399,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
@@ -1419,9 +1413,9 @@
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/1.1"
"source": "https://github.com/php-fig/http-message/tree/2.0"
},
"time": "2023-04-04T09:50:52+00:00"
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "psr/log",
@@ -1658,16 +1652,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.2.1",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"shasum": ""
},
"require": {
@@ -1676,7 +1670,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -1705,7 +1699,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -1721,7 +1715,7 @@
"type": "tidelift"
}
],
"time": "2023-03-01T10:25:55+00:00"
"time": "2023-05-23T14:45:45+00:00"
},
{
"name": "symfony/polyfill-php80",
@@ -1808,26 +1802,26 @@
},
{
"name": "utopia-php/abuse",
"version": "0.24.0",
"version": "0.25.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "403641f16a53b81ac40b91111a86e5672da49e8c"
"reference": "49a180cab5316cddec9676d900d5112d03e97ffc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/403641f16a53b81ac40b91111a86e5672da49e8c",
"reference": "403641f16a53b81ac40b91111a86e5672da49e8c",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/49a180cab5316cddec9676d900d5112d03e97ffc",
"reference": "49a180cab5316cddec9676d900d5112d03e97ffc",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/database": "0.35.*"
"utopia-php/database": "0.36.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
"laravel/pint": "1.5.*",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.4"
},
@@ -1851,9 +1845,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.24.0"
"source": "https://github.com/utopia-php/abuse/tree/0.25.0"
},
"time": "2023-04-11T05:31:55+00:00"
"time": "2023-04-27T15:43:47+00:00"
},
{
"name": "utopia-php/analytics",
@@ -1912,24 +1906,24 @@
},
{
"name": "utopia-php/audit",
"version": "0.25.0",
"version": "0.26.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "adc209f2e16878e5468f0b9cfd9f7f7ab497db31"
"reference": "e7228080f14df28737fbb050c180c26d86ac0403"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/adc209f2e16878e5468f0b9cfd9f7f7ab497db31",
"reference": "adc209f2e16878e5468f0b9cfd9f7f7ab497db31",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/e7228080f14df28737fbb050c180c26d86ac0403",
"reference": "e7228080f14df28737fbb050c180c26d86ac0403",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.35.*"
"utopia-php/database": "0.36.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
"laravel/pint": "1.5.*",
"phpstan/phpstan": "^1.8",
"phpunit/phpunit": "^9.3"
},
@@ -1953,9 +1947,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.25.0"
"source": "https://github.com/utopia-php/audit/tree/0.26.0"
},
"time": "2023-04-11T05:31:15+00:00"
"time": "2023-04-27T15:43:50+00:00"
},
{
"name": "utopia-php/cache",
@@ -2112,16 +2106,16 @@
},
{
"name": "utopia-php/database",
"version": "0.35.0",
"version": "0.36.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "f162c142fd61753c4b413b15c3c4041f3cd00bb2"
"reference": "f6ab65e59a199da5155c114564077b1ab8c4daef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/f162c142fd61753c4b413b15c3c4041f3cd00bb2",
"reference": "f162c142fd61753c4b413b15c3c4041f3cd00bb2",
"url": "https://api.github.com/repos/utopia-php/database/zipball/f6ab65e59a199da5155c114564077b1ab8c4daef",
"reference": "f6ab65e59a199da5155c114564077b1ab8c4daef",
"shasum": ""
},
"require": {
@@ -2164,9 +2158,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.35.0"
"source": "https://github.com/utopia-php/database/tree/0.36.1"
},
"time": "2023-04-11T04:02:22+00:00"
"time": "2023-04-27T08:39:55+00:00"
},
{
"name": "utopia-php/domains",
@@ -2228,12 +2222,12 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/framework.git",
"reference": "0d67d992ed6043cdc8b28ab0540ed66b2d1f06e3"
"reference": "1f1051041971abeb143fa2eee3dac8135859bb29"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/0d67d992ed6043cdc8b28ab0540ed66b2d1f06e3",
"reference": "0d67d992ed6043cdc8b28ab0540ed66b2d1f06e3",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/1f1051041971abeb143fa2eee3dac8135859bb29",
"reference": "1f1051041971abeb143fa2eee3dac8135859bb29",
"shasum": ""
},
"require": {
@@ -2241,7 +2235,8 @@
},
"require-dev": {
"laravel/pint": "^1.2",
"phpstan/phpstan": "1.9.x-dev",
"phpbench/phpbench": "^1.2",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5.25"
},
"type": "library",
@@ -2264,7 +2259,7 @@
"issues": "https://github.com/utopia-php/framework/issues",
"source": "https://github.com/utopia-php/framework/tree/feat-routing-v2"
},
"time": "2023-04-12T13:25:49+00:00"
"time": "2023-07-06T06:34:15+00:00"
},
{
"name": "utopia-php/image",
@@ -2969,16 +2964,16 @@
},
{
"name": "webonyx/graphql-php",
"version": "v14.11.9",
"version": "v14.11.10",
"source": {
"type": "git",
"url": "https://github.com/webonyx/graphql-php.git",
"reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70"
"reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff91c9f3cf241db702e30b2c42bcc0920e70ac70",
"reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70",
"url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19",
"reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19",
"shasum": ""
},
"require": {
@@ -2998,8 +2993,7 @@
"phpunit/phpunit": "^7.2 || ^8.5",
"psr/http-message": "^1.0",
"react/promise": "2.*",
"simpod/php-coveralls-mirror": "^3.0",
"squizlabs/php_codesniffer": "3.5.4"
"simpod/php-coveralls-mirror": "^3.0"
},
"suggest": {
"psr/http-message": "To use standard GraphQL server",
@@ -3023,7 +3017,7 @@
],
"support": {
"issues": "https://github.com/webonyx/graphql-php/issues",
"source": "https://github.com/webonyx/graphql-php/tree/v14.11.9"
"source": "https://github.com/webonyx/graphql-php/tree/v14.11.10"
},
"funding": [
{
@@ -3031,22 +3025,22 @@
"type": "open_collective"
}
],
"time": "2023-01-06T12:12:50+00:00"
"time": "2023-07-05T14:23:37+00:00"
}
],
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.32.1",
"version": "0.33.7",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "ba1d7afd57e3baef06c04ce6abc26f79310146df"
"reference": "9f5db4a637b23879ceacea9ed2d33b0486771ffc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/ba1d7afd57e3baef06c04ce6abc26f79310146df",
"reference": "ba1d7afd57e3baef06c04ce6abc26f79310146df",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9f5db4a637b23879ceacea9ed2d33b0486771ffc",
"reference": "9f5db4a637b23879ceacea9ed2d33b0486771ffc",
"shasum": ""
},
"require": {
@@ -3082,31 +3076,35 @@
"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.32.1"
"source": "https://github.com/appwrite/sdk-generator/tree/0.33.7"
},
"time": "2023-04-12T04:43:07+00:00"
"time": "2023-07-12T12:15:43+00:00"
},
{
"name": "doctrine/deprecations",
"version": "v1.0.0",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
"reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
"reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
"shasum": ""
},
"require": {
"php": "^7.1|^8.0"
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3"
"phpstan/phpstan": "1.4.10 || 1.10.15",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "0.18.4",
"psr/log": "^1 || ^2 || ^3",
"vimeo/psalm": "4.30.0 || 5.12.0"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
@@ -3125,9 +3123,9 @@
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
"source": "https://github.com/doctrine/deprecations/tree/v1.1.1"
},
"time": "2022-05-02T15:47:09+00:00"
"time": "2023-06-03T09:27:29+00:00"
},
{
"name": "doctrine/instantiator",
@@ -3201,16 +3199,16 @@
},
{
"name": "matthiasmullie/minify",
"version": "1.3.70",
"version": "1.3.71",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/minify.git",
"reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b"
"reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/2807d9f9bece6877577ad44acb5c801bb3ae536b",
"reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1",
"reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1",
"shasum": ""
},
"require": {
@@ -3260,7 +3258,7 @@
],
"support": {
"issues": "https://github.com/matthiasmullie/minify/issues",
"source": "https://github.com/matthiasmullie/minify/tree/1.3.70"
"source": "https://github.com/matthiasmullie/minify/tree/1.3.71"
},
"funding": [
{
@@ -3268,7 +3266,7 @@
"type": "github"
}
],
"time": "2022-12-09T12:56:44+00:00"
"time": "2023-04-25T20:33:03+00:00"
},
{
"name": "matthiasmullie/path-converter",
@@ -3384,16 +3382,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.15.4",
"version": "v4.16.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
"shasum": ""
},
"require": {
@@ -3434,9 +3432,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
},
"time": "2023-03-05T19:49:14+00:00"
"time": "2023-06-25T14:52:30+00:00"
},
{
"name": "phar-io/manifest",
@@ -3661,16 +3659,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.1",
"version": "1.7.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "dfc078e8af9c99210337325ff5aa152872c98714"
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714",
"reference": "dfc078e8af9c99210337325ff5aa152872c98714",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"shasum": ""
},
"require": {
@@ -3713,9 +3711,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
},
"time": "2023-03-27T19:02:04+00:00"
"time": "2023-05-30T18:13:47+00:00"
},
{
"name": "phpspec/prophecy",
@@ -3787,22 +3785,24 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.18.1",
"version": "1.22.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f"
"reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
"reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0",
"reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"doctrine/annotations": "^2.0",
"nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.5",
@@ -3826,9 +3826,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1"
},
"time": "2023-04-07T11:51:11+00:00"
"time": "2023-06-29T20:46:06+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -4551,16 +4551,16 @@
},
{
"name": "sebastian/diff",
"version": "4.0.4",
"version": "4.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
"reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
"reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
"shasum": ""
},
"require": {
@@ -4605,7 +4605,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
"source": "https://github.com/sebastianbergmann/diff/tree/4.0.5"
},
"funding": [
{
@@ -4613,7 +4613,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:10:38+00:00"
"time": "2023-05-07T05:35:17+00:00"
},
{
"name": "sebastian/environment",
@@ -5580,16 +5580,16 @@
},
{
"name": "twig/twig",
"version": "v3.5.1",
"version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "a6e0510cc793912b451fd40ab983a1d28f611c15"
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15",
"reference": "a6e0510cc793912b451fd40ab983a1d28f611c15",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
"shasum": ""
},
"require": {
@@ -5598,15 +5598,10 @@
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"psr/container": "^1.0",
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.5-dev"
}
},
"autoload": {
"psr-4": {
"Twig\\": "src/"
@@ -5640,7 +5635,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.5.1"
"source": "https://github.com/twigphp/Twig/tree/v3.6.1"
},
"funding": [
{
@@ -5652,7 +5647,7 @@
"type": "tidelift"
}
],
"time": "2023-02-08T07:49:20+00:00"
"time": "2023-06-08T12:52:13+00:00"
}
],
"aliases": [
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -14,7 +14,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);

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