Merge branch '1.6.x' into feat-1.5.x-mfa-emails

# Conflicts:
#	composer.lock
This commit is contained in:
Bradley Schofield
2024-09-27 15:03:33 +09:00
218 changed files with 306579 additions and 383 deletions
+47
View File
@@ -0,0 +1,47 @@
name: Nightly Security Scan
on:
schedule:
- cron: '0 0 * * *' # 12am UTC daily runtime
workflow_dispatch:
jobs:
scan-image:
name: Scan Docker Image
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build the Docker image
run: docker build . -t appwrite_image:latest
- name: Run Trivy vulnerability scanner on image
uses: aquasecurity/trivy-action@0.20.0
with:
image-ref: 'appwrite_image:latest'
format: 'sarif'
output: 'trivy-image-results.sarif'
ignore-unfixed: 'false'
severity: 'CRITICAL,HIGH'
- name: Upload Docker Image Scan Results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-image-results.sarif'
scan-code:
name: Scan Code
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner on filesystem
uses: aquasecurity/trivy-action@0.20.0
with:
scan-type: 'fs'
format: 'sarif'
output: 'trivy-fs-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Code Scan Results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-fs-results.sarif'
+30 -12
View File
@@ -1,17 +1,22 @@
name: PR Security Scan
on:
pull_request:
on:
pull_request_target:
types: [opened, synchronize, reopened]
workflow_dispatch:
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check out code
- name: Check out code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
submodules: 'recursive'
- name: Build the Docker image
uses: docker/build-push-action@v5
with:
@@ -19,6 +24,7 @@ jobs:
push: false
load: true
tags: pr_image:${{ github.sha }}
- name: Run Trivy vulnerability scanner on image
uses: aquasecurity/trivy-action@0.20.0
with:
@@ -26,6 +32,7 @@ jobs:
format: 'json'
output: 'trivy-image-results.json'
severity: 'CRITICAL,HIGH'
- name: Run Trivy vulnerability scanner on source code
uses: aquasecurity/trivy-action@0.20.0
with:
@@ -34,10 +41,11 @@ jobs:
format: 'json'
output: 'trivy-fs-results.json'
severity: 'CRITICAL,HIGH'
- name: Process and post Trivy scan results
- name: Process Trivy scan results
id: process-results
uses: actions/github-script@v7
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs');
let commentBody = '## Security Scan Results for PR\n\n';
@@ -79,9 +87,19 @@ jobs:
commentBody += 'Please contact the core team for assistance.';
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
core.setOutput('comment-body', commentBody);
- name: Find Comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Security Scan Results for PR
- name: Create or update comment
uses: peter-evans/create-or-update-comment@v3
with:
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.fc.outputs.comment-id }}
body: ${{ steps.process-results.outputs.comment-body }}
edit-mode: replace
+142
View File
@@ -1,3 +1,145 @@
# Version 1.6.0
## What's Changed
### Notable changes
* Allow execution filter attributes in [#7607](https://github.com/appwrite/appwrite/pull/7607)
* Add dynamic API keys for function executions in [#7512](https://github.com/appwrite/appwrite/pull/7512)
* Add metrics for successful and failed builds in [#8210](https://github.com/appwrite/appwrite/pull/8210)
* Update logging config to use a DSN approach in [#8187](https://github.com/appwrite/appwrite/pull/8187)
* Add projects.createJWT endpoint for dynamic keys in [#8213](https://github.com/appwrite/appwrite/pull/8213)
* Add users.createJWT() endpoint for local function development in [#8207](https://github.com/appwrite/appwrite/pull/8207)
* Added cancel build endpoint in [#7605](https://github.com/appwrite/appwrite/pull/7605)
* Add CLI as a function deployment type in [#8215](https://github.com/appwrite/appwrite/pull/8215)
* Add vcs.getRepositoryContents() endpoint in [#8330](https://github.com/appwrite/appwrite/pull/8330)
* Add appwrite version in function variables in [#8336](https://github.com/appwrite/appwrite/pull/8336)
* Add support for scheduled executions in [#8243](https://github.com/appwrite/appwrite/pull/8243)
* Add endpoint to delete execution in [#8337](https://github.com/appwrite/appwrite/pull/8337)
* OPR v4 support in [#8323](https://github.com/appwrite/appwrite/pull/8323)
* Mock OTP and phone numbers in [#7565](https://github.com/appwrite/appwrite/pull/7565)
* Support scheduled executions in [#8355](https://github.com/appwrite/appwrite/pull/8355)
* Add alert for new sessions in [#8315](https://github.com/appwrite/appwrite/pull/8315)
* Update delete authenticator to remove OTP Validation in [#8367](https://github.com/appwrite/appwrite/pull/8367)
* Track project last activity in [#8366](https://github.com/appwrite/appwrite/pull/8366)
* Containerize the console in [#8406](https://github.com/appwrite/appwrite/pull/8406)
* Implement MBSeconds Metric on 1.5.X in [#8385](https://github.com/appwrite/appwrite/pull/8385)
* Support JWTs without session ID in [#8420](https://github.com/appwrite/appwrite/pull/8420)
* 1.6.x sdks in [#8359](https://github.com/appwrite/appwrite/pull/8359)
* Base migration for 1.6.x in [#8417](https://github.com/appwrite/appwrite/pull/8417)
* 1.6.x migrations and filters in [#8403](https://github.com/appwrite/appwrite/pull/8403)
* Add APPWRITE_REGION in function variables in [#8394](https://github.com/appwrite/appwrite/pull/8394)
* Support dynamic keys for domain executions in [#8428](https://github.com/appwrite/appwrite/pull/8428)
* Bump DBIP to latest version in [#8467](https://github.com/appwrite/appwrite/pull/8467)
* Automatically restart function on crash in [#8473](https://github.com/appwrite/appwrite/pull/8473)
* Don't send session alerts for otp and magic-url logins in [#8459](https://github.com/appwrite/appwrite/pull/8459)
* Mark 4XX executions as successful in [#8493](https://github.com/appwrite/appwrite/pull/8493)
* Add dynamic keys in builds in [#8492](https://github.com/appwrite/appwrite/pull/8492)
* Allow deployment queries on type and size in [#8515](https://github.com/appwrite/appwrite/pull/8515)
* Add OTP email template in [#8501](https://github.com/appwrite/appwrite/pull/8501)
* Update console links in [#8523](https://github.com/appwrite/appwrite/pull/8523)
* Add multipart support in [#8477](https://github.com/appwrite/appwrite/pull/8477)
* Separate deployment sizes in [#8556](https://github.com/appwrite/appwrite/pull/8556)
* Add go runtime in [#8572](https://github.com/appwrite/appwrite/pull/8572)
* Add react native platform in [#8562](https://github.com/appwrite/appwrite/pull/8562)
* Merge deployments and build storage metrics together in API in [#8443](https://github.com/appwrite/appwrite/pull/8443)
* Support string attribute resizing in [#8597](https://github.com/appwrite/appwrite/pull/8597)
* Support renaming attributes in [#8544](https://github.com/appwrite/appwrite/pull/8544)
* Add VCS vars to deployments & executions in [#8631](https://github.com/appwrite/appwrite/pull/8631)
* Function storage metrics in [#8668](https://github.com/appwrite/appwrite/pull/8668)
* External messaging usage count in [#8672](https://github.com/appwrite/appwrite/pull/8672)
### Fixes
* Fix execution duration in [#8357](https://github.com/appwrite/appwrite/pull/8357)
* Fix file size calculations in [#8432](https://github.com/appwrite/appwrite/pull/8432)
* Fix disabled function logging in [#8398](https://github.com/appwrite/appwrite/pull/8398)
* Fix function redeployments in [#8434](https://github.com/appwrite/appwrite/pull/8434)
* Add value to variables template in [#8483](https://github.com/appwrite/appwrite/pull/8483)
* Fix build size limits in [#8396](https://github.com/appwrite/appwrite/pull/8396)
* Fix deployment method name in [#8490](https://github.com/appwrite/appwrite/pull/8490)
* Fix function disconnecting from git in [#8500](https://github.com/appwrite/appwrite/pull/8500)
* Increase buckets metadata in [#8452](https://github.com/appwrite/appwrite/pull/8452)
* Fix deploy from git with space in [#8517](https://github.com/appwrite/appwrite/pull/8517)
* Fix missing build logs in [#8484](https://github.com/appwrite/appwrite/pull/8484)
* Delete team memberships synchronously in [#8217](https://github.com/appwrite/appwrite/pull/8217)
* Fix Anyof validator in specs in [#8543](https://github.com/appwrite/appwrite/pull/8543)
* Fix missing function variables in [#8554](https://github.com/appwrite/appwrite/pull/8554)
* Fix deadlock in [#8609](https://github.com/appwrite/appwrite/pull/8609)
* Fix domain execution stats in [#8608](https://github.com/appwrite/appwrite/pull/8608)
* Update console redirect to include query params in [#8619](https://github.com/appwrite/appwrite/pull/8619)
* Update abuse-key for mfa challenge endpoints in [#8649](https://github.com/appwrite/appwrite/pull/8649)
* Fix cross-project scheduler stability in [#8641](https://github.com/appwrite/appwrite/pull/8641)
* Fix vcs deployment size in [#8640](https://github.com/appwrite/appwrite/pull/8640)
* Fix logging behaviour for Functions in [#8627](https://github.com/appwrite/appwrite/pull/8627)
* Add retention env vars to deletes worker in [#8662](https://github.com/appwrite/appwrite/pull/8662)
* Fix scheduled executions data in [#8639](https://github.com/appwrite/appwrite/pull/8639)
### Miscellaneous
* Sync 1.6.x with main in [#8163](https://github.com/appwrite/appwrite/pull/8163)
* Remove build ID from rebuild deployment endpoint in [#8214](https://github.com/appwrite/appwrite/pull/8214)
* 1.6.x specs in [#8304](https://github.com/appwrite/appwrite/pull/8304)
* Sync with main in [#8295](https://github.com/appwrite/appwrite/pull/8295)
* Fix 1.6.x failing tests in [#8333](https://github.com/appwrite/appwrite/pull/8333)
* Ensure CI/CD works in [#8350](https://github.com/appwrite/appwrite/pull/8350)
* Update specs in [#8356](https://github.com/appwrite/appwrite/pull/8356)
* Sync main to 1.6.x in [#8430](https://github.com/appwrite/appwrite/pull/8430)
* Add scheduledAt in execution response model in [#8425](https://github.com/appwrite/appwrite/pull/8425)
* Move functions marketplace to appwrite in [#8427](https://github.com/appwrite/appwrite/pull/8427)
* Refactor deployment check in function tests in [#8444](https://github.com/appwrite/appwrite/pull/8444)
* Add ci/cd benchmark in [#8414](https://github.com/appwrite/appwrite/pull/8414)
* Upgrade SDK version in [#8465](https://github.com/appwrite/appwrite/pull/8465)
* Improve session alert in [#8399](https://github.com/appwrite/appwrite/pull/8399)
* Address review comments in [#8422](https://github.com/appwrite/appwrite/pull/8422)
* Add scopes to function template in [#8496](https://github.com/appwrite/appwrite/pull/8496)
* Update benchmark comment in [#8507](https://github.com/appwrite/appwrite/pull/8507)
* Add key to runtime model in [#8503](https://github.com/appwrite/appwrite/pull/8503)
* Upgrade logger in [#8497](https://github.com/appwrite/appwrite/pull/8497)
* Change default email addresses in [#8466](https://github.com/appwrite/appwrite/pull/8466)
* Improve scheduled executions in [#8412](https://github.com/appwrite/appwrite/pull/8412)
* Sync 1.5.x into main in [#8509](https://github.com/appwrite/appwrite/pull/8509)
* Sync 1.6 with main in [#8529](https://github.com/appwrite/appwrite/pull/8529)
* Fix templates CORS in [#8528](https://github.com/appwrite/appwrite/pull/8528)
* Update size to specification for variable runtimes in [#8537](https://github.com/appwrite/appwrite/pull/8537)
* Add boundary to multipart header in [#8539](https://github.com/appwrite/appwrite/pull/8539)
* Support manual templates in [#8527](https://github.com/appwrite/appwrite/pull/8527)
* Reorder runtimes in [#8540](https://github.com/appwrite/appwrite/pull/8540)
* Fix 1.6 bugs in [#8358](https://github.com/appwrite/appwrite/pull/8358)
* Add seconds precision to scheduledAt in [#8546](https://github.com/appwrite/appwrite/pull/8546)
* Update docker base image in [#8485](https://github.com/appwrite/appwrite/pull/8485)
* Update create execution return type in [#8542](https://github.com/appwrite/appwrite/pull/8542)
* Default fallback to for templateBranch in [#8547](https://github.com/appwrite/appwrite/pull/8547)
* Fix env vars functions test in [#8555](https://github.com/appwrite/appwrite/pull/8555)
* Fix session alerts in [#8550](https://github.com/appwrite/appwrite/pull/8550)
* Add runtime controls in [#8384](https://github.com/appwrite/appwrite/pull/8384)
* Revert request type to json in create execution in [#8563](https://github.com/appwrite/appwrite/pull/8563)
* Sync 1.6.x Filters and Migrations with latest in [#8553](https://github.com/appwrite/appwrite/pull/8553)
* Update sdks in [#8551](https://github.com/appwrite/appwrite/pull/8551)
* Update Docs in [#8567](https://github.com/appwrite/appwrite/pull/8567)
* Headers validator benchmark in [#8561](https://github.com/appwrite/appwrite/pull/8561)
* Fix go version in [#8571](https://github.com/appwrite/appwrite/pull/8571)
* Update dependencies in [#8574](https://github.com/appwrite/appwrite/pull/8574)
* Upgrade console in [#8575](https://github.com/appwrite/appwrite/pull/8575)
* 1.6.x logging test in [#8580](https://github.com/appwrite/appwrite/pull/8580)
* Bump console sdk in [#8581](https://github.com/appwrite/appwrite/pull/8581)
* Update sdks in [#8582](https://github.com/appwrite/appwrite/pull/8582)
* Add changelogs for dart and flutter in [#8587](https://github.com/appwrite/appwrite/pull/8587)
* Add payload validator in [#8594](https://github.com/appwrite/appwrite/pull/8594)
* Update geodb in [#8615](https://github.com/appwrite/appwrite/pull/8615)
* Update createdeployment methodtype to upload in [#8616](https://github.com/appwrite/appwrite/pull/8616)
* Remove tenant in document filter in [#8624](https://github.com/appwrite/appwrite/pull/8624)
* Improve mail datetime format in [#8628](https://github.com/appwrite/appwrite/pull/8628)
* Fix router function execution logging in [#8625](https://github.com/appwrite/appwrite/pull/8625)
* Add Functions templates async test in [#8622](https://github.com/appwrite/appwrite/pull/8622)
* Update console in [#8629](https://github.com/appwrite/appwrite/pull/8629)
* 1.6.1 in [#8630](https://github.com/appwrite/appwrite/pull/8630)
* Update version in [#8646](https://github.com/appwrite/appwrite/pull/8646)
* Phone auth metric rename in [#8648](https://github.com/appwrite/appwrite/pull/8648)
* Pretty print specs in [#8643](https://github.com/appwrite/appwrite/pull/8643)
* Fix messaging metrics in [#8674](https://github.com/appwrite/appwrite/pull/8674)
* Bump console to 5.0.6 in [#8585](https://github.com/appwrite/appwrite/pull/8585)
# Version 1.5.10
## What's Changed
+6
View File
@@ -319,10 +319,16 @@ These are the current metrics we collect usage stats for:
| users | Total number of users per project|
| executions | Total number of executions per project |
| databases | Total number of databases per project |
| databases.storage | Total amount of storage used by all databases per project (in bytes) |
| databases.storage_disk | Total amount of storage used by all database per project on disk (in bytes) |
| collections | Total number of collections per project |
| {databaseInternalId}.collections | Total number of collections per database|
| {databaseInternalId}.storage | Sum of database storage (in bytes) |
| {databaseInternalId}.storage_disk | Sum of database storage on disk (in bytes) |
| documents | Total number of documents per project |
| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
| {databaseInternalId}.{collectionInternalId}.storage | Sum of database storage used by the collection (in bytes) |
| {databsaeInternalId}.{collectionInternalId}.storage_disk | Sum of database storage used by the collection on disk (in bytes) |
| buckets | Total number of buckets per project |
| files | Total number of files per project |
| {bucketInternalId}.files.storage | Sum of files.storage per bucket (in bytes) |
+2 -2
View File
@@ -1,4 +1,4 @@
FROM composer:2.0 as composer
FROM composer:2.0 AS composer
ARG TESTING=false
ENV TESTING=$TESTING
@@ -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 appwrite/base:0.9.2 as final
FROM appwrite/base:0.9.3 AS final
LABEL maintainer="team@appwrite.io"
+3 -3
View File
@@ -67,7 +67,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.5.10
appwrite/appwrite:1.6.0
```
### Windows
@@ -79,7 +79,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.5.10
appwrite/appwrite:1.6.0
```
#### PowerShell
@@ -89,7 +89,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.5.10
appwrite/appwrite:1.6.0
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
+10 -4
View File
@@ -1,4 +1,4 @@
> Our Appwrite Init event has concluded. You can check out all the new and upcoming features [on our Init website](https://appwrite.io/init) 🚀
> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) 🚀
<br />
<p align="center">
@@ -75,7 +75,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.5.10
appwrite/appwrite:1.6.0
```
### Windows
@@ -87,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.5.10
appwrite/appwrite:1.6.0
```
#### PowerShell
@@ -97,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.5.10
appwrite/appwrite:1.6.0
```
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.
@@ -134,6 +134,12 @@ Choose from one of the providers below:
<br /><sub><b>Akamai Compute</b></sub></a>
</a>
</td>
<td align="center" width="100" height="100">
<a href="https://aws.amazon.com/marketplace/pp/prodview-2hiaeo2px4md6">
<img width="50" height="39" src="public/images/integrations/aws-logo.svg" alt="AWS Logo" />
<br /><sub><b>AWS Marketplace</b></sub></a>
</a>
</td>
</tr>
</table>
Binary file not shown.
Binary file not shown.
+7 -1
View File
@@ -1,12 +1,12 @@
<?php
require_once __DIR__ . '/init.php';
require_once __DIR__ . '/controllers/general.php';
use Appwrite\Event\Certificate;
use Appwrite\Event\Delete;
use Appwrite\Event\Func;
use Appwrite\Platform\Appwrite;
use Appwrite\Runtimes\Runtimes;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\CLI;
@@ -23,6 +23,12 @@ use Utopia\Queue\Connection;
use Utopia\Registry\Registry;
use Utopia\System\System;
// overwriting runtimes to be architectur agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
// require controllers after overwriting runtimes
require_once __DIR__ . '/controllers/general.php';
Authorization::disable();
CLI::setResource('register', fn () => $register);
+5
View File
@@ -699,6 +699,11 @@ return [
'description' => 'The relationship value is invalid.',
'code' => 400,
],
Exception::ATTRIBUTE_INVALID_RESIZE => [
'name' => Exception::ATTRIBUTE_INVALID_RESIZE,
'description' => "Existing data is too large for new size, truncate your existing data then try again.",
'code' => 400,
],
/** Indexes */
Exception::INDEX_NOT_FOUND => [
+58 -38
View File
@@ -82,7 +82,7 @@ return [
'providerOwner' => 'appwrite',
'providerVersion' => '0.2.*',
'variables' => [],
'scopes' => ["users.read"]
'scopes' => ['users.read']
],
[
'icon' => 'icon-upstash',
@@ -125,7 +125,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-redis',
@@ -167,7 +168,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-neo4j',
@@ -217,7 +219,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-mongodb',
@@ -253,7 +256,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-neon',
@@ -320,7 +324,8 @@ return [
'required' => true,
'type' => 'text'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-open-ai',
@@ -380,7 +385,8 @@ return [
'required' => false,
'type' => 'number'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-discord',
@@ -442,7 +448,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-perspective-api',
@@ -476,7 +483,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-pangea',
@@ -523,7 +531,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-document',
@@ -543,7 +552,8 @@ return [
'providerRepositoryId' => 'templates',
'providerOwner' => 'appwrite',
'providerVersion' => '0.2.*',
'variables' => []
'variables' => [],
'scopes' => []
],
[
'icon' => 'icon-github',
@@ -586,7 +596,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-bookmark',
@@ -637,7 +648,7 @@ return [
'type' => 'url'
]
],
'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"]
'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write']
],
[
'icon' => 'icon-algolia',
@@ -718,7 +729,7 @@ return [
'type' => 'password'
],
],
'scopes' => ["databases.read", "collections.read", "documents.read"]
'scopes' => ['databases.read', 'collections.read', 'documents.read']
],
[
'icon' => 'icon-meilisearch',
@@ -811,7 +822,7 @@ return [
'type' => 'text'
],
],
'scopes' => ["databases.read", "collections.read", "documents.read"]
'scopes' => ['databases.read', 'collections.read', 'documents.read']
],
[
'icon' => 'icon-vonage',
@@ -896,7 +907,8 @@ return [
'required' => true,
'type' => 'phone'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-bell',
@@ -951,7 +963,8 @@ return [
'required' => true,
'type' => 'url'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-mail',
@@ -1033,7 +1046,8 @@ return [
'required' => false,
'type' => 'text'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-stripe',
@@ -1074,7 +1088,7 @@ return [
'type' => 'password'
]
],
'scopes' => ["users.read", "sessions.write", "users.write"]
'scopes' => ['users.read', 'sessions.write', 'users.write']
],
[
'icon' => 'icon-stripe',
@@ -1131,7 +1145,7 @@ return [
'type' => 'text'
]
],
'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"]
'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write']
],
[
'icon' => 'icon-chat',
@@ -1164,7 +1178,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-translate',
@@ -1197,7 +1212,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-eye',
@@ -1255,7 +1271,7 @@ return [
'type' => 'password'
]
],
'scopes' => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read']
],
[
'icon' => 'icon-eye',
@@ -1313,7 +1329,7 @@ return [
'type' => 'password'
]
],
"scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read']
],
[
'icon' => 'icon-text',
@@ -1371,7 +1387,7 @@ return [
'type' => 'password'
]
],
"scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read']
],
[
'icon' => 'icon-chat',
@@ -1429,7 +1445,7 @@ return [
'type' => 'password'
]
],
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
],
[
'icon' => 'icon-chip',
@@ -1463,7 +1479,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-chip',
@@ -1505,7 +1522,7 @@ return [
'type' => 'text'
]
],
"scopes" => ["buckets.write", "files.read", "files.write"]
'scopes' => ['buckets.write', 'files.read', 'files.write']
],
[
'icon' => 'icon-chip',
@@ -1579,7 +1596,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-document-search',
@@ -1642,7 +1660,7 @@ return [
'type' => 'text'
]
],
"scopes" => ["databases.read", "collections.read", "documents.read"]
'scopes' => ['databases.read', 'collections.read', 'documents.read']
],
[
'icon' => 'icon-chip',
@@ -1705,7 +1723,7 @@ return [
'type' => 'text'
]
],
"scopes" => ["databases.read", "collections.read", "documents.read"]
'scopes' => ['databases.read', 'collections.read', 'documents.read']
],
[
'icon' => 'icon-chat',
@@ -1760,7 +1778,7 @@ return [
'type' => 'text'
]
],
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
],
[
'icon' => 'icon-chip',
@@ -1801,7 +1819,7 @@ return [
'type' => 'text'
]
],
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
],
[
'icon' => 'icon-chip',
@@ -1841,7 +1859,8 @@ return [
'required' => false,
'type' => 'number'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-music-note',
@@ -1883,7 +1902,7 @@ return [
'type' => 'password'
]
],
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
],
[
'icon' => 'icon-chip',
@@ -1917,7 +1936,8 @@ return [
'required' => true,
'type' => 'password'
]
]
],
'scopes' => []
],
[
'icon' => 'icon-currency-dollar',
@@ -1972,7 +1992,7 @@ return [
'type' => 'text'
]
],
"scopes" => ["users.read", "users.write"]
'scopes' => ['users.read', 'users.write']
],
[
'icon' => 'icon-currency-dollar',
@@ -2043,6 +2063,6 @@ return [
'type' => 'text'
]
],
"scopes" => ["users.read", "users.write"]
'scopes' => ['users.read', 'users.write']
]
];
+1 -1
View File
@@ -20,7 +20,7 @@
"emails.magicSession.signature": "{{project}} team",
"emails.sessionAlert.subject": "Security alert: new session on your {{project}} account",
"emails.sessionAlert.hello":"Hello {{user}}",
"emails.sessionAlert.body": "A new session has been created on your {{b}}{{project}}{{/b}} account, on {{b}}{{dateTime}}{{/b}}.\nHere are the details of the new session: ",
"emails.sessionAlert.body": "A new session has been created on your {{b}}{{project}}{{/b}} account, {{b}}on {{date}}, {{year}} at {{time}} UTC{{/b}}.\nHere are the details of the new session: ",
"emails.sessionAlert.listDevice": "Device: {{b}}{{device}}{{/b}}",
"emails.sessionAlert.listIpAddress": "IP Address: {{b}}{{ipAddress}}{{/b}}",
"emails.sessionAlert.listCountry": "Country: {{b}}{{country}}{{/b}}",
+10 -10
View File
@@ -203,7 +203,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '1.0.1',
'version' => '1.1.0',
'url' => 'https://github.com/appwrite/sdk-for-console',
'package' => '',
'enabled' => true,
@@ -249,7 +249,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '14.0.0',
'version' => '14.1.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -267,7 +267,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '12.0.0',
'version' => '12.1.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@@ -303,7 +303,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '6.0.0',
'version' => '6.1.0',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -321,7 +321,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '12.0.0',
'version' => '12.1.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -339,7 +339,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '0.1.0',
'version' => '0.2.0',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -357,7 +357,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.9.0',
'version' => '0.10.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -375,7 +375,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '12.0.0',
'version' => '12.1.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -393,7 +393,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '6.0.0',
'version' => '6.1.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -415,7 +415,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '6.0.0',
'version' => '6.1.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
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
+1 -1
View File
@@ -193,7 +193,7 @@ return [
'introduction' => '1.5.1',
'default' => '',
'required' => true,
'question' => '',
'question' => 'Enter an email that will be used when registering for SSL certificates',
'filter' => ''
],
[
+6 -4
View File
@@ -124,7 +124,9 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
$emailVariables = [
'direction' => $locale->getText('settings.direction'),
'dateTime' => DateTime::format(new \DateTime(), 'h:ia MMMM dS'),
'date' => (new \DateTime())->format('F j'),
'year' => (new \DateTime())->format('YYYY'),
'time' => (new \DateTime())->format('H:i:s'),
'user' => $user->getAttribute('name'),
'project' => $project->getAttribute('name'),
'device' => $session->getAttribute('clientName'),
@@ -392,7 +394,7 @@ App::post('/v1/account')
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
if($existingTarget) {
if ($existingTarget) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
@@ -4121,7 +4123,7 @@ App::post('/v1/account/mfa/challenge')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MFA_CHALLENGE)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},token:{param-token}')
->label('abuse-key', 'url:{url},userId:{userId}')
->param('factor', '', new WhiteList([Type::EMAIL, Type::PHONE, Type::TOTP, Type::RECOVERY_CODE]), 'Factor used for verification. Must be one of following: `' . Type::EMAIL . '`, `' . Type::PHONE . '`, `' . Type::TOTP . '`, `' . Type::RECOVERY_CODE . '`.')
->inject('response')
->inject('dbForProject')
@@ -4308,7 +4310,7 @@ App::put('/v1/account/mfa/challenge')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_SESSION)
->label('abuse-limit', 10)
->label('abuse-key', 'userId:{param-userId}')
->label('abuse-key', 'url:{url},challengeId:{param-challengeId}')
->param('challengeId', '', new Text(256), 'ID of the challenge.')
->param('otp', '', new Text(256), 'Valid verification token.')
->inject('project')
+122 -33
View File
@@ -5,6 +5,7 @@ use Appwrite\Detector\Detector;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Utopia\Database\Validator\CustomId;
@@ -20,12 +21,15 @@ use Utopia\Audit\Audit;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception as DatabaseException;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Conflict as ConflictException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Truncate as TruncateException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -228,13 +232,15 @@ function updateAttribute(
Database $dbForProject,
Event $queueForEvents,
string $type,
int $size = null,
string $filter = null,
string|bool|int|float $default = null,
bool $required = null,
int|float $min = null,
int|float $max = null,
array $elements = null,
array $options = []
array $options = [],
string $newKey = null,
): Document {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -280,6 +286,10 @@ function updateAttribute(
->setAttribute('default', $default)
->setAttribute('required', $required);
if (!empty($size)) {
$attribute->setAttribute('size', $size);
}
$formatOptions = $attribute->getAttribute('formatOptions');
switch ($attribute->getAttribute('format')) {
@@ -345,6 +355,7 @@ function updateAttribute(
$dbForProject->updateRelationship(
collection: $collectionId,
id: $key,
newKey: $newKey,
onDelete: $primaryDocumentOptions['onDelete'],
);
@@ -352,22 +363,52 @@ function updateAttribute(
$relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryDocumentOptions['relatedCollection']);
$relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']);
if (!empty($newKey) && $newKey !== $key) {
$options['twoWayKey'] = $newKey;
}
$relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options);
$relatedAttribute->setAttribute('options', $relatedOptions);
$dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute);
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
}
} else {
$dbForProject->updateAttribute(
collection: $collectionId,
id: $key,
required: $required,
default: $default,
formatOptions: $options ?? null
);
try {
$dbForProject->updateAttribute(
collection: $collectionId,
id: $key,
size: $size,
required: $required,
default: $default,
formatOptions: $options ?? null,
newKey: $newKey ?? null
);
} catch (TruncateException) {
throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE);
}
}
if (!empty($newKey) && $key !== $newKey) {
// Delete attribute and recreate since we can't modify IDs
$original = clone $attribute;
$dbForProject->deleteDocument('attributes', $attribute->getId());
$attribute
->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $newKey))
->setAttribute('key', $newKey);
try {
$attribute = $dbForProject->createDocument('attributes', $attribute);
} catch (DatabaseException|PDOException) {
$attribute = $dbForProject->createDocument('attributes', $original);
}
} else {
$attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute);
}
$attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute);
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collection->getId());
$queueForEvents
@@ -412,7 +453,8 @@ App::post('/v1/databases')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) {
->inject('queueForUsage')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage) {
$databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId;
@@ -462,6 +504,7 @@ App::post('/v1/databases')
}
$queueForEvents->setParam('databaseId', $database->getId());
$queueForUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -693,7 +736,8 @@ App::delete('/v1/databases/:databaseId')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
->inject('queueForUsage')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
$database = $dbForProject->getDocument('databases', $databaseId);
@@ -716,6 +760,9 @@ App::delete('/v1/databases/:databaseId')
->setParam('databaseId', $database->getId())
->setPayload($response->output($database, Response::MODEL_DATABASE));
$queueForUsage
->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation
$response->noContent();
});
@@ -1152,6 +1199,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
'filters' => $filters,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING);
@@ -1859,10 +1907,12 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('size', null, new Integer(), 'Maximum size of the string attribute.', true)
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
@@ -1871,8 +1921,10 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_STRING,
size: $size,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -1898,10 +1950,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -1911,7 +1964,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_EMAIL,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -1938,10 +1992,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
->param('elements', null, new ArrayList(new Text(DATABASE::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE::LENGTH_KEY . ' characters long.')
->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('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -1952,7 +2007,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
filter: APP_DATABASE_ATTRIBUTE_ENUM,
default: $default,
required: $required,
elements: $elements
elements: $elements,
newKey: $newKey
);
$response
@@ -1978,10 +2034,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new IP()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -1991,7 +2048,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_IP,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2017,10 +2075,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2030,7 +2089,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_URL,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2058,10 +2118,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
->param('min', null, new Integer(), 'Minimum value to enforce on new documents')
->param('max', null, new Integer(), 'Maximum value to enforce on new documents')
->param('default', null, new Nullable(new Integer()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2072,7 +2133,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
default: $default,
required: $required,
min: $min,
max: $max
max: $max,
newKey: $newKey
);
$formatOptions = $attribute->getAttribute('formatOptions', []);
@@ -2107,10 +2169,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents')
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents')
->param('default', null, new Nullable(new FloatValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2121,7 +2184,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
default: $default,
required: $required,
min: $min,
max: $max
max: $max,
newKey: $newKey
);
$formatOptions = $attribute->getAttribute('formatOptions', []);
@@ -2154,10 +2218,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2166,7 +2231,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
queueForEvents: $queueForEvents,
type: Database::VAR_BOOLEAN,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2192,10 +2258,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new DatetimeValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2204,7 +2271,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
queueForEvents: $queueForEvents,
type: Database::VAR_DATETIME,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2229,6 +2297,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
->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('onDelete', null, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true)
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2237,6 +2306,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
string $collectionId,
string $key,
?string $onDelete,
?string $newKey,
Response $response,
Database $dbForProject,
Event $queueForEvents
@@ -2251,7 +2321,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
required: false,
options: [
'onDelete' => $onDelete
]
],
newKey: $newKey
);
$options = $attribute->getAttribute('options', []);
@@ -2286,7 +2357,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -2371,6 +2443,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->setContext('database', $db)
->setPayload($response->output($attribute, $model));
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$response->noContent();
});
@@ -2746,8 +2821,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->inject('dbForProject')
->inject('user')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode) {
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -2963,6 +3039,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->setContext('database', $database)
->setPayload($response->getPayload(), sensitive: $relationships);
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
});
App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
@@ -3579,8 +3658,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->inject('dbForProject')
->inject('queueForDeletes')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) {
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Usage $queueForUsage, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -3665,6 +3745,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->setContext('database', $database)
->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships);
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$response->noContent();
});
@@ -3690,6 +3773,7 @@ App::get('/v1/databases/usage')
METRIC_DATABASES,
METRIC_COLLECTIONS,
METRIC_DOCUMENTS,
METRIC_DATABASES_STORAGE
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
@@ -3740,9 +3824,11 @@ App::get('/v1/databases/usage')
'databasesTotal' => $usage[$metrics[0]]['total'],
'collectionsTotal' => $usage[$metrics[1]]['total'],
'documentsTotal' => $usage[$metrics[2]]['total'],
'storageTotal' => $usage[$metrics[3]]['total'],
'databases' => $usage[$metrics[0]]['data'],
'collections' => $usage[$metrics[1]]['data'],
'documents' => $usage[$metrics[2]]['data'],
'storage' => $usage[$metrics[3]]['data'],
]), Response::MODEL_USAGE_DATABASES);
});
@@ -3774,6 +3860,7 @@ App::get('/v1/databases/:databaseId/usage')
$metrics = [
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS),
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS),
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE)
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
@@ -3824,8 +3911,10 @@ App::get('/v1/databases/:databaseId/usage')
'range' => $range,
'collectionsTotal' => $usage[$metrics[0]]['total'],
'documentsTotal' => $usage[$metrics[1]]['total'],
'storageTotal' => $usage[$metrics[2]]['total'],
'collections' => $usage[$metrics[0]]['data'],
'documents' => $usage[$metrics[1]]['data'],
'storage' => $usage[$metrics[2]]['data'],
]), Response::MODEL_USAGE_DATABASE);
});
+26 -10
View File
@@ -11,6 +11,7 @@ use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Functions\Validator\Headers;
use Appwrite\Functions\Validator\Payload;
use Appwrite\Functions\Validator\RuntimeSpecification;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Platform\Tasks\ScheduleExecutions;
@@ -295,7 +296,7 @@ App::post('/v1/functions')
if (!empty($providerRepositoryId)) {
// Deploy VCS
$redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
} elseif(!$template->isEmpty()) {
} elseif (!$template->isEmpty()) {
// Deploy non-VCS from template
$deploymentId = ID::unique();
$deployment = $dbForProject->createDocument('deployments', new Document([
@@ -1151,6 +1152,7 @@ App::post('/v1/functions/:functionId/deployments')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createDeployment')
->label('sdk.methodType', 'upload')
->label('sdk.description', '/docs/references/functions/create-deployment.md')
->label('sdk.packaging', true)
->label('sdk.request.type', 'multipart/form-data')
@@ -1579,7 +1581,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
}
$path = $deployment->getAttribute('path');
if(empty($path) || !$deviceForFunctions->exists($path)) {
if (empty($path) || !$deviceForFunctions->exists($path)) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
@@ -1715,8 +1717,8 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.response.model', Response::MODEL_EXECUTION)
->label('sdk.request.type', Response::CONTENT_TYPE_JSON)
->param('functionId', '', new UID(), 'Function ID.')
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true)
->param('body', '', new Payload(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
->param('async', false, new Boolean(true), 'Execute code in the background. Default value is false.', true)
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
->param('headers', [], new AnyOf([new Assoc(), new Text(65535)], AnyOf::TYPE_MIXED), 'HTTP headers of execution. Defaults to empty.', true)
@@ -1731,9 +1733,10 @@ App::post('/v1/functions/:functionId/executions')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->action(function (string $functionId, string $body, bool $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
$async = \strval($async) === 'true' || \strval($async) === '1';
if(!$async && !is_null($scheduledAt)) {
if (!$async && !is_null($scheduledAt)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.');
}
@@ -1866,7 +1869,7 @@ App::post('/v1/functions/:functionId/executions')
$status = $async ? 'waiting' : 'processing';
if(!is_null($scheduledAt)) {
if (!is_null($scheduledAt)) {
$status = 'scheduled';
}
@@ -1896,7 +1899,7 @@ App::post('/v1/functions/:functionId/executions')
->setContext('function', $function);
if ($async) {
if(is_null($scheduledAt)) {
if (is_null($scheduledAt)) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$queueForFunctions
->setType('http')
@@ -1918,7 +1921,7 @@ App::post('/v1/functions/:functionId/executions')
'path' => $path,
'method' => $method,
'body' => $body,
'jwt' => $jwt,
'userId' => $user->getId()
];
$schedule = $dbForConsole->createDocument('schedules', new Document([
@@ -1987,6 +1990,19 @@ App::post('/v1/functions/:functionId/executions')
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''),
'APPWRITE_VCS_REPOSITORY_ID' => $deployment->getAttribute('providerRepositoryId', ''),
'APPWRITE_VCS_REPOSITORY_NAME' => $deployment->getAttribute('providerRepositoryName', ''),
'APPWRITE_VCS_REPOSITORY_OWNER' => $deployment->getAttribute('providerRepositoryOwner', ''),
'APPWRITE_VCS_REPOSITORY_URL' => $deployment->getAttribute('providerRepositoryUrl', ''),
'APPWRITE_VCS_REPOSITORY_BRANCH' => $deployment->getAttribute('providerBranch', ''),
'APPWRITE_VCS_REPOSITORY_BRANCH_URL' => $deployment->getAttribute('providerBranchUrl', ''),
'APPWRITE_VCS_COMMIT_HASH' => $deployment->getAttribute('providerCommitHash', ''),
'APPWRITE_VCS_COMMIT_MESSAGE' => $deployment->getAttribute('providerCommitMessage', ''),
'APPWRITE_VCS_COMMIT_URL' => $deployment->getAttribute('providerCommitUrl', ''),
'APPWRITE_VCS_COMMIT_AUTHOR_NAME' => $deployment->getAttribute('providerCommitAuthor', ''),
'APPWRITE_VCS_COMMIT_AUTHOR_URL' => $deployment->getAttribute('providerCommitAuthorUrl', ''),
'APPWRITE_VCS_ROOT_DIRECTORY' => $deployment->getAttribute('providerRootDirectory', ''),
]);
/** Execute function */
@@ -2075,7 +2091,7 @@ App::post('/v1/functions/:functionId/executions')
$acceptTypes = \explode(', ', $request->getHeader('accept'));
foreach ($acceptTypes as $acceptType) {
if(\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) {
if (\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) {
$response->setContentType(Response::CONTENT_TYPE_JSON);
break;
} elseif (\str_starts_with($acceptType, 'multipart/form-data') || \str_starts_with($acceptType, 'multipart/*')) {
+23
View File
@@ -47,6 +47,7 @@ App::get('/v1/project/usage')
METRIC_USERS,
METRIC_BUCKETS,
METRIC_FILES_STORAGE,
METRIC_DATABASES_STORAGE,
METRIC_DEPLOYMENTS_STORAGE,
METRIC_BUILDS_STORAGE
],
@@ -56,6 +57,7 @@ App::get('/v1/project/usage')
METRIC_NETWORK_OUTBOUND,
METRIC_USERS,
METRIC_EXECUTIONS,
METRIC_DATABASES_STORAGE,
METRIC_EXECUTIONS_MB_SECONDS,
METRIC_BUILDS_MB_SECONDS
]
@@ -182,6 +184,23 @@ App::get('/v1/project/usage')
];
}, $dbForProject->find('buckets'));
$databasesStorageBreakdown = array_map(function ($database) use ($dbForProject) {
$id = $database->getId();
$name = $database->getAttribute('name');
$metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE);
$value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
return [
'resourceId' => $id,
'name' => $name,
'value' => $value['value'] ?? 0,
];
}, $dbForProject->find('databases'));
$functionsStorageBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
@@ -269,14 +288,18 @@ App::get('/v1/project/usage')
'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS],
'documentsTotal' => $total[METRIC_DOCUMENTS],
'databasesTotal' => $total[METRIC_DATABASES],
'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE],
'usersTotal' => $total[METRIC_USERS],
'bucketsTotal' => $total[METRIC_BUCKETS],
'filesStorageTotal' => $total[METRIC_FILES_STORAGE],
'functionsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE] + $total[METRIC_BUILDS_STORAGE],
'buildsStorageTotal' => $total[METRIC_BUILDS_STORAGE],
'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE],
'executionsBreakdown' => $executionsBreakdown,
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
'bucketsBreakdown' => $bucketsBreakdown,
'databasesStorageBreakdown' => $databasesStorageBreakdown,
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
'functionsStorageBreakdown' => $functionsStorageBreakdown,
+3 -3
View File
@@ -140,7 +140,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
if($existingTarget) {
if ($existingTarget) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
@@ -164,7 +164,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]);
if($existingTarget) {
if ($existingTarget) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
@@ -2124,7 +2124,7 @@ App::post('/v1/users/:userId/jwts')
$sessions = $user->getAttribute('sessions', []);
$session = new Document();
if($sessionId === 'recent') {
if ($sessionId === 'recent') {
// Get most recent
$session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document();
} else {
+1 -1
View File
@@ -509,7 +509,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
$vcsContents = [];
foreach ($contents as $content) {
$isDirectory = false;
if($content['type'] === GitHub::CONTENTS_DIRECTORY) {
if ($content['type'] === GitHub::CONTENTS_DIRECTORY) {
$isDirectory = true;
}
+26 -4
View File
@@ -97,6 +97,9 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$type = $route->getAttribute('resourceType');
if ($type === 'function') {
$utopia->getRoute()?->label('sdk.namespace', 'functions');
$utopia->getRoute()?->label('sdk.method', 'createExecution');
if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if ($request->getProtocol() !== 'https') {
if ($request->getMethod() !== Request::METHOD_GET) {
@@ -272,6 +275,19 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''),
'APPWRITE_VCS_REPOSITORY_ID' => $deployment->getAttribute('providerRepositoryId', ''),
'APPWRITE_VCS_REPOSITORY_NAME' => $deployment->getAttribute('providerRepositoryName', ''),
'APPWRITE_VCS_REPOSITORY_OWNER' => $deployment->getAttribute('providerRepositoryOwner', ''),
'APPWRITE_VCS_REPOSITORY_URL' => $deployment->getAttribute('providerRepositoryUrl', ''),
'APPWRITE_VCS_REPOSITORY_BRANCH' => $deployment->getAttribute('providerBranch', ''),
'APPWRITE_VCS_REPOSITORY_BRANCH_URL' => $deployment->getAttribute('providerBranchUrl', ''),
'APPWRITE_VCS_COMMIT_HASH' => $deployment->getAttribute('providerCommitHash', ''),
'APPWRITE_VCS_COMMIT_MESSAGE' => $deployment->getAttribute('providerCommitMessage', ''),
'APPWRITE_VCS_COMMIT_URL' => $deployment->getAttribute('providerCommitUrl', ''),
'APPWRITE_VCS_COMMIT_AUTHOR_NAME' => $deployment->getAttribute('providerCommitAuthor', ''),
'APPWRITE_VCS_COMMIT_AUTHOR_URL' => $deployment->getAttribute('providerCommitAuthorUrl', ''),
'APPWRITE_VCS_ROOT_DIRECTORY' => $deployment->getAttribute('providerRootDirectory', ''),
]);
/** Execute function */
@@ -330,7 +346,16 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
throw $th;
}
} finally {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize())
->addMetric(METRIC_EXECUTIONS, 1)
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
@@ -341,10 +366,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
->trigger()
;
if ($function->getAttribute('logging')) {
/** @var Document $execution */
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$execution->setAttribute('logs', '');
+4 -4
View File
@@ -95,7 +95,7 @@ $databaseListener = function (string $event, Document $document, Document $proje
$databaseInternalId = $parts[1] ?? 0;
$queueForUsage
->addMetric(METRIC_COLLECTIONS, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value)
;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
@@ -207,14 +207,14 @@ App::init()
}
// Remove after migration
if(!\str_contains($apiKey, '_')) {
if (!\str_contains($apiKey, '_')) {
$keyType = API_KEY_STANDARD;
$authKey = $apiKey;
} else {
[ $keyType, $authKey ] = \explode('_', $apiKey, 2);
}
if($keyType === API_KEY_DYNAMIC) {
if ($keyType === API_KEY_DYNAMIC) {
// Dynamic key
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
@@ -244,7 +244,7 @@ App::init()
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
}
} elseif($keyType === API_KEY_STANDARD) {
} elseif ($keyType === API_KEY_STANDARD) {
// No underline means no prefix. Backwards compatibility.
// Regular key
+3 -2
View File
@@ -32,8 +32,9 @@ App::get('/')
->action(function (Request $request, Response $response) {
$url = parse_url($request->getURI());
$target = "/console{$url['path']}";
if ($url['query'] ?? false) {
$target .= "?{$url['query']}";
$params = $request->getParams();
if (!empty($params)) {
$target .= "?" . \http_build_query($params);
}
if ($url['fragment'] ?? false) {
$target .= "#{$url['fragment']}";
+21 -5
View File
@@ -223,15 +223,31 @@ const API_KEY_DYNAMIC = 'dynamic';
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
const METRIC_MESSAGES = 'messages';
const METRIC_MESSAGES_COUNTRY_CODE = '{countryCode}.messages';
const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone';
const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}';
const METRIC_MESSAGES = 'messages';
const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent';
const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed';
const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}';
const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent';
const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed';
const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}';
const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent';
const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed';
const METRIC_SESSIONS = 'sessions';
const METRIC_DATABASES = 'databases';
const METRIC_COLLECTIONS = 'collections';
const METRIC_DATABASES_STORAGE = 'databases.storage';
const METRIC_DATABASES_STORAGE_DISK = 'databases.storage_disk';
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
const METRIC_DATABASE_ID_STORAGE_DISK = '{databaseInternalId}.databases.storage_disk';
const METRIC_DOCUMENTS = 'documents';
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE_DISK = '{databaseInternalId}.{collectionInternalId}.databases.storage_disk';
const METRIC_BUCKETS = 'buckets';
const METRIC_FILES = 'files';
const METRIC_FILES_STORAGE = 'files.storage';
@@ -791,7 +807,7 @@ $register->set('logger', function () {
$adapter = null;
}
if($adapter === null) {
if ($adapter === null) {
Console::error("Logging provider not supported. Logging is disabled");
return;
}
@@ -1018,7 +1034,7 @@ $register->set('smtp', function () {
return $mail;
});
$register->set('geodb', function () {
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-08.mmdb');
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb');
});
$register->set('passwordsDictionary', function () {
$content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords');
@@ -1272,7 +1288,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
}
$jwtSessionId = $payload['sessionId'] ?? '';
if(!empty($jwtSessionId)) {
if (!empty($jwtSessionId)) {
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
}
+8 -2
View File
@@ -166,7 +166,7 @@ $image = $this->getParam('image', '');
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:5.0.4
image: <?php echo $organization; ?>/console:5.0.12
restart: unless-stopped
networks:
- appwrite
@@ -336,6 +336,9 @@ $image = $this->getParam('image', '');
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_EXECUTION
appwrite-worker-databases:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
@@ -528,6 +531,8 @@ $image = $this->getParam('image', '');
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_LOGGING_CONFIG
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
appwrite-worker-messaging:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
@@ -676,6 +681,7 @@ $image = $this->getParam('image', '');
entrypoint: worker-usage-dump
<<: *x-logging
container_name: appwrite-worker-usage-dump
restart: unless-stopped
networks:
- appwrite
depends_on:
@@ -789,7 +795,7 @@ $image = $this->getParam('image', '');
<<: *x-logging
restart: unless-stopped
stop_signal: SIGINT
image: openruntimes/executor:0.6.7
image: openruntimes/executor:0.6.11
networks:
- appwrite
- runtimes
+9 -3
View File
@@ -45,13 +45,13 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.15.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.42.0",
"utopia-php/abuse": "0.43.0",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.42.0",
"utopia-php/audit": "0.43.0",
"utopia-php/cache": "0.10.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.52.*",
"utopia-php/database": "0.53.5-rc1",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
@@ -97,5 +97,11 @@
"platform": {
"php": "8.3"
}
},
"repositories": {
"utopia-php/database": {
"type": "vcs",
"url": "https://github.com/utopia-php/database"
}
}
}
Generated
+57 -24
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": "eba741eab8bb748ed684c32711d472df",
"content-hash": "7066d9ca32e7a1a60614effdc4701970",
"packages": [
{
"name": "adhocore/jwt",
@@ -1429,16 +1429,16 @@
},
{
"name": "utopia-php/abuse",
"version": "0.42.0",
"version": "0.43.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "08cf17e7f4fd213966c8d8702e406f2269244f0f"
"reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/08cf17e7f4fd213966c8d8702e406f2269244f0f",
"reference": "08cf17e7f4fd213966c8d8702e406f2269244f0f",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/6346a3b4c5177a43160035a7289e30fdfb0790d6",
"reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6",
"shasum": ""
},
"require": {
@@ -1446,7 +1446,7 @@
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/database": "0.52.*"
"utopia-php/database": "0.53.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1474,9 +1474,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.42.0"
"source": "https://github.com/utopia-php/abuse/tree/0.43.0"
},
"time": "2024-08-21T08:24:01+00:00"
"time": "2024-08-30T05:17:23+00:00"
},
{
"name": "utopia-php/analytics",
@@ -1526,21 +1526,21 @@
},
{
"name": "utopia-php/audit",
"version": "0.42.0",
"version": "0.43.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "9dc168470625bcf11ff8cd9ab5660db09129f618"
"reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/9dc168470625bcf11ff8cd9ab5660db09129f618",
"reference": "9dc168470625bcf11ff8cd9ab5660db09129f618",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e",
"reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.52.*"
"utopia-php/database": "0.53.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1567,9 +1567,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.42.0"
"source": "https://github.com/utopia-php/audit/tree/0.43.0"
},
"time": "2024-08-21T08:24:08+00:00"
"time": "2024-08-30T05:17:36+00:00"
},
{
"name": "utopia-php/cache",
@@ -1723,16 +1723,16 @@
},
{
"name": "utopia-php/database",
"version": "0.52.2",
"version": "0.53.5-rc1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e"
"reference": "689ba22063bf46def385da8695ba7a921e81e38d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/24b29bcac7eb7a8b81698a80bb75fc5909f4975e",
"reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e",
"url": "https://api.github.com/repos/utopia-php/database/zipball/689ba22063bf46def385da8695ba7a921e81e38d",
"reference": "689ba22063bf46def385da8695ba7a921e81e38d",
"shasum": ""
},
"require": {
@@ -1759,7 +1759,38 @@
"Utopia\\Database\\": "src/Database"
}
},
"notification-url": "https://packagist.org/downloads/",
"autoload-dev": {
"psr-4": {
"Tests\\E2E\\": "tests/e2e",
"Tests\\Unit\\": "tests/unit"
}
},
"scripts": {
"build": [
"Composer\\Config::disableProcessTimeout",
"docker compose build"
],
"start": [
"Composer\\Config::disableProcessTimeout",
"docker compose up -d"
],
"test": [
"Composer\\Config::disableProcessTimeout",
"docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml"
],
"lint": [
"./vendor/bin/pint --test"
],
"format": [
"./vendor/bin/pint"
],
"check": [
"./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 512M"
],
"coverage": [
"./vendor/bin/coverage-check ./tmp/clover.xml 90"
]
},
"license": [
"MIT"
],
@@ -1772,10 +1803,10 @@
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.52.2"
"source": "https://github.com/utopia-php/database/tree/0.53.5-rc1",
"issues": "https://github.com/utopia-php/database/issues"
},
"time": "2024-09-02T06:28:50+00:00"
"time": "2024-09-24T08:43:10+00:00"
},
{
"name": "utopia-php/domains",
@@ -6995,7 +7026,9 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"utopia-php/database": 5
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+2 -2
View File
@@ -196,7 +196,7 @@ services:
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.0.4
image: appwrite/console:5.0.12
restart: unless-stopped
networks:
- appwrite
@@ -874,7 +874,7 @@ services:
hostname: exc1
<<: *x-logging
stop_signal: SIGINT
image: openruntimes/executor:0.6.7
image: openruntimes/executor:0.6.11
restart: unless-stopped
networks:
- appwrite
@@ -11,7 +11,8 @@ const result = await databases.updateBooleanAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
false // default
false, // default
'' // newKey (optional)
);
console.log(result);
@@ -11,7 +11,8 @@ const result = await databases.updateDatetimeAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'' // default
'', // default
'' // newKey (optional)
);
console.log(result);
@@ -11,7 +11,8 @@ const result = await databases.updateEmailAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'email@example.com' // default
'email@example.com', // default
'' // newKey (optional)
);
console.log(result);
@@ -12,7 +12,8 @@ const result = await databases.updateEnumAttribute(
'', // key
[], // elements
false, // required
'<DEFAULT>' // default
'<DEFAULT>', // default
'' // newKey (optional)
);
console.log(result);
@@ -13,7 +13,8 @@ const result = await databases.updateFloatAttribute(
false, // required
null, // min
null, // max
null // default
null, // default
'' // newKey (optional)
);
console.log(result);
@@ -13,7 +13,8 @@ const result = await databases.updateIntegerAttribute(
false, // required
null, // min
null, // max
null // default
null, // default
'' // newKey (optional)
);
console.log(result);
@@ -11,7 +11,8 @@ const result = await databases.updateIpAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'' // default
'', // default
'' // newKey (optional)
);
console.log(result);
@@ -10,7 +10,8 @@ const result = await databases.updateRelationshipAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'', // key
RelationMutate.Cascade // onDelete (optional)
RelationMutate.Cascade, // onDelete (optional)
'' // newKey (optional)
);
console.log(result);
@@ -11,7 +11,9 @@ const result = await databases.updateStringAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'<DEFAULT>' // default
'<DEFAULT>', // default
null, // size (optional)
'' // newKey (optional)
);
console.log(result);
@@ -11,7 +11,8 @@ const result = await databases.updateUrlAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'https://example.com' // default
'https://example.com', // default
'' // newKey (optional)
);
console.log(result);
@@ -13,4 +13,5 @@ AttributeBoolean result = await databases.updateBooleanAttribute(
key: '',
xrequired: false,
xdefault: false,
newKey: '', // (optional)
);
@@ -13,4 +13,5 @@ AttributeDatetime result = await databases.updateDatetimeAttribute(
key: '',
xrequired: false,
xdefault: '',
newKey: '', // (optional)
);
@@ -13,4 +13,5 @@ AttributeEmail result = await databases.updateEmailAttribute(
key: '',
xrequired: false,
xdefault: 'email@example.com',
newKey: '', // (optional)
);
@@ -14,4 +14,5 @@ AttributeEnum result = await databases.updateEnumAttribute(
elements: [],
xrequired: false,
xdefault: '<DEFAULT>',
newKey: '', // (optional)
);
@@ -15,4 +15,5 @@ AttributeFloat result = await databases.updateFloatAttribute(
min: 0,
max: 0,
xdefault: 0,
newKey: '', // (optional)
);
@@ -15,4 +15,5 @@ AttributeInteger result = await databases.updateIntegerAttribute(
min: 0,
max: 0,
xdefault: 0,
newKey: '', // (optional)
);
@@ -13,4 +13,5 @@ AttributeIp result = await databases.updateIpAttribute(
key: '',
xrequired: false,
xdefault: '',
newKey: '', // (optional)
);
@@ -12,4 +12,5 @@ AttributeRelationship result = await databases.updateRelationshipAttribute(
collectionId: '<COLLECTION_ID>',
key: '',
onDelete: RelationMutate.cascade, // (optional)
newKey: '', // (optional)
);
@@ -13,4 +13,6 @@ AttributeString result = await databases.updateStringAttribute(
key: '',
xrequired: false,
xdefault: '<DEFAULT>',
size: 0, // (optional)
newKey: '', // (optional)
);
@@ -13,4 +13,5 @@ AttributeUrl result = await databases.updateUrlAttribute(
key: '',
xrequired: false,
xdefault: 'https://example.com',
newKey: '', // (optional)
);
@@ -1,3 +1,4 @@
import 'dart:io';
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
@@ -12,5 +12,6 @@ const response = await databases.updateBooleanAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
false // default
false, // default
'' // newKey (optional)
);
@@ -12,5 +12,6 @@ const response = await databases.updateDatetimeAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'' // default
'', // default
'' // newKey (optional)
);
@@ -12,5 +12,6 @@ const response = await databases.updateEmailAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'email@example.com' // default
'email@example.com', // default
'' // newKey (optional)
);
@@ -13,5 +13,6 @@ const response = await databases.updateEnumAttribute(
'', // key
[], // elements
false, // required
'<DEFAULT>' // default
'<DEFAULT>', // default
'' // newKey (optional)
);
@@ -14,5 +14,6 @@ const response = await databases.updateFloatAttribute(
false, // required
null, // min
null, // max
null // default
null, // default
'' // newKey (optional)
);
@@ -14,5 +14,6 @@ const response = await databases.updateIntegerAttribute(
false, // required
null, // min
null, // max
null // default
null, // default
'' // newKey (optional)
);
@@ -12,5 +12,6 @@ const response = await databases.updateIpAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'' // default
'', // default
'' // newKey (optional)
);
@@ -11,5 +11,6 @@ const response = await databases.updateRelationshipAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'', // key
RelationMutate.Cascade // onDelete (optional)
RelationMutate.Cascade, // onDelete (optional)
'' // newKey (optional)
);
@@ -12,5 +12,7 @@ const response = await databases.updateStringAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'<DEFAULT>' // default
'<DEFAULT>', // default
null, // size (optional)
'' // newKey (optional)
);
@@ -12,5 +12,6 @@ const response = await databases.updateUrlAttribute(
'<COLLECTION_ID>', // collectionId
'', // key
false, // required
'https://example.com' // default
'https://example.com', // default
'' // newKey (optional)
);
@@ -14,5 +14,6 @@ AttributeBoolean result = await databases.UpdateBooleanAttribute(
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: false
default: false,
newKey: "" // optional
);
@@ -14,5 +14,6 @@ AttributeDatetime result = await databases.UpdateDatetimeAttribute(
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: ""
default: "",
newKey: "" // optional
);
@@ -14,5 +14,6 @@ AttributeEmail result = await databases.UpdateEmailAttribute(
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: "email@example.com"
default: "email@example.com",
newKey: "" // optional
);
@@ -15,5 +15,6 @@ AttributeEnum result = await databases.UpdateEnumAttribute(
key: "",
elements: new List<string>(),
required: false,
default: "<DEFAULT>"
default: "<DEFAULT>",
newKey: "" // optional
);
@@ -16,5 +16,6 @@ AttributeFloat result = await databases.UpdateFloatAttribute(
required: false,
min: 0,
max: 0,
default: 0
default: 0,
newKey: "" // optional
);
@@ -16,5 +16,6 @@ AttributeInteger result = await databases.UpdateIntegerAttribute(
required: false,
min: 0,
max: 0,
default: 0
default: 0,
newKey: "" // optional
);
@@ -14,5 +14,6 @@ AttributeIp result = await databases.UpdateIpAttribute(
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: ""
default: "",
newKey: "" // optional
);
@@ -14,5 +14,6 @@ AttributeRelationship result = await databases.UpdateRelationshipAttribute(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
key: "",
onDelete: RelationMutate.Cascade // optional
onDelete: RelationMutate.Cascade, // optional
newKey: "" // optional
);
@@ -14,5 +14,7 @@ AttributeString result = await databases.UpdateStringAttribute(
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: "<DEFAULT>"
default: "<DEFAULT>",
size: 0, // optional
newKey: "" // optional
);
@@ -14,5 +14,6 @@ AttributeUrl result = await databases.UpdateUrlAttribute(
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: "https://example.com"
default: "https://example.com",
newKey: "" // optional
);
@@ -20,6 +20,7 @@ func main() {
"",
false,
false,
databases.WithUpdateBooleanAttributeNewKey(""),
)
if error != nil {
@@ -20,6 +20,7 @@ func main() {
"",
false,
"",
databases.WithUpdateDatetimeAttributeNewKey(""),
)
if error != nil {
@@ -20,6 +20,7 @@ func main() {
"",
false,
"email@example.com",
databases.WithUpdateEmailAttributeNewKey(""),
)
if error != nil {
@@ -21,6 +21,7 @@ func main() {
[]interface{}{},
false,
"<DEFAULT>",
databases.WithUpdateEnumAttributeNewKey(""),
)
if error != nil {
@@ -22,6 +22,7 @@ func main() {
0,
0,
0,
databases.WithUpdateFloatAttributeNewKey(""),
)
if error != nil {
@@ -22,6 +22,7 @@ func main() {
0,
0,
0,
databases.WithUpdateIntegerAttributeNewKey(""),
)
if error != nil {
@@ -20,6 +20,7 @@ func main() {
"",
false,
"",
databases.WithUpdateIpAttributeNewKey(""),
)
if error != nil {
@@ -19,6 +19,7 @@ func main() {
"<COLLECTION_ID>",
"",
databases.WithUpdateRelationshipAttributeOnDelete("cascade"),
databases.WithUpdateRelationshipAttributeNewKey(""),
)
if error != nil {
@@ -20,6 +20,8 @@ func main() {
"",
false,
"<DEFAULT>",
databases.WithUpdateStringAttributeSize(0),
databases.WithUpdateStringAttributeNewKey(""),
)
if error != nil {
@@ -20,6 +20,7 @@ func main() {
"",
false,
"https://example.com",
databases.WithUpdateUrlAttributeNewKey(""),
)
if error != nil {
@@ -4,7 +4,8 @@ mutation {
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: false
default: false,
newKey: ""
) {
key
type
@@ -4,7 +4,8 @@ mutation {
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: ""
default: "",
newKey: ""
) {
key
type
@@ -4,7 +4,8 @@ mutation {
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: "email@example.com"
default: "email@example.com",
newKey: ""
) {
key
type
@@ -5,7 +5,8 @@ mutation {
key: "",
elements: [],
required: false,
default: "<DEFAULT>"
default: "<DEFAULT>",
newKey: ""
) {
key
type
@@ -6,7 +6,8 @@ mutation {
required: false,
min: 0,
max: 0,
default: 0
default: 0,
newKey: ""
) {
key
type
@@ -6,7 +6,8 @@ mutation {
required: false,
min: 0,
max: 0,
default: 0
default: 0,
newKey: ""
) {
key
type
@@ -4,7 +4,8 @@ mutation {
collectionId: "<COLLECTION_ID>",
key: "",
required: false,
default: ""
default: "",
newKey: ""
) {
key
type
@@ -3,7 +3,8 @@ mutation {
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
key: "",
onDelete: "cascade"
onDelete: "cascade",
newKey: ""
) {
key
type

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