mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
368
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8f83285c8 | ||
|
|
0a3b879140 | ||
|
|
c1c53789ae | ||
|
|
131bf76848 | ||
|
|
e352eb8ced | ||
|
|
c187efadb4 | ||
|
|
fc5ea06821 | ||
|
|
aa135d5cd4 | ||
|
|
6d85d1567e | ||
|
|
9786b4f6cd | ||
|
|
7567639996 | ||
|
|
057bff2140 | ||
|
|
d7bb234072 | ||
|
|
3b5b15d1a6 | ||
|
|
2c5bc32f7e | ||
|
|
a37aa2dd68 | ||
|
|
b29f70a0af | ||
|
|
2fc5e56a61 | ||
|
|
b1373c02bc | ||
|
|
1db12e78ef | ||
|
|
a25c8004e4 | ||
|
|
547d59d95e | ||
|
|
4e0477af32 | ||
|
|
1659bc3ffa | ||
|
|
84e9c8243c | ||
|
|
020d7bb801 | ||
|
|
b6aeaffe8b | ||
|
|
65883122e3 | ||
|
|
d4660a70c3 | ||
|
|
16f9c35850 | ||
|
|
49511ebdd9 | ||
|
|
63511c56ae | ||
|
|
445ada0226 | ||
|
|
68b4a49e9d | ||
|
|
28a09d7aaa | ||
|
|
ee3b2d75ae | ||
|
|
ffe1fd89a7 | ||
|
|
06c4ba81e9 | ||
|
|
f2772cea6b | ||
|
|
b76a4bdd2f | ||
|
|
1c94381830 | ||
|
|
0209d40a38 | ||
|
|
06367eca69 | ||
|
|
8d1acef95d | ||
|
|
55343379f2 | ||
|
|
a66b565150 | ||
|
|
ff160871ae | ||
|
|
92a3eca194 | ||
|
|
07135956df | ||
|
|
b5a363a2a7 | ||
|
|
31dc9ff873 | ||
|
|
408f9c2d39 | ||
|
|
910bd69b16 | ||
|
|
dfd5965d25 | ||
|
|
2a38eedc98 | ||
|
|
f875b965be | ||
|
|
2e57e5a868 | ||
|
|
69ce99d1b4 | ||
|
|
6b881d4aaf | ||
|
|
aad7d6dbca | ||
|
|
33ffe4aca7 | ||
|
|
77eb3d4bcd | ||
|
|
49989f38f7 | ||
|
|
18a49ccc44 | ||
|
|
73173a7f8c | ||
|
|
e3412bc554 | ||
|
|
c1e50c7abd | ||
|
|
6499f436ca | ||
|
|
55fbd77bf1 | ||
|
|
4046829fc6 | ||
|
|
57e218266f | ||
|
|
188dd5c423 | ||
|
|
d8d3f22883 | ||
|
|
15dc316535 | ||
|
|
7581019e76 | ||
|
|
c4fcec0a8a | ||
|
|
ca877fa71d | ||
|
|
425dd9639c | ||
|
|
e60391bdb3 | ||
|
|
57157a71b4 | ||
|
|
8a52975048 | ||
|
|
45e3267930 | ||
|
|
2b96d60c1e | ||
|
|
417bb22790 | ||
|
|
1b70bc812b | ||
|
|
e7e429142f | ||
|
|
80bb9a1f33 | ||
|
|
774e3af61c | ||
|
|
30373980f1 | ||
|
|
8cf12f685a | ||
|
|
f99cb20d05 | ||
|
|
a7724028b3 | ||
|
|
a0514d2aac | ||
|
|
ac137cee3d | ||
|
|
f4f4ad9c7d | ||
|
|
da7738edaa | ||
|
|
58e091099f | ||
|
|
6410a245eb | ||
|
|
670edd845e | ||
|
|
834c18f9fa | ||
|
|
77930a0cec | ||
|
|
5d9201466b | ||
|
|
d583b5e228 | ||
|
|
c463a9a662 | ||
|
|
bd9f02b689 | ||
|
|
472e2c282d | ||
|
|
7a89779827 | ||
|
|
126de78ce2 | ||
|
|
e919896eef | ||
|
|
fd3475f945 | ||
|
|
9c8f565211 | ||
|
|
08f30224b6 | ||
|
|
6d02554f2a | ||
|
|
61c619f373 | ||
|
|
419d56b4b1 | ||
|
|
b32dd316e1 | ||
|
|
d2cda9770b | ||
|
|
22042b4749 | ||
|
|
aa17eeb6e3 | ||
|
|
1529357821 | ||
|
|
3a98361736 | ||
|
|
0c425dbac3 | ||
|
|
69b861cd87 | ||
|
|
0599d23cc5 | ||
|
|
a37b4a40d9 | ||
|
|
7e37046959 | ||
|
|
f9efdfd98e | ||
|
|
d53090647b | ||
|
|
e8b8a8fe52 | ||
|
|
69430154e6 | ||
|
|
0601305545 | ||
|
|
73d3d15ed8 | ||
|
|
b78be65376 | ||
|
|
5519086c29 | ||
|
|
33b9f33f44 | ||
|
|
0e75793bef | ||
|
|
2f4a362e1b | ||
|
|
cda843d8f6 | ||
|
|
d6a40a35ee | ||
|
|
e111b4cc18 | ||
|
|
cdbd80ceda | ||
|
|
58751bbdf1 | ||
|
|
21334ecdff | ||
|
|
47ea2893c3 | ||
|
|
bbeca28026 | ||
|
|
9ab9f6d709 | ||
|
|
309fde38f7 | ||
|
|
3310d271bf | ||
|
|
4e12b6236e | ||
|
|
4df8157d74 | ||
|
|
37e76e52f6 | ||
|
|
cd824faf64 | ||
|
|
96b11b02e6 | ||
|
|
9f24cb4aa3 | ||
|
|
9e4e23ad3f | ||
|
|
3e96f9c2f1 | ||
|
|
dd9f6ed820 | ||
|
|
f7f1bc8352 | ||
|
|
bf50fbeb6c | ||
|
|
1f7bfb3352 | ||
|
|
406181e887 | ||
|
|
a7b243aff9 | ||
|
|
dc85d44647 | ||
|
|
71621f4ccf | ||
|
|
68deee4a56 | ||
|
|
a20392d13a | ||
|
|
e43292052c | ||
|
|
ee7103c4b0 | ||
|
|
1b1ef80b10 | ||
|
|
33f90fcf6a | ||
|
|
2f1eee390b | ||
|
|
35bf13cfab | ||
|
|
f814fd980b | ||
|
|
3ddfd9c3b0 | ||
|
|
75d8110ad9 | ||
|
|
c958f3f1cb | ||
|
|
06e3a144a3 | ||
|
|
36519c13a8 | ||
|
|
9fb1aaadaf | ||
|
|
d9ac4f2f2d | ||
|
|
e61f3c7a42 | ||
|
|
1b6d51a116 | ||
|
|
0058e88217 | ||
|
|
81b4065921 | ||
|
|
d094d6a081 | ||
|
|
cf90653deb | ||
|
|
bae194e866 | ||
|
|
144e88452e | ||
|
|
89980b1f0e | ||
|
|
16fb25ce5a | ||
|
|
7dea3f19a9 | ||
|
|
e78887001a | ||
|
|
aa4ecdf138 | ||
|
|
9bda83104d | ||
|
|
3783378288 | ||
|
|
937cac454a | ||
|
|
36eb28dc22 | ||
|
|
5aff78a619 | ||
|
|
e6781cb4c3 | ||
|
|
62b391ef68 | ||
|
|
6f1d2d094a | ||
|
|
36690229c5 | ||
|
|
fe5d9cc662 | ||
|
|
f46ec96040 | ||
|
|
88bd35ce98 | ||
|
|
2dc813342a | ||
|
|
fd08f7dd36 | ||
|
|
13f4aa9a80 | ||
|
|
22fba9defc | ||
|
|
fcbb073bcc | ||
|
|
db0dbeb27b | ||
|
|
0e27088e2a | ||
|
|
70a7deaa38 | ||
|
|
514934649e | ||
|
|
ee8e7333d8 | ||
|
|
8d94b6585e | ||
|
|
9fd92ca5ac | ||
|
|
6d9a6051bf | ||
|
|
461c5895a4 | ||
|
|
94fc0416e6 | ||
|
|
ad54a8c334 | ||
|
|
e2b72e1f58 | ||
|
|
e1a9eb4994 | ||
|
|
b0c3ab71c4 | ||
|
|
ce8f72bee9 | ||
|
|
ee4f3e2df4 | ||
|
|
930db2bcc4 | ||
|
|
fadcb39765 | ||
|
|
5034597750 | ||
|
|
f73b48d4ca | ||
|
|
0a7f31333d | ||
|
|
8964fbc821 | ||
|
|
8baa5cb659 | ||
|
|
2e5bea1aee | ||
|
|
759792c447 | ||
|
|
86deab8f6c | ||
|
|
3cf8f151c4 | ||
|
|
4e0945d0ed | ||
|
|
052c8d4ead | ||
|
|
3c3aa6d416 | ||
|
|
eabe06d981 | ||
|
|
8d4c6a8218 | ||
|
|
1a09913a27 | ||
|
|
e8e47746ea | ||
|
|
546cd6596a | ||
|
|
58be8f411a | ||
|
|
990209b624 | ||
|
|
49b1c3db79 | ||
|
|
dfb53895eb | ||
|
|
109967953e | ||
|
|
5ae148f026 | ||
|
|
ddb0d5db26 | ||
|
|
cec5cd7bdc | ||
|
|
63c2d72b2e | ||
|
|
801219374c | ||
|
|
dc3459edd0 | ||
|
|
ace9d86744 | ||
|
|
2d3e34fd5a | ||
|
|
69ad4ae930 | ||
|
|
0037305e16 | ||
|
|
1a205c3f3b | ||
|
|
99966b5a24 | ||
|
|
2437d2be77 | ||
|
|
2043470bd0 | ||
|
|
667285d536 | ||
|
|
1199c1fc52 | ||
|
|
f270e47b48 | ||
|
|
5678860d13 | ||
|
|
b83125a41e | ||
|
|
2dcb131778 | ||
|
|
30083598c6 | ||
|
|
a0599d2658 | ||
|
|
c50db111d6 | ||
|
|
9441295510 | ||
|
|
d4517384dc | ||
|
|
a94ccdb4f5 | ||
|
|
9518764cae | ||
|
|
d174233cd6 | ||
|
|
3d0aa74ffe | ||
|
|
e0a937912c | ||
|
|
8951a8465c | ||
|
|
2fa95b52a0 | ||
|
|
0f84f21bb4 | ||
|
|
a14d5c584e | ||
|
|
b79ff7fbff | ||
|
|
0a0cf26269 | ||
|
|
bd288ce650 | ||
|
|
9f2105b294 | ||
|
|
b3944c678c | ||
|
|
31c8c09060 | ||
|
|
4283671d49 | ||
|
|
c3a3717bde | ||
|
|
5cdb59142f | ||
|
|
bf6f784826 | ||
|
|
33f7fd259b | ||
|
|
bc99e04b57 | ||
|
|
10b8f97e96 | ||
|
|
4713ae5baa | ||
|
|
e47802d1e1 | ||
|
|
77face0f2d | ||
|
|
13f886f07f | ||
|
|
7041cae48c | ||
|
|
b5537bb714 | ||
|
|
7392f6aeff | ||
|
|
c34649cb50 | ||
|
|
fde30b6216 | ||
|
|
e0e9b2ba6f | ||
|
|
250a7da1ee | ||
|
|
f208aa206d | ||
|
|
d472978c1a | ||
|
|
1d0c522fad | ||
|
|
beea3abfc7 | ||
|
|
2dfa7d9542 | ||
|
|
22c90ca7ad | ||
|
|
2563a8f967 | ||
|
|
eb1c119c21 | ||
|
|
27d88a421e | ||
|
|
e4755c624f | ||
|
|
21ef33ea45 | ||
|
|
5b2fdf7e5b | ||
|
|
cf6605746d | ||
|
|
b061f95f6d | ||
|
|
c5a45aaded | ||
|
|
d3fac8ae07 | ||
|
|
013e961c37 | ||
|
|
b899e13763 | ||
|
|
5b3659af7c | ||
|
|
95e29d1046 | ||
|
|
aed0079cad | ||
|
|
67311fe5d6 | ||
|
|
5da1f791a5 | ||
|
|
f216ea5d65 | ||
|
|
fb95a05599 | ||
|
|
b76f01b144 | ||
|
|
35e3294dc4 | ||
|
|
81c2ff501b | ||
|
|
1bfd413040 | ||
|
|
c2ea1b5877 | ||
|
|
4dc4cc2c11 | ||
|
|
830eda7460 | ||
|
|
b69bf3d269 | ||
|
|
cee9906073 | ||
|
|
4ad249f918 | ||
|
|
c915a00bac | ||
|
|
d03d9e5795 | ||
|
|
7cde35d7a3 | ||
|
|
27932854c8 | ||
|
|
a01d689ae2 | ||
|
|
f319841fd1 | ||
|
|
92f9ddfc33 | ||
|
|
86878e3b55 | ||
|
|
df8c696b86 | ||
|
|
650da7ba94 | ||
|
|
efb8ff7bb1 | ||
|
|
16f7031799 | ||
|
|
90e5824f9d | ||
|
|
d7677a8992 | ||
|
|
94390f4a65 | ||
|
|
f59cd158ef | ||
|
|
3967b2e338 | ||
|
|
eb08a3379a | ||
|
|
fee6ad43d6 | ||
|
|
350e651c53 | ||
|
|
d49bad0de5 | ||
|
|
e885cece85 | ||
|
|
9b71c70d1a | ||
|
|
414692083f | ||
|
|
be1e681aa8 |
@@ -21,13 +21,13 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled
|
||||
_APP_OPTIONS_FORCE_HTTPS=disabled
|
||||
_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled
|
||||
_APP_OPENSSL_KEY_V1=your-secret-key
|
||||
_APP_DNS=8.8.8.8
|
||||
_APP_DNS=172.16.238.100 # CoreDNS
|
||||
_APP_DOMAIN=appwrite.test
|
||||
_APP_CONSOLE_DOMAIN=localhost
|
||||
_APP_DOMAIN_FUNCTIONS=functions.localhost
|
||||
_APP_DOMAIN_SITES=sites.localhost
|
||||
_APP_DOMAIN_TARGET_CNAME=test.localhost
|
||||
_APP_DOMAIN_TARGET_A=127.0.0.1
|
||||
_APP_DOMAIN_TARGET_CNAME=cname.localhost
|
||||
_APP_DOMAIN_TARGET_A=203.0.0.1
|
||||
_APP_DOMAIN_TARGET_AAAA=::1
|
||||
_APP_DOMAIN_TARGET_CAA=digicert.com
|
||||
_APP_RULES_FORMAT=md5
|
||||
@@ -69,8 +69,8 @@ _APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||
_APP_SMTP_HOST=maildev
|
||||
_APP_SMTP_PORT=1025
|
||||
_APP_SMTP_SECURE=
|
||||
_APP_SMTP_USERNAME=
|
||||
_APP_SMTP_PASSWORD=
|
||||
_APP_SMTP_USERNAME=user
|
||||
_APP_SMTP_PASSWORD=password
|
||||
_APP_SMS_PROVIDER=sms://username:password@mock
|
||||
_APP_SMS_FROM=+123456789
|
||||
_APP_SMS_PROJECTS_DENY_LIST=
|
||||
@@ -101,6 +101,7 @@ _APP_USAGE_AGGREGATION_INTERVAL=30
|
||||
_APP_STATS_RESOURCES_INTERVAL=30
|
||||
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
|
||||
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
|
||||
_APP_INTERVAL_DOMAIN_VERIFICATION=60
|
||||
_APP_USAGE_STATS=enabled
|
||||
_APP_LOGGING_CONFIG=
|
||||
_APP_LOGGING_CONFIG_REALTIME=
|
||||
@@ -125,4 +126,4 @@ _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10
|
||||
_APP_PROJECT_REGIONS=default
|
||||
_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000
|
||||
_APP_STATS_USAGE_DUAL_WRITING_DBS=database_db_main
|
||||
_APP_TRUSTED_HEADERS=x-forwarded-for
|
||||
_APP_TRUSTED_HEADERS=x-forwarded-for
|
||||
|
||||
+11
-11
@@ -223,7 +223,7 @@ jobs:
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group abuseEnabled,screenshots
|
||||
|
||||
- name: Failure Logs
|
||||
if: failure()
|
||||
@@ -312,7 +312,7 @@ jobs:
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group abuseEnabled,screenshots
|
||||
|
||||
- name: Failure Logs
|
||||
if: failure()
|
||||
@@ -322,8 +322,8 @@ jobs:
|
||||
echo "=== OpenRuntimes Executor Logs ==="
|
||||
docker compose logs openruntimes-executor
|
||||
|
||||
e2e_dev_keys:
|
||||
name: E2E Service Test (Dev Keys)
|
||||
e2e_abuse_enabled:
|
||||
name: E2E Service Test (Abuse enabled)
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
@@ -344,7 +344,7 @@ jobs:
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Run Projects tests with dev keys in dedicated table mode
|
||||
- name: Run Projects tests in dedicated table mode
|
||||
run: |
|
||||
echo "Using project tables"
|
||||
export _APP_DATABASE_SHARED_TABLES=
|
||||
@@ -354,7 +354,7 @@ jobs:
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=abuseEnabled
|
||||
|
||||
- name: Failure Logs
|
||||
if: failure()
|
||||
@@ -364,8 +364,8 @@ jobs:
|
||||
echo "=== OpenRuntimes Executor Logs ==="
|
||||
docker compose logs openruntimes-executor
|
||||
|
||||
e2e_dev_keys_shared_mode:
|
||||
name: E2E Shared Mode Service Test (Dev Keys)
|
||||
e2e_abuse_enabled_shared_mode:
|
||||
name: E2E Shared Mode Service Test (Abuse enabled)
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ setup, check_database_changes ]
|
||||
if: needs.check_database_changes.outputs.database_changed == 'true'
|
||||
@@ -394,7 +394,7 @@ jobs:
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Run Projects tests with dev keys in ${{ matrix.tables-mode }} table mode
|
||||
- name: Run Projects tests in ${{ matrix.tables-mode }} table mode
|
||||
run: |
|
||||
if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then
|
||||
echo "Using shared tables V1"
|
||||
@@ -410,7 +410,7 @@ jobs:
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=abuseEnabled
|
||||
|
||||
- name: Failure Logs
|
||||
if: failure()
|
||||
@@ -420,7 +420,7 @@ jobs:
|
||||
echo "=== OpenRuntimes Executor Logs ==="
|
||||
docker compose logs openruntimes-executor
|
||||
|
||||
e2e_screenshots_keys:
|
||||
e2e_screenshots:
|
||||
name: E2E Service Test (Site Screenshots)
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# AGENTS.md
|
||||
|
||||
Appwrite is an end-to-end backend server for web, mobile, native, and backend apps. This guide provides context and instructions for AI coding agents working on the Appwrite codebase.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Appwrite is a self-hosted Backend-as-a-Service (BaaS) platform that provides developers with a set of APIs and tools to build secure, scalable applications. The project uses a hybrid monolithic-microservice architecture built with PHP, running on Swoole for high performance.
|
||||
|
||||
**Key Technologies:**
|
||||
- **Backend:** PHP 8.3+, Swoole
|
||||
- **Libraries:** Utopia PHP
|
||||
- **Database:** MariaDB, Redis
|
||||
- **Cache:** Redis
|
||||
- **Queue:** Redis
|
||||
- **Containers:** Docker
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Run Appwrite
|
||||
docker compose up -d --force-recreate --build
|
||||
|
||||
# Run specific test
|
||||
docker compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName] --filter=[FunctionName]
|
||||
|
||||
# Format code
|
||||
composer format
|
||||
```
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
- Follow [PSR-12](https://www.php-fig.org/psr/psr-12/) coding standard
|
||||
- Use PSR-4 autoloading
|
||||
- Strict type declarations where applicable
|
||||
- Comprehensive PHPDoc comments
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
#### `resourceType` Naming Rule
|
||||
|
||||
When a collection has a combination of `resourceType`, `resourceId`, and/or `resourceInternalId`, the value of `resourceType` MUST always be **plural** - for example: `functions`, `sites`, `deployments`.
|
||||
|
||||
Examples:
|
||||
```php
|
||||
'resourceType' => 'functions'
|
||||
'resourceType' => 'sites'
|
||||
'resourceType' => 'deployments'
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Critical Security Practices
|
||||
|
||||
- **Never hardcode credentials** - Use environment variables
|
||||
- **Rate limiting** - Respect abuse prevention mechanisms
|
||||
|
||||
## Dependencies
|
||||
|
||||
Avoid introducing new dependencies other than utopia-php.
|
||||
|
||||
## Adding new endpoints
|
||||
|
||||
When adding new endpoints, make sure to use modules and follow its patterns. Find instruction in [Modules AGENTS.md](src/Appwrite/Platform/AGENTS.md) file.
|
||||
|
||||
## Pull Request Guidelines
|
||||
### Before Submitting
|
||||
|
||||
- Run `composer format`
|
||||
- Update documentation if adding features
|
||||
- Add/update tests for your changes
|
||||
- Check that Docker build succeeds
|
||||
`docs/specs/authentication.drawio.svg`
|
||||
|
||||
## Known Issues and Gotchas
|
||||
|
||||
- **Hot Reload:** Code changes require container restart in some cases
|
||||
- **Logging:** There is no central place for logs, so when debugging, ensure to check all possibly relevant containers
|
||||
+98
@@ -1,3 +1,101 @@
|
||||
# Version 1.8.1
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Notable changes
|
||||
|
||||
* Add branch deployments support in [#10486](https://github.com/appwrite/appwrite/pull/10486)
|
||||
* Add TanStack Start sites support in [#10681](https://github.com/appwrite/appwrite/pull/10681)
|
||||
* Add Next.js standalone support in [#10747](https://github.com/appwrite/appwrite/pull/10747)
|
||||
* Add Resend integration in [#10690](https://github.com/appwrite/appwrite/pull/10690)
|
||||
* Add option to enable/disable image transformations per-bucket in [#10722](https://github.com/appwrite/appwrite/pull/10722)
|
||||
* Add operators support in [#10735](https://github.com/appwrite/appwrite/pull/10735) and [#10800](https://github.com/appwrite/appwrite/pull/10800)
|
||||
* Add function and sites stats in [#10786](https://github.com/appwrite/appwrite/pull/10786)
|
||||
* Add disable count feature in [#10668](https://github.com/appwrite/appwrite/pull/10668)
|
||||
* Add ElevenLabs site template in [#10782](https://github.com/appwrite/appwrite/pull/10782)
|
||||
* Add suggested environment variables in [#10795](https://github.com/appwrite/appwrite/pull/10795)
|
||||
* Update GeoDB database in [#10890](https://github.com/appwrite/appwrite/pull/10890)
|
||||
* Update Flutter default build runtime in [#10807](https://github.com/appwrite/appwrite/pull/10807)
|
||||
* Upgrade runtimes in [#10804](https://github.com/appwrite/appwrite/pull/10804)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix duplicate document error while creating file in [#10891](https://github.com/appwrite/appwrite/pull/10891)
|
||||
* Fix "Update external deployment (authorize)" throwing 500 error due to invalid query in [#10888](https://github.com/appwrite/appwrite/pull/10888)
|
||||
* Fix error setting user password in [#10889](https://github.com/appwrite/appwrite/pull/10889)
|
||||
* Fix error generating email MFA challenges in [#10884](https://github.com/appwrite/appwrite/pull/10884)
|
||||
* Fix file token expiry in [#10877](https://github.com/appwrite/appwrite/pull/10877)
|
||||
* Fix TanStack Nitro default in [#10860](https://github.com/appwrite/appwrite/pull/10860)
|
||||
* Fix TanStack builds in [#10767](https://github.com/appwrite/appwrite/pull/10767)
|
||||
* Fix nullable validation in [#10819](https://github.com/appwrite/appwrite/pull/10819) and [#10778](https://github.com/appwrite/appwrite/pull/10778)
|
||||
* Fix WebP library in [#10738](https://github.com/appwrite/appwrite/pull/10738)
|
||||
* Fix batch writes in [#10812](https://github.com/appwrite/appwrite/pull/10812)
|
||||
* Fix error handler error in [#10719](https://github.com/appwrite/appwrite/pull/10719)
|
||||
* Fix Next 16 compatibility in [#10713](https://github.com/appwrite/appwrite/pull/10713)
|
||||
* Fix stats usage memory leak in [#10683](https://github.com/appwrite/appwrite/pull/10683)
|
||||
* Fix author URL in template deployments in [#10535](https://github.com/appwrite/appwrite/pull/10535)
|
||||
* Fix VCS lock deletion in [#10691](https://github.com/appwrite/appwrite/pull/10691)
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Add CSV export functionality in [#10546](https://github.com/appwrite/appwrite/pull/10546), [#10750](https://github.com/appwrite/appwrite/pull/10750), [#10813](https://github.com/appwrite/appwrite/pull/10813), and [#10847](https://github.com/appwrite/appwrite/pull/10847)
|
||||
* Add JWT disposition in [#10867](https://github.com/appwrite/appwrite/pull/10867)
|
||||
* Add screenshots endpoint in [#10675](https://github.com/appwrite/appwrite/pull/10675)
|
||||
* Add screenshot endpoint stats in [#10706](https://github.com/appwrite/appwrite/pull/10706)
|
||||
* Add users attributes in [#10688](https://github.com/appwrite/appwrite/pull/10688)
|
||||
* Add max build duration environment variable in [#10674](https://github.com/appwrite/appwrite/pull/10674)
|
||||
* Add custom realtime logger in [#10871](https://github.com/appwrite/appwrite/pull/10871)
|
||||
* Add logs in [#10869](https://github.com/appwrite/appwrite/pull/10869)
|
||||
* Improve MFA docs endpoint order in [#10793](https://github.com/appwrite/appwrite/pull/10793)
|
||||
* Auth refactor in [#10758](https://github.com/appwrite/appwrite/pull/10758), [#10837](https://github.com/appwrite/appwrite/pull/10837), [#10682](https://github.com/appwrite/appwrite/pull/10682), and [#10667](https://github.com/appwrite/appwrite/pull/10667)
|
||||
* Bump assistant to 0.8.4 in [#10887](https://github.com/appwrite/appwrite/pull/10887)
|
||||
* Bump database to 3.1.5 in [#10766](https://github.com/appwrite/appwrite/pull/10766)
|
||||
* Bump Utopia DNS in [#10761](https://github.com/appwrite/appwrite/pull/10761)
|
||||
* Update domains to 0.8.3 in [#10658](https://github.com/appwrite/appwrite/pull/10658)
|
||||
* Update domains to 0.9.1 in [#10678](https://github.com/appwrite/appwrite/pull/10678)
|
||||
* Update Apple Swift to 13.3.0 in [#10679](https://github.com/appwrite/appwrite/pull/10679)
|
||||
* Update Apple Swift in [#10663](https://github.com/appwrite/appwrite/pull/10663)
|
||||
* Update CLI to 10.2.2 in [#10672](https://github.com/appwrite/appwrite/pull/10672)
|
||||
* Update to CLI 12.0.0 in [#10853](https://github.com/appwrite/appwrite/pull/10853)
|
||||
* Update docs examples to use Permission class in [#10707](https://github.com/appwrite/appwrite/pull/10707)
|
||||
* Update SDK examples docs in [#10855](https://github.com/appwrite/appwrite/pull/10855)
|
||||
* Release Python SDK in [#10762](https://github.com/appwrite/appwrite/pull/10762)
|
||||
* Release Flutter 20.3.2 in [#10838](https://github.com/appwrite/appwrite/pull/10838)
|
||||
* Release Flutter/Dart add screenshot examples in [#10811](https://github.com/appwrite/appwrite/pull/10811)
|
||||
* Release PHP CLI in [#10791](https://github.com/appwrite/appwrite/pull/10791)
|
||||
* Release SDKs in [#10817](https://github.com/appwrite/appwrite/pull/10817)
|
||||
* Update SDKs in [#10694](https://github.com/appwrite/appwrite/pull/10694), [#10729](https://github.com/appwrite/appwrite/pull/10729), and [#10744](https://github.com/appwrite/appwrite/pull/10744)
|
||||
* Update SDK generator in [#10743](https://github.com/appwrite/appwrite/pull/10743)
|
||||
* Update database in [#10664](https://github.com/appwrite/appwrite/pull/10664)
|
||||
* Update README file in [#10763](https://github.com/appwrite/appwrite/pull/10763)
|
||||
* SDK release documentation in [#10745](https://github.com/appwrite/appwrite/pull/10745)
|
||||
* SDK release runtime config in [#10765](https://github.com/appwrite/appwrite/pull/10765)
|
||||
* Sync specs in [#10789](https://github.com/appwrite/appwrite/pull/10789)
|
||||
* Sync 1.8.0 in [#10677](https://github.com/appwrite/appwrite/pull/10677)
|
||||
* Add workflow for issue triage in [#10718](https://github.com/appwrite/appwrite/pull/10718)
|
||||
* Add issue auto-labeler in [#10700](https://github.com/appwrite/appwrite/pull/10700)
|
||||
* Add AI moderator repo in [#10717](https://github.com/appwrite/appwrite/pull/10717)
|
||||
* Browser bump in [#10850](https://github.com/appwrite/appwrite/pull/10850)
|
||||
* Template type enum override in [#10848](https://github.com/appwrite/appwrite/pull/10848)
|
||||
* VCS reference type in [#10852](https://github.com/appwrite/appwrite/pull/10852)
|
||||
* Index scope description in [#10851](https://github.com/appwrite/appwrite/pull/10851)
|
||||
* Config for environment in [#10833](https://github.com/appwrite/appwrite/pull/10833)
|
||||
* Format instance in [#10830](https://github.com/appwrite/appwrite/pull/10830)
|
||||
* Replace sleep in webhooks service in [#10656](https://github.com/appwrite/appwrite/pull/10656)
|
||||
* Update email composer in [#10720](https://github.com/appwrite/appwrite/pull/10720)
|
||||
* Update facts on GitHub sites and functions in [#10593](https://github.com/appwrite/appwrite/pull/10593) and [#10771](https://github.com/appwrite/appwrite/pull/10771)
|
||||
* Fix wrong user type in [#10875](https://github.com/appwrite/appwrite/pull/10875)
|
||||
* Fix limit and offset computation in [#10880](https://github.com/appwrite/appwrite/pull/10880)
|
||||
* Fix enum examples in [#10828](https://github.com/appwrite/appwrite/pull/10828)
|
||||
* Fix response models multi-methods in [#10815](https://github.com/appwrite/appwrite/pull/10815)
|
||||
* Fix undefined variable in [#10654](https://github.com/appwrite/appwrite/pull/10654)
|
||||
* Fix undefined sequence in [#10652](https://github.com/appwrite/appwrite/pull/10652)
|
||||
* Fix description in [#10702](https://github.com/appwrite/appwrite/pull/10702)
|
||||
* Fix warning in builds worker in [#10705](https://github.com/appwrite/appwrite/pull/10705)
|
||||
* Fix sites create deployment docs in [#10566](https://github.com/appwrite/appwrite/pull/10566)
|
||||
* Fix test dependencies projects in [#10655](https://github.com/appwrite/appwrite/pull/10655)
|
||||
* Fix list sites test in [#10726](https://github.com/appwrite/appwrite/pull/10726)
|
||||
|
||||
# Version 1.8.0
|
||||
|
||||
## What's Changed
|
||||
|
||||
+4
-1
@@ -57,6 +57,7 @@ RUN mkdir -p /storage/uploads && \
|
||||
# Executables
|
||||
RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/install && \
|
||||
chmod +x /usr/local/bin/interval && \
|
||||
chmod +x /usr/local/bin/maintenance && \
|
||||
chmod +x /usr/local/bin/migrate && \
|
||||
chmod +x /usr/local/bin/realtime && \
|
||||
@@ -86,7 +87,9 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/worker-webhooks && \
|
||||
chmod +x /usr/local/bin/worker-stats-usage && \
|
||||
chmod +x /usr/local/bin/stats-resources && \
|
||||
chmod +x /usr/local/bin/worker-stats-resources
|
||||
chmod +x /usr/local/bin/worker-stats-resources && \
|
||||
chmod +x /usr/local/bin/worker-payments-usage && \
|
||||
chmod +x /usr/local/bin/schedule-payments-usage
|
||||
|
||||
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
|
||||
|
||||
|
||||
+3
-3
@@ -72,7 +72,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.8.0
|
||||
appwrite/appwrite:1.8.1
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -84,7 +84,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.8.0
|
||||
appwrite/appwrite:1.8.1
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -94,7 +94,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.8.0
|
||||
appwrite/appwrite:1.8.1
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
@@ -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.8.0
|
||||
appwrite/appwrite:1.8.1
|
||||
```
|
||||
|
||||
### 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.8.0
|
||||
appwrite/appwrite:1.8.1
|
||||
```
|
||||
|
||||
#### 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.8.0
|
||||
appwrite/appwrite:1.8.1
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
+12
@@ -5,6 +5,7 @@ require_once __DIR__ . '/init.php';
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\PaymentsUsage;
|
||||
use Appwrite\Event\StatsResources;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
@@ -228,6 +229,9 @@ CLI::setResource('queueForDeletes', function (Publisher $publisher) {
|
||||
CLI::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
return new Certificate($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('queueForPaymentsUsage', function (Publisher $publisher) {
|
||||
return new PaymentsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('logError', function (Registry $register) {
|
||||
return function (Throwable $error, string $namespace, string $action) use ($register) {
|
||||
Console::error('[Error] Timestamp: ' . date('c', time()));
|
||||
@@ -257,6 +261,14 @@ CLI::setResource('logError', function (Registry $register) {
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
if ($error->getPrevious() !== null) {
|
||||
if ($error->getPrevious()->getMessage() != $error->getMessage()) {
|
||||
$log->addExtra('previousMessage', $error->getPrevious()->getMessage());
|
||||
}
|
||||
$log->addExtra('previousFile', $error->getPrevious()->getFile());
|
||||
$log->addExtra('previousLine', $error->getPrevious()->getLine());
|
||||
}
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
|
||||
@@ -6,7 +6,7 @@ use Utopia\Database\Helpers\ID;
|
||||
|
||||
$providers = Config::getParam('oAuthProviders', []);
|
||||
|
||||
return [
|
||||
$platformCollections = [
|
||||
'projects' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('projects'),
|
||||
@@ -232,6 +232,17 @@ return [
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('payments'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 65536,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'array' => false,
|
||||
'filters' => ['json', 'encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('auths'),
|
||||
'type' => Database::VAR_STRING,
|
||||
@@ -330,7 +341,18 @@ return [
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('labels'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
@@ -643,6 +665,39 @@ return [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'resourceType',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'resourceId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'resourceInternalId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('name'),
|
||||
'type' => Database::VAR_STRING,
|
||||
@@ -718,6 +773,13 @@ return [
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_resource',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceType', 'resourceInternalId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_accessedAt',
|
||||
'type' => Database::INDEX_KEY,
|
||||
@@ -1317,6 +1379,17 @@ return [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('logs'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 1000000,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
@@ -1351,21 +1424,21 @@ return [
|
||||
'$id' => '_key_type',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['type'],
|
||||
'lengths' => [32],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_trigger',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['trigger'],
|
||||
'lengths' => [32],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_deploymentResourceType',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deploymentResourceType'],
|
||||
'lengths' => [32],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
@@ -1407,23 +1480,23 @@ return [
|
||||
'$id' => ID::custom('_key_owner'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['owner'],
|
||||
'lengths' => [16],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_region'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region'],
|
||||
'lengths' => [16],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_piid_riid_rt'),
|
||||
'$id' => ID::custom('_key_piid_diid_drt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'deploymentInternalId', 'deploymentResourceType'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_region_status_createdAt',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region', 'status', '$createdAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1892,3 +1965,31 @@ return [
|
||||
'indexes' => []
|
||||
],
|
||||
];
|
||||
|
||||
// Organization API keys subquery
|
||||
$platformCollections['teams']['attributes'][] = [
|
||||
'$id' => ID::custom('keys'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['subQueryOrganizationKeys'],
|
||||
];
|
||||
|
||||
// Account API keys subquery
|
||||
$platformCollections['users']['attributes'][] = [
|
||||
'$id' => ID::custom('keys'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['subQueryAccountKeys'],
|
||||
];
|
||||
|
||||
return $platformCollections;
|
||||
|
||||
@@ -2645,4 +2645,125 @@ return [
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'payments_plans' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('payments_plans'),
|
||||
'name' => 'Payments Plans',
|
||||
'attributes' => [
|
||||
[ '$id' => ID::custom('planId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('name'), 'type' => Database::VAR_STRING, 'size' => 2048, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('description'), 'type' => Database::VAR_STRING, 'size' => 8192, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('pricing'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
[ '$id' => ID::custom('isDefault'), 'type' => Database::VAR_BOOLEAN, 'size' => 0, 'signed' => true, 'required' => false, 'default' => false, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('isFree'), 'type' => Database::VAR_BOOLEAN, 'size' => 0, 'signed' => true, 'required' => false, 'default' => false, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('status'), 'type' => Database::VAR_STRING, 'size' => 32, 'signed' => true, 'required' => false, 'default' => 'active', 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('providers'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
[ '$id' => ID::custom('features'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
[ '$id' => ID::custom('search'), 'type' => Database::VAR_STRING, 'size' => 16384, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
],
|
||||
'indexes' => [
|
||||
[ '$id' => ID::custom('_key_plan'), 'type' => Database::INDEX_UNIQUE, 'attributes' => ['planId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_status'), 'type' => Database::INDEX_KEY, 'attributes' => ['status'], 'lengths' => [32], 'orders' => [Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_fulltext_search'), 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], 'lengths' => [], 'orders' => [] ],
|
||||
],
|
||||
],
|
||||
|
||||
'payments_features' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('payments_features'),
|
||||
'name' => 'Payments Features',
|
||||
'attributes' => [
|
||||
[ '$id' => ID::custom('featureId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('name'), 'type' => Database::VAR_STRING, 'size' => 2048, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('type'), 'type' => Database::VAR_STRING, 'size' => 32, 'signed' => true, 'required' => true, 'default' => 'boolean', 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('description'), 'type' => Database::VAR_STRING, 'size' => 8192, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('providers'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
],
|
||||
'indexes' => [
|
||||
[ '$id' => ID::custom('_key_feature'), 'type' => Database::INDEX_UNIQUE, 'attributes' => ['featureId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_type'), 'type' => Database::INDEX_KEY, 'attributes' => ['type'], 'lengths' => [32], 'orders' => [Database::ORDER_ASC] ],
|
||||
],
|
||||
],
|
||||
|
||||
'payments_plan_features' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('payments_plan_features'),
|
||||
'name' => 'Payments Plan Features',
|
||||
'attributes' => [
|
||||
[ '$id' => ID::custom('planId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('featureId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('type'), 'type' => Database::VAR_STRING, 'size' => 32, 'signed' => true, 'required' => true, 'default' => 'boolean', 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('enabled'), 'type' => Database::VAR_BOOLEAN, 'size' => 0, 'signed' => true, 'required' => false, 'default' => true, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('currency'), 'type' => Database::VAR_STRING, 'size' => 8, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('interval'), 'type' => Database::VAR_STRING, 'size' => 16, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('includedUnits'), 'type' => Database::VAR_INTEGER, 'size' => 0, 'signed' => false, 'required' => false, 'default' => 0, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('tiersMode'), 'type' => Database::VAR_STRING, 'size' => 16, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('tiers'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
[ '$id' => ID::custom('usageCap'), 'type' => Database::VAR_INTEGER, 'size' => 0, 'signed' => false, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('overagePrice'), 'type' => Database::VAR_INTEGER, 'size' => 0, 'signed' => false, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('providers'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
[ '$id' => ID::custom('metadata'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
],
|
||||
'indexes' => [
|
||||
[ '$id' => ID::custom('_key_unique'), 'type' => Database::INDEX_UNIQUE, 'attributes' => ['planId','featureId'], 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_type'), 'type' => Database::INDEX_KEY, 'attributes' => ['type'], 'lengths' => [16], 'orders' => [Database::ORDER_ASC] ],
|
||||
],
|
||||
],
|
||||
|
||||
'payments_subscriptions' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('payments_subscriptions'),
|
||||
'name' => 'Payments Subscriptions',
|
||||
'attributes' => [
|
||||
[ '$id' => ID::custom('subscriptionId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('providerSubscriptionId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('providerCheckoutId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('actorType'), 'type' => Database::VAR_STRING, 'size' => 16, 'signed' => true, 'required' => true, 'default' => 'user', 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('actorId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('actorInternalId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('planId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('priceId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('status'), 'type' => Database::VAR_STRING, 'size' => 32, 'signed' => true, 'required' => false, 'default' => 'active', 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('trialEndsAt'), 'type' => Database::VAR_DATETIME, 'size' => 0, 'signed' => false, 'required' => false, 'default' => null, 'array' => false, 'filters' => ['datetime'] ],
|
||||
[ '$id' => ID::custom('currentPeriodStart'), 'type' => Database::VAR_DATETIME, 'size' => 0, 'signed' => false, 'required' => false, 'default' => null, 'array' => false, 'filters' => ['datetime'] ],
|
||||
[ '$id' => ID::custom('currentPeriodEnd'), 'type' => Database::VAR_DATETIME, 'size' => 0, 'signed' => false, 'required' => false, 'default' => null, 'array' => false, 'filters' => ['datetime'] ],
|
||||
[ '$id' => ID::custom('cancelAtPeriodEnd'), 'type' => Database::VAR_BOOLEAN, 'size' => 0, 'signed' => true, 'required' => false, 'default' => false, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('canceledAt'), 'type' => Database::VAR_DATETIME, 'size' => 0, 'signed' => false, 'required' => false, 'default' => null, 'array' => false, 'filters' => ['datetime'] ],
|
||||
[ '$id' => ID::custom('providers'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
[ '$id' => ID::custom('usageSummary'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
[ '$id' => ID::custom('tags'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => false, 'default' => [], 'array' => true, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('search'), 'type' => Database::VAR_STRING, 'size' => 16384, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
],
|
||||
'indexes' => [
|
||||
[ '$id' => ID::custom('_key_actor'), 'type' => Database::INDEX_KEY, 'attributes' => ['actorType','actorId'], 'lengths' => [16, Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_status'), 'type' => Database::INDEX_KEY, 'attributes' => ['status'], 'lengths' => [32], 'orders' => [Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_plan'), 'type' => Database::INDEX_KEY, 'attributes' => ['planId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_provider_subscription'), 'type' => Database::INDEX_KEY, 'attributes' => ['providerSubscriptionId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_provider_checkout'), 'type' => Database::INDEX_KEY, 'attributes' => ['providerCheckoutId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_fulltext_search'), 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], 'lengths' => [], 'orders' => [] ],
|
||||
],
|
||||
],
|
||||
|
||||
'payments_usage_events' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('payments_usage_events'),
|
||||
'name' => 'Payments Usage Events',
|
||||
'attributes' => [
|
||||
[ '$id' => ID::custom('subscriptionId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('actorType'), 'type' => Database::VAR_STRING, 'size' => 16, 'signed' => true, 'required' => true, 'default' => 'user', 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('actorId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('planId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('featureId'), 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, 'signed' => true, 'required' => true, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('quantity'), 'type' => Database::VAR_INTEGER, 'size' => 0, 'signed' => false, 'required' => true, 'default' => 0, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('timestamp'), 'type' => Database::VAR_DATETIME, 'size' => 0, 'signed' => false, 'required' => true, 'default' => null, 'array' => false, 'filters' => ['datetime'] ],
|
||||
[ '$id' => ID::custom('providerSyncState'), 'type' => Database::VAR_STRING, 'size' => 32, 'signed' => true, 'required' => false, 'default' => 'pending', 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('providerEventId'), 'type' => Database::VAR_STRING, 'size' => 256, 'signed' => true, 'required' => false, 'default' => null, 'array' => false, 'filters' => [] ],
|
||||
[ '$id' => ID::custom('metadata'), 'type' => Database::VAR_STRING, 'size' => 65536, 'signed' => true, 'required' => false, 'default' => [], 'array' => false, 'filters' => ['json'] ],
|
||||
],
|
||||
'indexes' => [
|
||||
[ '$id' => ID::custom('_key_subscription_feature_time'), 'type' => Database::INDEX_KEY, 'attributes' => ['subscriptionId','featureId','timestamp'], 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY, 0], 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC, Database::ORDER_ASC] ],
|
||||
[ '$id' => ID::custom('_key_sync_state'), 'type' => Database::INDEX_KEY, 'attributes' => ['providerSyncState'], 'lengths' => [32], 'orders' => [Database::ORDER_ASC] ],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1329,4 +1329,46 @@ return [
|
||||
'description' => 'Target has an invalid provider type.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Payments */
|
||||
Exception::PAYMENT_PLAN_NOT_FOUND => [
|
||||
'name' => Exception::PAYMENT_PLAN_NOT_FOUND,
|
||||
'description' => 'Payment plan with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::PAYMENT_PLAN_ALREADY_EXISTS => [
|
||||
'name' => Exception::PAYMENT_PLAN_ALREADY_EXISTS,
|
||||
'description' => 'Payment plan with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::PAYMENT_SUBSCRIPTION_NOT_FOUND => [
|
||||
'name' => Exception::PAYMENT_SUBSCRIPTION_NOT_FOUND,
|
||||
'description' => 'Payment subscription with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::PAYMENT_SUBSCRIPTION_ALREADY_EXISTS => [
|
||||
'name' => Exception::PAYMENT_SUBSCRIPTION_ALREADY_EXISTS,
|
||||
'description' => 'Payment subscription already exists for this actor.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::PAYMENT_PROVIDER_NOT_CONFIGURED => [
|
||||
'name' => Exception::PAYMENT_PROVIDER_NOT_CONFIGURED,
|
||||
'description' => 'No payment provider has been configured for this project.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::PAYMENT_PROVIDER_ALREADY_CONFIGURED => [
|
||||
'name' => Exception::PAYMENT_PROVIDER_ALREADY_CONFIGURED,
|
||||
'description' => 'Payment provider is already configured. Disconnect the existing provider before configuring a new one.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::PAYMENT_WEBHOOK_FAILED => [
|
||||
'name' => Exception::PAYMENT_WEBHOOK_FAILED,
|
||||
'description' => 'Failed to create payment provider webhook.',
|
||||
'code' => 500,
|
||||
],
|
||||
Exception::PAYMENT_FEATURE_NOT_FOUND => [
|
||||
'name' => Exception::PAYMENT_FEATURE_NOT_FOUND,
|
||||
'description' => 'Payment feature with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
<img
|
||||
height="26px"
|
||||
src="{{logoUrl}}"
|
||||
alt="Appwrite logo"
|
||||
alt="{{platform}} logo"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -225,7 +225,7 @@
|
||||
<tr>
|
||||
<td style="padding-left: 4px; padding-right: 4px">
|
||||
<a
|
||||
href="{{twitterUrl}}"
|
||||
href="{{twitter}}"
|
||||
class="social-icon"
|
||||
title="Twitter"
|
||||
>
|
||||
@@ -234,7 +234,7 @@
|
||||
</td>
|
||||
<td style="padding-left: 4px; padding-right: 4px">
|
||||
<a
|
||||
href="{{discordUrl}}"
|
||||
href="{{discord}}"
|
||||
class="social-icon"
|
||||
>
|
||||
<img src="https://cloud.appwrite.io/images/mails/discord.png" height="24" width="24" />
|
||||
@@ -242,7 +242,7 @@
|
||||
</td>
|
||||
<td style="padding-left: 4px; padding-right: 4px">
|
||||
<a
|
||||
href="{{githubUrl}}"
|
||||
href="{{github}}"
|
||||
class="social-icon"
|
||||
>
|
||||
<img src="https://cloud.appwrite.io/images/mails/github.png" height="24" width="24" />
|
||||
@@ -252,15 +252,15 @@
|
||||
</table>
|
||||
<table style="width: auto; margin: 0 auto; margin-top: 60px">
|
||||
<tr>
|
||||
<td><a href="{{termsUrl}}">Terms</a></td>
|
||||
<td><a href="{{terms}}">Terms</a></td>
|
||||
<td style="color: #e8e9f0">
|
||||
<div style="margin: 0 8px">|</div>
|
||||
</td>
|
||||
<td><a href="{{privacyUrl}}">Privacy</a></td>
|
||||
<td><a href="{{privacy}}">Privacy</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p style="text-align: center" align="center">
|
||||
© {{year}} Appwrite | 251 Little Falls Drive, Wilmington 19808,
|
||||
© {{year}} {{platform}} | 251 Little Falls Drive, Wilmington 19808,
|
||||
Delaware, United States
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -462,4 +462,15 @@ return [
|
||||
'mock' => true,
|
||||
'class' => 'Appwrite\\Auth\\OAuth2\\Mock',
|
||||
],
|
||||
'mock-unverified' => [
|
||||
'name' => 'MockUnverified',
|
||||
'developers' => 'https://appwrite.io',
|
||||
'icon' => 'icon-appwrite',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => true,
|
||||
'class' => 'Appwrite\\Auth\\OAuth2\\MockUnverified',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -22,4 +22,5 @@ return [
|
||||
'termsUrl' => APP_EMAIL_TERMS_URL,
|
||||
'privacyUrl' => APP_EMAIL_PRIVACY_URL,
|
||||
'websiteUrl' => 'https://' . APP_DOMAIN,
|
||||
'emailSenderName' => APP_EMAIL_PLATFORM_NAME,
|
||||
];
|
||||
|
||||
@@ -58,6 +58,8 @@ $admins = [
|
||||
'projects.write',
|
||||
'keys.read',
|
||||
'keys.write',
|
||||
'devKeys.read',
|
||||
'devKeys.write',
|
||||
'webhooks.read',
|
||||
'webhooks.write',
|
||||
'locale.read',
|
||||
|
||||
@@ -166,4 +166,13 @@ return [ // List of publicly visible scopes
|
||||
'tokens.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s tokens',
|
||||
],
|
||||
'payments.read' => [
|
||||
'description' => 'Access to read your project\'s payments data (plans, subscriptions, usage)'
|
||||
],
|
||||
'payments.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s payments resources'
|
||||
],
|
||||
'payments.subscribe' => [
|
||||
'description' => 'Access to create and manage subscriptions as an actor (user/team)'
|
||||
],
|
||||
];
|
||||
|
||||
+11
-9
@@ -116,6 +116,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'namespace' => 'io.appwrite',
|
||||
'version' => '11.4.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
@@ -262,7 +263,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '21.0.0',
|
||||
'version' => '21.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -281,7 +282,7 @@ return [
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '19.0.0',
|
||||
'version' => '19.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -300,7 +301,7 @@ return [
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '14.0.0',
|
||||
'version' => '14.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -319,7 +320,7 @@ return [
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '20.0.0',
|
||||
'version' => '20.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -338,7 +339,7 @@ return [
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => 'v0.15.0',
|
||||
'version' => 'v0.16.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
@@ -357,7 +358,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.23.0',
|
||||
'version' => '0.24.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -376,7 +377,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '20.0.0',
|
||||
'version' => '20.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -395,7 +396,8 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '13.0.0',
|
||||
'namespace' => 'io.appwrite',
|
||||
'version' => '13.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
@@ -418,7 +420,7 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '14.0.0',
|
||||
'version' => '14.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
|
||||
+17
-4
@@ -20,7 +20,7 @@ return [
|
||||
'name' => 'Console',
|
||||
'subtitle' => '',
|
||||
'description' => '',
|
||||
'controller' => 'web/console.php',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => false,
|
||||
'docs' => false,
|
||||
'docsUrl' => '',
|
||||
@@ -146,7 +146,7 @@ return [
|
||||
'name' => 'Storage',
|
||||
'subtitle' => 'The Storage service allows you to manage your project files.',
|
||||
'description' => '/docs/services/storage.md',
|
||||
'controller' => 'api/storage.php',
|
||||
'controller' => '',
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => 'https://appwrite.io/docs/client/storage',
|
||||
@@ -225,6 +225,19 @@ return [
|
||||
'icon' => '/images/services/functions.png',
|
||||
'platforms' => ['client', 'server', 'console'],
|
||||
],
|
||||
'payments' => [
|
||||
'key' => 'payments',
|
||||
'name' => 'Payments',
|
||||
'subtitle' => 'Create and manage plans, subscriptions, usage and billing providers.',
|
||||
'description' => '/docs/services/payments.md',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => '',
|
||||
'tests' => false,
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/databases.png',
|
||||
],
|
||||
'proxy' => [
|
||||
'key' => 'proxy',
|
||||
'name' => 'Proxy',
|
||||
@@ -270,9 +283,9 @@ return [
|
||||
'console' => [
|
||||
'key' => 'console',
|
||||
'name' => 'Console',
|
||||
'subtitle' => 'The Console service allows you to interact with console relevant informations.',
|
||||
'subtitle' => 'The Console service allows you to interact with console relevant information.',
|
||||
'description' => '',
|
||||
'controller' => 'api/console.php',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => '',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+168
-74
@@ -37,7 +37,7 @@ use libphonenumber\PhoneNumberUtil;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit as EventAudit;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Code as ProofsCode;
|
||||
use Utopia\Auth\Proofs\Password as ProofsPassword;
|
||||
@@ -68,13 +68,14 @@ use Utopia\Validator;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
$oauthDefaultSuccess = '/console/auth/oauth2/success';
|
||||
$oauthDefaultFailure = '/console/auth/oauth2/failure';
|
||||
|
||||
function sendSessionAlert(Locale $locale, Document $user, Document $project, Document $session, Mail $queueForMails)
|
||||
function sendSessionAlert(Locale $locale, Document $user, Document $project, array $platform, Document $session, Mail $queueForMails)
|
||||
{
|
||||
$subject = $locale->getText("emails.sessionAlert.subject");
|
||||
$preview = $locale->getText("emails.sessionAlert.preview");
|
||||
@@ -157,13 +158,18 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
$session->setAttribute('clientName', $clientName);
|
||||
}
|
||||
|
||||
$projectName = $project->getAttribute('name');
|
||||
if ($project->getId() === 'console') {
|
||||
$projectName = $platform['platformName'];
|
||||
}
|
||||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
'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'),
|
||||
'project' => $projectName,
|
||||
'device' => $session->getAttribute('clientName'),
|
||||
'ipAddress' => $session->getAttribute('ip'),
|
||||
'country' => $locale->getText('countries.' . $session->getAttribute('countryCode'), $locale->getText('locale.country.unknown')),
|
||||
@@ -171,13 +177,14 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
|
||||
if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) {
|
||||
$emailVariables = array_merge($emailVariables, [
|
||||
'accentColor' => APP_EMAIL_ACCENT_COLOR,
|
||||
'logoUrl' => APP_EMAIL_LOGO_URL,
|
||||
'twitterUrl' => APP_SOCIAL_TWITTER,
|
||||
'discordUrl' => APP_SOCIAL_DISCORD,
|
||||
'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE,
|
||||
'termsUrl' => APP_EMAIL_TERMS_URL,
|
||||
'privacyUrl' => APP_EMAIL_PRIVACY_URL,
|
||||
'accentColor' => $platform['accentColor'],
|
||||
'logoUrl' => $platform['logoUrl'],
|
||||
'twitter' => $platform['twitterUrl'],
|
||||
'discord' => $platform['discordUrl'],
|
||||
'github' => $platform['githubUrl'],
|
||||
'terms' => $platform['termsUrl'],
|
||||
'privacy' => $platform['privacyUrl'],
|
||||
'platform' => $platform['platformName'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -189,12 +196,18 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
->setBody($body)
|
||||
->setBodyTemplate($bodyTemplate)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
}
|
||||
;
|
||||
->setRecipient($email);
|
||||
|
||||
$createSession = function (string $userId, string $secret, Request $request, Response $response, User $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Store $store, ProofsToken $proofForToken, ProofsCode $proofForCode) {
|
||||
// since this is console project, set email sender name!
|
||||
if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) {
|
||||
$queueForMails->setSenderName($platform['emailSenderName']);
|
||||
}
|
||||
|
||||
$queueForMails->trigger();
|
||||
}
|
||||
|
||||
|
||||
$createSession = function (string $userId, string $secret, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Store $store, ProofsToken $proofForToken, ProofsCode $proofForCode) {
|
||||
|
||||
/** @var Appwrite\Utopia\Database\Documents\User $userFromRequest */
|
||||
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
|
||||
@@ -295,7 +308,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
]) !== 1;
|
||||
|
||||
if ($isAllowedTokenType && $hasUserEmail && $isSessionAlertsEnabled && $isNotFirstSession) {
|
||||
sendSessionAlert($locale, $user, $project, $session, $queueForMails);
|
||||
sendSessionAlert($locale, $user, $project, $platform, $session, $queueForMails);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
@@ -946,6 +959,7 @@ App::post('/v1/account/sessions/email')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->label('abuse-reset', [201])
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
|
||||
->inject('request')
|
||||
@@ -953,6 +967,7 @@ App::post('/v1/account/sessions/email')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
@@ -961,7 +976,7 @@ App::post('/v1/account/sessions/email')
|
||||
->inject('store')
|
||||
->inject('proofForPassword')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $email, string $password, Request $request, Response $response, User $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
|
||||
->action(function (string $email, string $password, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
|
||||
$email = \strtolower($email);
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
@@ -1062,7 +1077,7 @@ App::post('/v1/account/sessions/email')
|
||||
Query::equal('userId', [$user->getId()]),
|
||||
]) !== 1
|
||||
) {
|
||||
sendSessionAlert($locale, $user, $project, $session, $queueForMails);
|
||||
sendSessionAlert($locale, $user, $project, $platform, $session, $queueForMails);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1243,6 +1258,7 @@ App::post('/v1/account/sessions/token')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
|
||||
->label('abuse-reset', [201])
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('secret', '', new Text(256), 'Secret of a token generated by login methods. For example, the `createMagicURLToken` or `createPhoneToken` methods.')
|
||||
->inject('request')
|
||||
@@ -1250,6 +1266,7 @@ App::post('/v1/account/sessions/token')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
@@ -1620,9 +1637,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Is verified is not used yet, since we don't know after an account is created anymore if it was verified or not.
|
||||
*/
|
||||
$isVerified = $oauth2->isEmailVerified($accessToken);
|
||||
|
||||
$identity = $dbForProject->findOne('identities', [
|
||||
@@ -1634,16 +1648,32 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$user = $dbForProject->getDocument('users', $identity->getAttribute('userId'));
|
||||
}
|
||||
|
||||
// If user is not found, check if there is an identity with the same provider user ID
|
||||
// If user is not found, check if there is a user with the same email
|
||||
if ($user === false || $user->isEmpty()) {
|
||||
$userWithEmail = $dbForProject->findOne('users', [
|
||||
Query::equal('email', [$email]),
|
||||
]);
|
||||
if (!$userWithEmail->isEmpty()) {
|
||||
if (!$isVerified) {
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST);
|
||||
}
|
||||
$user->setAttributes($userWithEmail->getArrayCopy());
|
||||
}
|
||||
}
|
||||
|
||||
// If user is not found, check if there is an identity with the same email
|
||||
if ($user === false || $user->isEmpty()) {
|
||||
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
|
||||
Query::equal('providerEmail', [$email]),
|
||||
]);
|
||||
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||
if (!$isVerified) {
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST);
|
||||
}
|
||||
$user->setAttributes($dbForProject->getDocument('users', $identityWithMatchingEmail->getAttribute('userId'))->getArrayCopy());
|
||||
}
|
||||
}
|
||||
|
||||
if ($user === false || $user->isEmpty()) { // Last option -> create the user
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
@@ -1655,14 +1685,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure this email is not already used in another identity
|
||||
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
|
||||
Query::equal('providerEmail', [$email]),
|
||||
]);
|
||||
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
}
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
@@ -1716,7 +1738,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
'providerType' => MESSAGE_TYPE_EMAIL,
|
||||
'identifier' => $email,
|
||||
]));
|
||||
|
||||
} catch (Duplicate) {
|
||||
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
@@ -1953,7 +1974,16 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project, array $platform) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
|
||||
$callback = $platform['endpoint'] . '/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
|
||||
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||
|
||||
if (!$providerEnabled) {
|
||||
@@ -2242,11 +2272,16 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->setSmtpSenderName($senderName);
|
||||
}
|
||||
|
||||
$projectName = $project->getAttribute('name');
|
||||
if ($project->getId() === 'console') {
|
||||
$projectName = $platform['platformName'];
|
||||
}
|
||||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
// {{user}}, {{redirect}} and {{project}} are required in default and custom templates
|
||||
'user' => $user->getAttribute('name'),
|
||||
'project' => $project->getAttribute('name'),
|
||||
'project' => $projectName,
|
||||
'redirect' => $url,
|
||||
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
|
||||
'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN',
|
||||
@@ -2261,8 +2296,13 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->setPreview($preview)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
->setRecipient($email);
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
$queueForMails->setSenderName($platform['emailSenderName']);
|
||||
}
|
||||
|
||||
$queueForMails->trigger();
|
||||
|
||||
$token->setAttribute('secret', $tokenSecret);
|
||||
|
||||
@@ -2309,13 +2349,14 @@ App::post('/v1/account/tokens/email')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMails')
|
||||
->inject('proofForPassword')
|
||||
->inject('proofForCode')
|
||||
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, User $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, ProofsCode $proofForCode) {
|
||||
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, ProofsCode $proofForCode) {
|
||||
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
|
||||
}
|
||||
@@ -2516,12 +2557,17 @@ App::post('/v1/account/tokens/email')
|
||||
->setSmtpSenderName($senderName);
|
||||
}
|
||||
|
||||
$projectName = $project->getAttribute('name');
|
||||
if ($project->getId() === 'console') {
|
||||
$projectName = $platform['platformName'];
|
||||
}
|
||||
|
||||
$emailVariables = [
|
||||
'heading' => $heading,
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
// {{user}}, {{project}} and {{otp}} are required in the templates
|
||||
'user' => $user->getAttribute('name'),
|
||||
'project' => $project->getAttribute('name'),
|
||||
'project' => $projectName,
|
||||
'otp' => $tokenSecret,
|
||||
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
|
||||
'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN',
|
||||
@@ -2533,13 +2579,14 @@ App::post('/v1/account/tokens/email')
|
||||
|
||||
if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) {
|
||||
$emailVariables = array_merge($emailVariables, [
|
||||
'accentColor' => APP_EMAIL_ACCENT_COLOR,
|
||||
'logoUrl' => APP_EMAIL_LOGO_URL,
|
||||
'twitterUrl' => APP_SOCIAL_TWITTER,
|
||||
'discordUrl' => APP_SOCIAL_DISCORD,
|
||||
'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE,
|
||||
'termsUrl' => APP_EMAIL_TERMS_URL,
|
||||
'privacyUrl' => APP_EMAIL_PRIVACY_URL,
|
||||
'accentColor' => $platform['accentColor'],
|
||||
'logoUrl' => $platform['logoUrl'],
|
||||
'twitter' => $platform['twitterUrl'],
|
||||
'discord' => $platform['discordUrl'],
|
||||
'github' => $platform['githubUrl'],
|
||||
'terms' => $platform['termsUrl'],
|
||||
'privacy' => $platform['privacyUrl'],
|
||||
'platform' => $platform['platformName'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -2549,8 +2596,14 @@ App::post('/v1/account/tokens/email')
|
||||
->setBody($body)
|
||||
->setBodyTemplate($bodyTemplate)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
->setRecipient($email);
|
||||
|
||||
// since this is console project, set email sender name!
|
||||
if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) {
|
||||
$queueForMails->setSenderName($platform['emailSenderName']);
|
||||
}
|
||||
|
||||
$queueForMails->trigger();
|
||||
|
||||
$token->setAttribute('secret', $tokenSecret);
|
||||
|
||||
@@ -2594,6 +2647,7 @@ App::put('/v1/account/sessions/magic-url')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
|
||||
->label('abuse-reset', [201])
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('secret', '', new Text(256), 'Valid verification token.')
|
||||
->inject('request')
|
||||
@@ -2601,16 +2655,17 @@ App::put('/v1/account/sessions/magic-url')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMails')
|
||||
->inject('store')
|
||||
->inject('proofForCode')
|
||||
->action(function ($userId, $secret, $request, $response, $user, $dbForProject, $project, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForCode) use ($createSession) {
|
||||
->action(function ($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForCode) use ($createSession) {
|
||||
$proofForToken = new ProofsToken(TOKEN_LENGTH_MAGIC_URL);
|
||||
$proofForToken->setHash(new Sha());
|
||||
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode);
|
||||
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode);
|
||||
});
|
||||
|
||||
App::put('/v1/account/sessions/phone')
|
||||
@@ -2648,6 +2703,7 @@ App::put('/v1/account/sessions/phone')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
@@ -2688,6 +2744,7 @@ App::post('/v1/account/tokens/phone')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMessaging')
|
||||
@@ -2697,7 +2754,7 @@ App::post('/v1/account/tokens/phone')
|
||||
->inject('plan')
|
||||
->inject('store')
|
||||
->inject('proofForCode')
|
||||
->action(function (string $userId, string $phone, Request $request, Response $response, User $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Store $store, ProofsCode $proofForCode) {
|
||||
->action(function (string $userId, string $phone, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Store $store, ProofsCode $proofForCode) {
|
||||
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
}
|
||||
@@ -2814,9 +2871,14 @@ App::post('/v1/account/tokens/phone')
|
||||
$message = $customTemplate['message'] ?? $message;
|
||||
}
|
||||
|
||||
$projectName = $project->getAttribute('name');
|
||||
if ($project->getId() === 'console') {
|
||||
$projectName = $platform['platformName'];
|
||||
}
|
||||
|
||||
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
|
||||
$messageContent
|
||||
->setParam('{{project}}', $project->getAttribute('name'))
|
||||
->setParam('{{project}}', $projectName)
|
||||
->setParam('{{secret}}', $secret);
|
||||
$messageContent = \strip_tags($messageContent->render());
|
||||
$message = $message->setParam('{{token}}', $messageContent);
|
||||
@@ -2895,20 +2957,22 @@ App::post('/v1/account/jwts')
|
||||
],
|
||||
contentType: ContentType::JSON,
|
||||
))
|
||||
->label('abuse-limit', 100)
|
||||
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('abuse-key', 'url:{url},userId:{userId}')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('store')
|
||||
->inject('proofForToken')
|
||||
->action(function (Response $response, User $user, Store $store, ProofsToken $proofForToken) {
|
||||
->action(function (int $duration, Response $response, User $user, Store $store, ProofsToken $proofForToken) {
|
||||
$sessionId = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken);
|
||||
|
||||
if (!$sessionId) {
|
||||
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 0);
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -2972,7 +3036,8 @@ App::get('/v1/account/logs')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('dbForProject')
|
||||
->action(function (array $queries, bool $includeTotal, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject) {
|
||||
->inject('audit')
|
||||
->action(function (array $queries, bool $includeTotal, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Audit $audit) {
|
||||
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
@@ -2980,9 +3045,10 @@ App::get('/v1/account/logs')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$audit = new EventAudit($dbForProject);
|
||||
|
||||
$logs = $audit->getLogsByUser($user->getSequence(), $queries);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? 25;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
$logs = $audit->getLogsByUser($user->getSequence(), offset: $offset, limit: $limit);
|
||||
|
||||
$output = [];
|
||||
|
||||
@@ -3011,7 +3077,7 @@ App::get('/v1/account/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence(), $queries) : 0,
|
||||
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence()) : 0,
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
@@ -3464,15 +3530,16 @@ App::post('/v1/account/recovery')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('locale')
|
||||
->inject('queueForMails')
|
||||
->inject('queueForEvents')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $email, string $url, Request $request, Response $response, User $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, ProofsToken $proofForToken) {
|
||||
|
||||
->action(function (string $email, string $url, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Mail $queueForMails, Event $queueForEvents, ProofsToken $proofForToken) {
|
||||
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
|
||||
}
|
||||
|
||||
$url = htmlentities($url);
|
||||
$email = \strtolower($email);
|
||||
|
||||
@@ -3519,7 +3586,14 @@ App::post('/v1/account/recovery')
|
||||
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]);
|
||||
$url = Template::unParseURL($url);
|
||||
|
||||
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
|
||||
$projectName = $project->isEmpty()
|
||||
? 'Console'
|
||||
: $project->getAttribute('name', '[APP-NAME]');
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
$projectName = $platform['platformName'];
|
||||
}
|
||||
|
||||
$body = $locale->getText("emails.recovery.body");
|
||||
$subject = $locale->getText("emails.recovery.subject");
|
||||
$preview = $locale->getText("emails.recovery.preview");
|
||||
@@ -3597,8 +3671,13 @@ App::post('/v1/account/recovery')
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->trigger();
|
||||
->setPreview($preview);
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
$queueForMails->setSenderName($platform['emailSenderName']);
|
||||
}
|
||||
|
||||
$queueForMails->trigger();
|
||||
|
||||
$recovery->setAttribute('secret', $secret);
|
||||
|
||||
@@ -3758,13 +3837,14 @@ App::post('/v1/account/verifications/email')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMails')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $url, Request $request, Response $response, Document $project, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsToken $proofForToken) {
|
||||
->action(function (string $url, Request $request, Response $response, Document $project, array $platform, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsToken $proofForToken) {
|
||||
|
||||
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
|
||||
@@ -3808,7 +3888,15 @@ App::post('/v1/account/verifications/email')
|
||||
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]);
|
||||
$url = Template::unParseURL($url);
|
||||
|
||||
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
|
||||
$projectName = $project->isEmpty()
|
||||
? 'Console'
|
||||
: $project->getAttribute('name', '[APP-NAME]');
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
$projectName = $platform['platformName'];
|
||||
}
|
||||
|
||||
|
||||
$body = $locale->getText("emails.verification.body");
|
||||
$preview = $locale->getText("emails.verification.preview");
|
||||
$subject = $locale->getText("emails.verification.subject");
|
||||
@@ -3894,13 +3982,14 @@ App::post('/v1/account/verifications/email')
|
||||
|
||||
if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) {
|
||||
$emailVariables = array_merge($emailVariables, [
|
||||
'accentColor' => APP_EMAIL_ACCENT_COLOR,
|
||||
'logoUrl' => APP_EMAIL_LOGO_URL,
|
||||
'twitterUrl' => APP_SOCIAL_TWITTER,
|
||||
'discordUrl' => APP_SOCIAL_DISCORD,
|
||||
'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE,
|
||||
'termsUrl' => APP_EMAIL_TERMS_URL,
|
||||
'privacyUrl' => APP_EMAIL_PRIVACY_URL,
|
||||
'accentColor' => $platform['accentColor'],
|
||||
'logoUrl' => $platform['logoUrl'],
|
||||
'twitter' => $platform['twitterUrl'],
|
||||
'discord' => $platform['discordUrl'],
|
||||
'github' => $platform['githubUrl'],
|
||||
'terms' => $platform['termsUrl'],
|
||||
'privacy' => $platform['privacyUrl'],
|
||||
'platform' => $platform['platformName'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -3911,8 +4000,13 @@ App::post('/v1/account/verifications/email')
|
||||
->setBodyTemplate($bodyTemplate)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($user->getAttribute('email'))
|
||||
->setName($user->getAttribute('name') ?? '')
|
||||
->trigger();
|
||||
->setName($user->getAttribute('name') ?? '');
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
$queueForMails->setSenderName($platform['emailSenderName']);
|
||||
}
|
||||
|
||||
$queueForMails->trigger();
|
||||
|
||||
$verification->setAttribute('secret', $verificationSecret);
|
||||
|
||||
|
||||
@@ -697,7 +697,7 @@ App::get('/v1/avatars/screenshots')
|
||||
}
|
||||
|
||||
$client = new Client();
|
||||
$client->setTimeout(30);
|
||||
$client->setTimeout(30 * 1000); // 30 seconds
|
||||
$client->addHeader('content-type', Client::CONTENT_TYPE_APPLICATION_JSON);
|
||||
|
||||
// Convert indexed array to empty array (should not happen due to Assoc validator)
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\IP;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
App::init()
|
||||
->groups(['console'])
|
||||
->inject('project')
|
||||
->action(function (Document $project) {
|
||||
if ($project->getId() !== 'console') {
|
||||
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
App::get('/v1/console/variables')
|
||||
->desc('Get variables')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'console',
|
||||
group: 'console',
|
||||
name: 'variables',
|
||||
description: '/docs/references/console/variables.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_CONSOLE_VARIABLES,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->inject('response')
|
||||
->action(function (Response $response) {
|
||||
$validator = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME'));
|
||||
$isCNAMEValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')) && $validator->isKnown() && !$validator->isTest();
|
||||
|
||||
$validator = new IP(IP::V4);
|
||||
$isAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_A', '')) && ($validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_A')));
|
||||
|
||||
$validator = new IP(IP::V6);
|
||||
$isAAAAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_AAAA', '')) && $validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA'));
|
||||
|
||||
$isDomainEnabled = $isAAAAValid || $isAValid || $isCNAMEValid;
|
||||
|
||||
$isVcsEnabled = !empty(System::getEnv('_APP_VCS_GITHUB_APP_NAME', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_APP_ID', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''));
|
||||
|
||||
$isAssistantEnabled = !empty(System::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', ''));
|
||||
|
||||
$variables = new Document([
|
||||
'_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'),
|
||||
'_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'),
|
||||
'_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'),
|
||||
// Combine CAA domain with most common flags and tag (no parameters)
|
||||
'_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"',
|
||||
'_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'),
|
||||
'_APP_COMPUTE_BUILD_TIMEOUT' => +System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT'),
|
||||
'_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'),
|
||||
'_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'),
|
||||
'_APP_VCS_ENABLED' => $isVcsEnabled,
|
||||
'_APP_DOMAIN_ENABLED' => $isDomainEnabled,
|
||||
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled,
|
||||
'_APP_DOMAIN_SITES' => System::getEnv('_APP_DOMAIN_SITES'),
|
||||
'_APP_DOMAIN_FUNCTIONS' => System::getEnv('_APP_DOMAIN_FUNCTIONS'),
|
||||
'_APP_OPTIONS_FORCE_HTTPS' => System::getEnv('_APP_OPTIONS_FORCE_HTTPS'),
|
||||
'_APP_DOMAINS_NAMESERVERS' => System::getEnv('_APP_DOMAINS_NAMESERVERS'),
|
||||
]);
|
||||
|
||||
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
||||
});
|
||||
|
||||
App::post('/v1/console/assistant')
|
||||
->desc('Create assistant query')
|
||||
->groups(['api', 'assistant'])
|
||||
->label('scope', 'assistant.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'assistant',
|
||||
group: 'console',
|
||||
name: 'chat',
|
||||
description: '/docs/references/assistant/chat.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::TEXT
|
||||
))
|
||||
->label('abuse-limit', 15)
|
||||
->label('abuse-key', 'userId:{userId}')
|
||||
->param('prompt', '', new Text(2000), 'Prompt. A string containing questions asked to the AI assistant.')
|
||||
->inject('response')
|
||||
->action(function (string $prompt, Response $response) {
|
||||
$ch = curl_init('http://appwrite-assistant:3003/v1/models/assistant/prompt');
|
||||
$responseHeaders = [];
|
||||
$query = json_encode(['prompt' => $prompt]);
|
||||
$headers = ['accept: text/event-stream'];
|
||||
$handleEvent = function ($ch, $data) use ($response) {
|
||||
$response->chunk($data);
|
||||
|
||||
return \strlen($data);
|
||||
};
|
||||
|
||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $handleEvent);
|
||||
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 9000);
|
||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) {
|
||||
$len = strlen($header);
|
||||
$header = explode(':', $header, 2);
|
||||
|
||||
if (count($header) < 2) { // ignore invalid headers
|
||||
return $len;
|
||||
}
|
||||
|
||||
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
|
||||
|
||||
return $len;
|
||||
});
|
||||
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
|
||||
|
||||
curl_exec($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
$response->chunk('', true);
|
||||
});
|
||||
@@ -945,18 +945,18 @@ App::get('/v1/health/queue/failed/:name')
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('name', '', new WhiteList([
|
||||
Event::DATABASE_QUEUE_NAME,
|
||||
Event::DELETE_QUEUE_NAME,
|
||||
Event::AUDITS_QUEUE_NAME,
|
||||
Event::MAILS_QUEUE_NAME,
|
||||
Event::FUNCTIONS_QUEUE_NAME,
|
||||
Event::STATS_RESOURCES_QUEUE_NAME,
|
||||
Event::STATS_USAGE_QUEUE_NAME,
|
||||
Event::WEBHOOK_QUEUE_NAME,
|
||||
Event::CERTIFICATES_QUEUE_NAME,
|
||||
Event::BUILDS_QUEUE_NAME,
|
||||
Event::MESSAGING_QUEUE_NAME,
|
||||
Event::MIGRATIONS_QUEUE_NAME
|
||||
System::getEnv('_APP_DATABASE_QUEUE_NAME', Event::DATABASE_QUEUE_NAME),
|
||||
System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME),
|
||||
System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME),
|
||||
System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME),
|
||||
System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME),
|
||||
System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME),
|
||||
System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME),
|
||||
System::getEnv('_APP_WEBHOOK_QUEUE_NAME', Event::WEBHOOK_QUEUE_NAME),
|
||||
System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME),
|
||||
System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME),
|
||||
System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME),
|
||||
System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME)
|
||||
]), 'The name of the queue')
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('response')
|
||||
@@ -993,18 +993,18 @@ App::get('/v1/health/queue/failed/:name')
|
||||
|
||||
/** @var Event $queue */
|
||||
$queue = match ($name) {
|
||||
Event::DATABASE_QUEUE_NAME => $queueForDatabase,
|
||||
Event::DELETE_QUEUE_NAME => $queueForDeletes,
|
||||
Event::AUDITS_QUEUE_NAME => $queueForAudits,
|
||||
Event::MAILS_QUEUE_NAME => $queueForMails,
|
||||
Event::FUNCTIONS_QUEUE_NAME => $queueForFunctions,
|
||||
Event::STATS_RESOURCES_QUEUE_NAME => $queueForStatsResources,
|
||||
Event::STATS_USAGE_QUEUE_NAME => $queueForStatsUsage,
|
||||
Event::WEBHOOK_QUEUE_NAME => $queueForWebhooks,
|
||||
Event::CERTIFICATES_QUEUE_NAME => $queueForCertificates,
|
||||
Event::BUILDS_QUEUE_NAME => $queueForBuilds,
|
||||
Event::MESSAGING_QUEUE_NAME => $queueForMessaging,
|
||||
Event::MIGRATIONS_QUEUE_NAME => $queueForMigrations,
|
||||
System::getEnv('_APP_DATABASE_QUEUE_NAME', Event::DATABASE_QUEUE_NAME) => $queueForDatabase,
|
||||
System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME) => $queueForDeletes,
|
||||
System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME) => $queueForAudits,
|
||||
System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME) => $queueForMails,
|
||||
System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME) => $queueForFunctions,
|
||||
System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME) => $queueForStatsResources,
|
||||
System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME) => $queueForStatsUsage,
|
||||
System::getEnv('_APP_WEBHOOK_QUEUE_NAME', Event::WEBHOOK_QUEUE_NAME) => $queueForWebhooks,
|
||||
System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME) => $queueForCertificates,
|
||||
System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME) => $queueForBuilds,
|
||||
System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME) => $queueForMessaging,
|
||||
System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME) => $queueForMigrations,
|
||||
};
|
||||
$failed = $queue->getSize(failed: true);
|
||||
|
||||
|
||||
@@ -1145,7 +1145,8 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function (string $providerId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
->inject('audit')
|
||||
->action(function (string $providerId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
@@ -1158,9 +1159,12 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? 25;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$resource = 'provider/' . $providerId;
|
||||
$logs = $audit->getLogsByResource($resource, $queries);
|
||||
$logs = $audit->getLogsByResource($resource, offset: $offset, limit: $limit);
|
||||
$output = [];
|
||||
|
||||
foreach ($logs as $i => &$log) {
|
||||
@@ -1207,7 +1211,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
@@ -2549,7 +2553,8 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function (string $topicId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
->inject('audit')
|
||||
->action(function (string $topicId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
|
||||
$topic = $dbForProject->getDocument('topics', $topicId);
|
||||
|
||||
if ($topic->isEmpty()) {
|
||||
@@ -2562,9 +2567,12 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? 25;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$resource = 'topic/' . $topicId;
|
||||
$logs = $audit->getLogsByResource($resource, $queries);
|
||||
$logs = $audit->getLogsByResource($resource, offset: $offset, limit: $limit);
|
||||
|
||||
$output = [];
|
||||
|
||||
@@ -2612,7 +2620,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
@@ -2966,7 +2974,8 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function (string $subscriberId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
->inject('audit')
|
||||
->action(function (string $subscriberId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
|
||||
$subscriber = $dbForProject->getDocument('subscribers', $subscriberId);
|
||||
|
||||
if ($subscriber->isEmpty()) {
|
||||
@@ -2979,9 +2988,12 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? 25;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$resource = 'subscriber/' . $subscriberId;
|
||||
$logs = $audit->getLogsByResource($resource, $queries);
|
||||
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
|
||||
|
||||
$output = [];
|
||||
|
||||
@@ -3029,7 +3041,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
@@ -3552,7 +3564,8 @@ App::post('/v1/messaging/messages/push')
|
||||
throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$endpoint = "$protocol://{$platform['apiHostname']}/v1";
|
||||
|
||||
$scheduleTime = $currentScheduledAt ?? $scheduledAt;
|
||||
if (!\is_null($scheduleTime)) {
|
||||
@@ -3572,7 +3585,7 @@ App::post('/v1/messaging/messages/push')
|
||||
$image = [
|
||||
'bucketId' => $bucket->getId(),
|
||||
'fileId' => $file->getId(),
|
||||
'url' => "{$platform['endpoint']}/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/push?project={$project->getId()}&jwt={$jwt}",
|
||||
'url' => "{$endpoint}/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/push?project={$project->getId()}&jwt={$jwt}",
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3761,7 +3774,8 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function (string $messageId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
->inject('audit')
|
||||
->action(function (string $messageId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
|
||||
$message = $dbForProject->getDocument('messages', $messageId);
|
||||
|
||||
if ($message->isEmpty()) {
|
||||
@@ -3774,9 +3788,12 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? 25;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$resource = 'message/' . $messageId;
|
||||
$logs = $audit->getLogsByResource($resource, $queries);
|
||||
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
|
||||
|
||||
$output = [];
|
||||
|
||||
@@ -3824,7 +3841,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
@@ -4562,10 +4579,13 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
'projectId' => $project->getId(),
|
||||
]);
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$endpoint = "$protocol://{$platform['apiHostname']}/v1";
|
||||
|
||||
$pushData['image'] = [
|
||||
'bucketId' => $bucket->getId(),
|
||||
'fileId' => $file->getId(),
|
||||
'url' => "{$platform['endpoint']}/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/push?project={$project->getId()}&jwt={$jwt}",
|
||||
'url' => "{$endpoint}/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/push?project={$project->getId()}&jwt={$jwt}",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -69,10 +69,11 @@ App::post('/v1/migrations/appwrite')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMigrations')
|
||||
->action(function (array $resources, string $endpoint, string $projectId, string $apiKey, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
->action(function (array $resources, string $endpoint, string $projectId, string $apiKey, Response $response, Database $dbForProject, Document $project, array $platform, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
$migration = $dbForProject->createDocument('migrations', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'status' => 'pending',
|
||||
@@ -96,6 +97,7 @@ App::post('/v1/migrations/appwrite')
|
||||
$queueForMigrations
|
||||
->setMigration($migration)
|
||||
->setProject($project)
|
||||
->setPlatform($platform)
|
||||
->setUser($user)
|
||||
->trigger();
|
||||
|
||||
@@ -128,10 +130,11 @@ App::post('/v1/migrations/firebase')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMigrations')
|
||||
->action(function (array $resources, string $serviceAccount, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
->action(function (array $resources, string $serviceAccount, Response $response, Database $dbForProject, Document $project, array $platform, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
$serviceAccountData = json_decode($serviceAccount, true);
|
||||
|
||||
if (empty($serviceAccountData)) {
|
||||
@@ -163,6 +166,7 @@ App::post('/v1/migrations/firebase')
|
||||
$queueForMigrations
|
||||
->setMigration($migration)
|
||||
->setProject($project)
|
||||
->setPlatform($platform)
|
||||
->setUser($user)
|
||||
->trigger();
|
||||
|
||||
@@ -200,10 +204,11 @@ App::post('/v1/migrations/supabase')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMigrations')
|
||||
->action(function (array $resources, string $endpoint, string $apiKey, string $databaseHost, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
->action(function (array $resources, string $endpoint, string $apiKey, string $databaseHost, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, array $platform, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
$migration = $dbForProject->createDocument('migrations', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'status' => 'pending',
|
||||
@@ -230,6 +235,7 @@ App::post('/v1/migrations/supabase')
|
||||
$queueForMigrations
|
||||
->setMigration($migration)
|
||||
->setProject($project)
|
||||
->setPlatform($platform)
|
||||
->setUser($user)
|
||||
->trigger();
|
||||
|
||||
@@ -268,10 +274,11 @@ App::post('/v1/migrations/nhost')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMigrations')
|
||||
->action(function (array $resources, string $subdomain, string $region, string $adminSecret, string $database, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
->action(function (array $resources, string $subdomain, string $region, string $adminSecret, string $database, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, array $platform, Document $user, Event $queueForEvents, Migration $queueForMigrations) {
|
||||
$migration = $dbForProject->createDocument('migrations', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'status' => 'pending',
|
||||
@@ -299,6 +306,7 @@ App::post('/v1/migrations/nhost')
|
||||
$queueForMigrations
|
||||
->setMigration($migration)
|
||||
->setProject($project)
|
||||
->setPlatform($platform)
|
||||
->setUser($user)
|
||||
->trigger();
|
||||
|
||||
@@ -335,6 +343,7 @@ App::post('/v1/migrations/csv/imports')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('deviceForFiles')
|
||||
->inject('deviceForMigrations')
|
||||
->inject('queueForEvents')
|
||||
@@ -348,6 +357,7 @@ App::post('/v1/migrations/csv/imports')
|
||||
Database $dbForProject,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
array $platform,
|
||||
Device $deviceForFiles,
|
||||
Device $deviceForMigrations,
|
||||
Event $queueForEvents,
|
||||
@@ -441,6 +451,7 @@ App::post('/v1/migrations/csv/imports')
|
||||
$queueForMigrations
|
||||
->setMigration($migration)
|
||||
->setProject($project)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
$response
|
||||
@@ -481,6 +492,7 @@ App::post('/v1/migrations/csv/exports')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMigrations')
|
||||
->action(function (
|
||||
@@ -498,6 +510,7 @@ App::post('/v1/migrations/csv/exports')
|
||||
Database $dbForProject,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
array $platform,
|
||||
Event $queueForEvents,
|
||||
Migration $queueForMigrations
|
||||
) {
|
||||
@@ -571,6 +584,7 @@ App::post('/v1/migrations/csv/exports')
|
||||
$queueForMigrations
|
||||
->setMigration($migration)
|
||||
->setProject($project)
|
||||
->setPlatform($platform)
|
||||
->trigger();
|
||||
|
||||
$response
|
||||
@@ -903,9 +917,10 @@ App::patch('/v1/migrations/:migrationId')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->inject('user')
|
||||
->inject('queueForMigrations')
|
||||
->action(function (string $migrationId, Response $response, Database $dbForProject, Document $project, Document $user, Migration $queueForMigrations) {
|
||||
->action(function (string $migrationId, Response $response, Database $dbForProject, Document $project, array $platform, Document $user, Migration $queueForMigrations) {
|
||||
$migration = $dbForProject->getDocument('migrations', $migrationId);
|
||||
|
||||
if ($migration->isEmpty()) {
|
||||
@@ -924,6 +939,7 @@ App::patch('/v1/migrations/:migrationId')
|
||||
$queueForMigrations
|
||||
->setMigration($migration)
|
||||
->setProject($project)
|
||||
->setPlatform($platform)
|
||||
->setUser($user)
|
||||
->trigger();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
@@ -203,6 +204,7 @@ App::post('/v1/projects')
|
||||
'accessedAt' => DateTime::now(),
|
||||
'search' => implode(' ', [$projectId, $name]),
|
||||
'database' => $dsn,
|
||||
'labels' => [],
|
||||
]));
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
|
||||
@@ -247,13 +249,15 @@ App::post('/v1/projects')
|
||||
}
|
||||
|
||||
if ($create || $projectTables) {
|
||||
$audit = new Audit($dbForProject);
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$audit = new Audit($adapter);
|
||||
$audit->setup();
|
||||
}
|
||||
|
||||
if (!$create && $sharedTablesV1) {
|
||||
$attributes = \array_map(fn ($attribute) => new Document($attribute), Audit::ATTRIBUTES);
|
||||
$indexes = \array_map(fn (array $index) => new Document($index), Audit::INDEXES);
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$attributes = $adapter->getAttributeDocuments();
|
||||
$indexes = $adapter->getIndexDocuments();
|
||||
$dbForProject->createDocument(Database::METADATA, new Document([
|
||||
'$id' => ID::custom('audit'),
|
||||
'$permissions' => [Permission::create(Role::any())],
|
||||
@@ -294,7 +298,7 @@ App::post('/v1/projects')
|
||||
|
||||
// Hook allowing instant project mirroring during migration
|
||||
// Outside of migration, hook is not registered and has no effect
|
||||
$hooks->trigger('afterProjectCreation', [ $project, $pools, $cache ]);
|
||||
$hooks->trigger('afterProjectCreation', [$project, $pools, $cache]);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -1497,8 +1501,12 @@ App::post('/v1/projects/:projectId/keys')
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
// TODO: @hmacr Remove `projectInternalId` and `projectId` column writes before deleting the column.
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
'resourceInternalId' => $project->getSequence(),
|
||||
'resourceId' => $project->getId(),
|
||||
'resourceType' => 'projects',
|
||||
'name' => $name,
|
||||
'scopes' => $scopes,
|
||||
'expire' => $expire,
|
||||
@@ -1546,7 +1554,8 @@ App::get('/v1/projects/:projectId/keys')
|
||||
}
|
||||
|
||||
$keys = $dbForPlatform->find('keys', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::equal('resourceType', ['projects']),
|
||||
Query::equal('resourceInternalId', [$project->getSequence()]),
|
||||
Query::limit(5000),
|
||||
]);
|
||||
|
||||
@@ -1587,7 +1596,8 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForPlatform->findOne('keys', [
|
||||
Query::equal('$id', [$keyId]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::equal('resourceType', ['projects']),
|
||||
Query::equal('resourceInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($key->isEmpty()) {
|
||||
@@ -1631,7 +1641,8 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForPlatform->findOne('keys', [
|
||||
Query::equal('$id', [$keyId]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::equal('resourceType', ['projects']),
|
||||
Query::equal('resourceInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($key->isEmpty()) {
|
||||
@@ -1682,7 +1693,8 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForPlatform->findOne('keys', [
|
||||
Query::equal('$id', [$keyId]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::equal('resourceType', ['projects']),
|
||||
Query::equal('resourceInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($key->isEmpty()) {
|
||||
@@ -2079,6 +2091,7 @@ App::patch('/v1/projects/:projectId/smtp')
|
||||
if ($enabled) {
|
||||
$mail = new PHPMailer(true);
|
||||
$mail->isSMTP();
|
||||
$mail->SMTPAuth = (!empty($username) && !empty($password));
|
||||
$mail->Username = $username;
|
||||
$mail->Password = $password;
|
||||
$mail->Host = $host;
|
||||
@@ -2094,7 +2107,7 @@ App::patch('/v1/projects/:projectId/smtp')
|
||||
throw new Exception('Connection is not valid.');
|
||||
}
|
||||
} catch (Throwable $error) {
|
||||
throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, 'Could not connect to SMTP server: ' . $error->getMessage());
|
||||
throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, $error->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2665,7 +2678,7 @@ App::patch('/v1/projects/:projectId/auth/session-invalidation')
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
$auths['invalidateSessions'] = $enabled;
|
||||
$dbForPlatform->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('auths', $auths));
|
||||
->setAttribute('auths', $auths));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1464,7 +1464,8 @@ App::get('/v1/teams/:teamId/logs')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function (string $teamId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
->inject('audit')
|
||||
->action(function (string $teamId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
@@ -1478,9 +1479,12 @@ App::get('/v1/teams/:teamId/logs')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? 25;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$resource = 'team/' . $team->getId();
|
||||
$logs = $audit->getLogsByResource($resource, $queries);
|
||||
$logs = $audit->getLogsByResource($resource, offset: $offset, limit: $limit);
|
||||
|
||||
$output = [];
|
||||
|
||||
@@ -1527,7 +1531,7 @@ App::get('/v1/teams/:teamId/logs')
|
||||
}
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
|
||||
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
@@ -945,7 +945,8 @@ App::get('/v1/users/:userId/logs')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function (string $userId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
->inject('audit')
|
||||
->action(function (string $userId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
@@ -958,8 +959,11 @@ App::get('/v1/users/:userId/logs')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$logs = $audit->getLogsByUser($user->getSequence(), $queries);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? 25;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$logs = $audit->getLogsByUser($user->getSequence(), limit: $limit, offset: $offset);
|
||||
$output = [];
|
||||
foreach ($logs as $i => &$log) {
|
||||
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
|
||||
@@ -999,7 +1003,7 @@ App::get('/v1/users/:userId/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence(), $queries) : 0,
|
||||
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence()) : 0,
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
@@ -458,10 +458,13 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
|
||||
}
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$endpoint = "$protocol://{$platform['apiHostname']}/v1";
|
||||
|
||||
// Appwrite vars
|
||||
if ($type === 'function') {
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_API_ENDPOINT' => $platform['endpoint'],
|
||||
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
|
||||
'APPWRITE_FUNCTION_ID' => $resource->getId(),
|
||||
'APPWRITE_FUNCTION_NAME' => $resource->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
|
||||
@@ -473,7 +476,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
]);
|
||||
} elseif ($type === 'site') {
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_SITE_API_ENDPOINT' => $platform['endpoint'],
|
||||
'APPWRITE_SITE_API_ENDPOINT' => $endpoint,
|
||||
'APPWRITE_SITE_ID' => $resource->getId(),
|
||||
'APPWRITE_SITE_NAME' => $resource->getAttribute('name'),
|
||||
'APPWRITE_SITE_DEPLOYMENT' => $deployment->getId(),
|
||||
@@ -1045,18 +1048,15 @@ App::init()
|
||||
if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) {
|
||||
$cache[$domain->get()] = false;
|
||||
Config::setParam('hostnames', $cache);
|
||||
Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
|
||||
Console::warning('Skipping SSL certificates generation on ACME challenge.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Check if domain is a main domain
|
||||
if (!in_array($domain->get(), $platformHostnames)) {
|
||||
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,28 @@ App::get('/v1/mock/tests/general/oauth2/user')
|
||||
'id' => 1,
|
||||
'name' => 'User Name',
|
||||
'email' => 'useroauth@localhost.test',
|
||||
'verified' => true,
|
||||
]);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/user-unverified')
|
||||
->desc('OAuth2 User Unverified')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->param('token', '', new Text(100), 'OAuth2 Access Token.')
|
||||
->inject('response')
|
||||
->action(function (string $token, Response $response) {
|
||||
|
||||
if ($token != '123456') {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid token');
|
||||
}
|
||||
|
||||
$response->json([
|
||||
'id' => 2,
|
||||
'name' => 'User Name Unverified',
|
||||
'email' => 'useroauthunverified@localhost.test',
|
||||
'verified' => false,
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -178,8 +200,12 @@ App::post('/v1/mock/api-key-unprefixed')
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
// TODO: @hmacr Remove `projectInternalId` and `projectId` column writes before deleting the column.
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
'resourceInternalId' => $project->getSequence(),
|
||||
'resourceId' => $project->getId(),
|
||||
'resourceType' => 'projects',
|
||||
'name' => 'Outdated key',
|
||||
'scopes' => $scopes,
|
||||
'expire' => null,
|
||||
|
||||
@@ -814,7 +814,8 @@ App::shutdown()
|
||||
->inject('queueForWebhooks')
|
||||
->inject('queueForRealtime')
|
||||
->inject('dbForProject')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) use ($parseLabel) {
|
||||
->inject('timelimit')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, callable $timelimit) use ($parseLabel) {
|
||||
|
||||
$responsePayload = $response->getPayload();
|
||||
|
||||
@@ -848,6 +849,41 @@ App::shutdown()
|
||||
$route = $utopia->getRoute();
|
||||
$requestParams = $route->getParamsValues();
|
||||
|
||||
/**
|
||||
* Abuse labels
|
||||
*/
|
||||
$abuseEnabled = System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
|
||||
$abuseResetCode = $route->getLabel('abuse-reset', []);
|
||||
$abuseResetCode = \is_array($abuseResetCode) ? $abuseResetCode : [$abuseResetCode];
|
||||
|
||||
if ($abuseEnabled && \count($abuseResetCode) > 0 && \in_array($response->getStatusCode(), $abuseResetCode)) {
|
||||
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
|
||||
$abuseKeyLabel = (!is_array($abuseKeyLabel)) ? [$abuseKeyLabel] : $abuseKeyLabel;
|
||||
|
||||
foreach ($abuseKeyLabel as $abuseKey) {
|
||||
$start = $request->getContentRangeStart();
|
||||
$end = $request->getContentRangeEnd();
|
||||
$timeLimit = $timelimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600));
|
||||
$timeLimit
|
||||
->setParam('{projectId}', $project->getId())
|
||||
->setParam('{userId}', $user->getId())
|
||||
->setParam('{userAgent}', $request->getUserAgent(''))
|
||||
->setParam('{ip}', $request->getIP())
|
||||
->setParam('{url}', $request->getHostname() . $route->getPath())
|
||||
->setParam('{method}', $request->getMethod())
|
||||
->setParam('{chunkId}', (int)($start / ($end + 1 - $start)));
|
||||
|
||||
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||
if (!empty($value)) {
|
||||
$timeLimit->setParam('{param-' . $key . '}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||
}
|
||||
}
|
||||
|
||||
$abuse = new Abuse($timeLimit);
|
||||
$abuse->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit labels
|
||||
*/
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
|
||||
App::init()
|
||||
->groups(['web'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (Request $request, Response $response) {
|
||||
$response
|
||||
->addHeader('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes
|
||||
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url=' . \urlencode($request->getURI()))
|
||||
->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/')
|
||||
->alias('auth/*')
|
||||
->alias('/invite')
|
||||
->alias('/login')
|
||||
->alias('/mfa')
|
||||
->alias('/card/*')
|
||||
->alias('/recover')
|
||||
->alias('/register/*')
|
||||
->groups(['web'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (Request $request, Response $response) {
|
||||
$url = parse_url($request->getURI());
|
||||
$target = "/console{$url['path']}";
|
||||
$params = $request->getParams();
|
||||
if (!empty($params)) {
|
||||
$target .= "?" . \http_build_query($params);
|
||||
}
|
||||
if ($url['fragment'] ?? false) {
|
||||
$target .= "#{$url['fragment']}";
|
||||
}
|
||||
$response->redirect($target);
|
||||
});
|
||||
+8
-4
@@ -12,6 +12,8 @@ use Swoole\Process;
|
||||
use Swoole\Table;
|
||||
use Swoole\Timer;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Adapter\SQL as AuditAdapterSQL;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Compression\Compression;
|
||||
@@ -260,8 +262,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
|
||||
// create appwrite database, `dbForPlatform` is a direct access call.
|
||||
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections) {
|
||||
if ($dbForPlatform->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||
$audit = new Audit($dbForPlatform);
|
||||
if ($dbForPlatform->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
|
||||
$adapter = new AdapterDatabase($dbForPlatform);
|
||||
$audit = new Audit($adapter);
|
||||
$audit->setup();
|
||||
}
|
||||
|
||||
@@ -389,8 +392,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
Console::success('[Setup] - Skip: metadata table already exists');
|
||||
}
|
||||
|
||||
if ($dbForProject->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||
$audit = new Audit($dbForProject);
|
||||
if ($dbForProject->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$audit = new Audit($adapter);
|
||||
$audit->setup();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ require_once __DIR__ . '/init/database/filters.php';
|
||||
require_once __DIR__ . '/init/database/formats.php';
|
||||
require_once __DIR__ . '/init/locales.php';
|
||||
require_once __DIR__ . '/init/registers.php';
|
||||
require_once __DIR__ . '/init/models.php';
|
||||
require_once __DIR__ . '/init/resources.php';
|
||||
|
||||
\stream_context_set_default([ // Set global user agent and http settings
|
||||
|
||||
@@ -44,7 +44,7 @@ const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 4321;
|
||||
const APP_VERSION_STABLE = '1.8.0';
|
||||
const APP_VERSION_STABLE = '1.8.1';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
@@ -60,6 +60,7 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
|
||||
const APP_DATABASE_QUERY_MAX_VALUES = 500;
|
||||
const APP_DATABASE_QUERY_MAX_VALUES_WORKER = 5000;
|
||||
const APP_DATABASE_ENCRYPT_SIZE_MIN = 150;
|
||||
const APP_DATABASE_TXN_TTL_MIN = 60; // 1 minute
|
||||
const APP_DATABASE_TXN_TTL_MAX = 3600; // 1 hour
|
||||
@@ -208,6 +209,12 @@ const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
|
||||
const DELETE_TYPE_CSV_EXPORTS = 'csv_exports';
|
||||
const DELETE_TYPE_MAINTENANCE = 'maintenance';
|
||||
|
||||
// Rule statuses
|
||||
const RULE_STATUS_CREATED = 'created'; // This is also the status when domain DNS verification fails.
|
||||
const RULE_STATUS_CERTIFICATE_GENERATING = 'verifying';
|
||||
const RULE_STATUS_CERTIFICATE_GENERATION_FAILED = 'unverified';
|
||||
const RULE_STATUS_VERIFIED = 'verified';
|
||||
|
||||
// Message types
|
||||
const MESSAGE_SEND_TYPE_INTERNAL = 'internal';
|
||||
const MESSAGE_SEND_TYPE_EXTERNAL = 'external';
|
||||
@@ -353,6 +360,7 @@ const RESOURCE_TYPE_TOPICS = 'topics';
|
||||
const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers';
|
||||
const RESOURCE_TYPE_MESSAGES = 'messages';
|
||||
const RESOURCE_TYPE_EXECUTIONS = 'executions';
|
||||
const RESOURCE_TYPE_PAYMENTS = 'payments';
|
||||
|
||||
// Resource types for Tokens
|
||||
const TOKENS_RESOURCE_TYPE_FILES = 'files';
|
||||
|
||||
@@ -136,7 +136,8 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('keys', [
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
Query::equal('resourceType', ['projects']),
|
||||
Query::equal('resourceInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Account;
|
||||
use Appwrite\Utopia\Response\Model\AlgoArgon2;
|
||||
use Appwrite\Utopia\Response\Model\AlgoBcrypt;
|
||||
use Appwrite\Utopia\Response\Model\AlgoMd5;
|
||||
use Appwrite\Utopia\Response\Model\AlgoPhpass;
|
||||
use Appwrite\Utopia\Response\Model\AlgoScrypt;
|
||||
use Appwrite\Utopia\Response\Model\AlgoScryptModified;
|
||||
use Appwrite\Utopia\Response\Model\AlgoSha;
|
||||
use Appwrite\Utopia\Response\Model\Any;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
use Appwrite\Utopia\Response\Model\AttributeBoolean;
|
||||
use Appwrite\Utopia\Response\Model\AttributeDatetime;
|
||||
use Appwrite\Utopia\Response\Model\AttributeEmail;
|
||||
use Appwrite\Utopia\Response\Model\AttributeEnum;
|
||||
use Appwrite\Utopia\Response\Model\AttributeFloat;
|
||||
use Appwrite\Utopia\Response\Model\AttributeInteger;
|
||||
use Appwrite\Utopia\Response\Model\AttributeIP;
|
||||
use Appwrite\Utopia\Response\Model\AttributeLine;
|
||||
use Appwrite\Utopia\Response\Model\AttributeList;
|
||||
use Appwrite\Utopia\Response\Model\AttributePoint;
|
||||
use Appwrite\Utopia\Response\Model\AttributePolygon;
|
||||
use Appwrite\Utopia\Response\Model\AttributeRelationship;
|
||||
use Appwrite\Utopia\Response\Model\AttributeString;
|
||||
use Appwrite\Utopia\Response\Model\AttributeURL;
|
||||
use Appwrite\Utopia\Response\Model\AuthProvider;
|
||||
use Appwrite\Utopia\Response\Model\BaseList;
|
||||
use Appwrite\Utopia\Response\Model\Branch;
|
||||
use Appwrite\Utopia\Response\Model\Bucket;
|
||||
use Appwrite\Utopia\Response\Model\Collection;
|
||||
use Appwrite\Utopia\Response\Model\Column;
|
||||
use Appwrite\Utopia\Response\Model\ColumnBoolean;
|
||||
use Appwrite\Utopia\Response\Model\ColumnDatetime;
|
||||
use Appwrite\Utopia\Response\Model\ColumnEmail;
|
||||
use Appwrite\Utopia\Response\Model\ColumnEnum;
|
||||
use Appwrite\Utopia\Response\Model\ColumnFloat;
|
||||
use Appwrite\Utopia\Response\Model\ColumnIndex;
|
||||
use Appwrite\Utopia\Response\Model\ColumnInteger;
|
||||
use Appwrite\Utopia\Response\Model\ColumnIP;
|
||||
use Appwrite\Utopia\Response\Model\ColumnLine;
|
||||
use Appwrite\Utopia\Response\Model\ColumnList;
|
||||
use Appwrite\Utopia\Response\Model\ColumnPoint;
|
||||
use Appwrite\Utopia\Response\Model\ColumnPolygon;
|
||||
use Appwrite\Utopia\Response\Model\ColumnRelationship;
|
||||
use Appwrite\Utopia\Response\Model\ColumnString;
|
||||
use Appwrite\Utopia\Response\Model\ColumnURL;
|
||||
use Appwrite\Utopia\Response\Model\ConsoleVariables;
|
||||
use Appwrite\Utopia\Response\Model\Continent;
|
||||
use Appwrite\Utopia\Response\Model\Country;
|
||||
use Appwrite\Utopia\Response\Model\Currency;
|
||||
use Appwrite\Utopia\Response\Model\Database;
|
||||
use Appwrite\Utopia\Response\Model\Deployment;
|
||||
use Appwrite\Utopia\Response\Model\DetectionFramework;
|
||||
use Appwrite\Utopia\Response\Model\DetectionRuntime;
|
||||
use Appwrite\Utopia\Response\Model\DetectionVariable;
|
||||
use Appwrite\Utopia\Response\Model\DevKey;
|
||||
use Appwrite\Utopia\Response\Model\Document as ModelDocument;
|
||||
use Appwrite\Utopia\Response\Model\Error;
|
||||
use Appwrite\Utopia\Response\Model\ErrorDev;
|
||||
use Appwrite\Utopia\Response\Model\Execution;
|
||||
use Appwrite\Utopia\Response\Model\File;
|
||||
use Appwrite\Utopia\Response\Model\Framework;
|
||||
use Appwrite\Utopia\Response\Model\FrameworkAdapter;
|
||||
use Appwrite\Utopia\Response\Model\Func;
|
||||
use Appwrite\Utopia\Response\Model\Headers;
|
||||
use Appwrite\Utopia\Response\Model\HealthAntivirus;
|
||||
use Appwrite\Utopia\Response\Model\HealthCertificate;
|
||||
use Appwrite\Utopia\Response\Model\HealthQueue;
|
||||
use Appwrite\Utopia\Response\Model\HealthStatus;
|
||||
use Appwrite\Utopia\Response\Model\HealthTime;
|
||||
use Appwrite\Utopia\Response\Model\HealthVersion;
|
||||
use Appwrite\Utopia\Response\Model\Identity;
|
||||
use Appwrite\Utopia\Response\Model\Index;
|
||||
use Appwrite\Utopia\Response\Model\Installation;
|
||||
use Appwrite\Utopia\Response\Model\JWT;
|
||||
use Appwrite\Utopia\Response\Model\Key;
|
||||
use Appwrite\Utopia\Response\Model\Language;
|
||||
use Appwrite\Utopia\Response\Model\Locale;
|
||||
use Appwrite\Utopia\Response\Model\LocaleCode;
|
||||
use Appwrite\Utopia\Response\Model\Log;
|
||||
use Appwrite\Utopia\Response\Model\Membership;
|
||||
use Appwrite\Utopia\Response\Model\Message;
|
||||
use Appwrite\Utopia\Response\Model\Metric;
|
||||
use Appwrite\Utopia\Response\Model\MetricBreakdown;
|
||||
use Appwrite\Utopia\Response\Model\MFAChallenge;
|
||||
use Appwrite\Utopia\Response\Model\MFAFactors;
|
||||
use Appwrite\Utopia\Response\Model\MFARecoveryCodes;
|
||||
use Appwrite\Utopia\Response\Model\MFAType;
|
||||
use Appwrite\Utopia\Response\Model\Migration;
|
||||
use Appwrite\Utopia\Response\Model\MigrationFirebaseProject;
|
||||
use Appwrite\Utopia\Response\Model\MigrationReport;
|
||||
use Appwrite\Utopia\Response\Model\Mock;
|
||||
use Appwrite\Utopia\Response\Model\MockNumber;
|
||||
use Appwrite\Utopia\Response\Model\None;
|
||||
use Appwrite\Utopia\Response\Model\PaymentFeature;
|
||||
use Appwrite\Utopia\Response\Model\PaymentInvoice;
|
||||
use Appwrite\Utopia\Response\Model\PaymentPlan;
|
||||
use Appwrite\Utopia\Response\Model\PaymentPlanFeature;
|
||||
use Appwrite\Utopia\Response\Model\PaymentProviderConfig;
|
||||
use Appwrite\Utopia\Response\Model\PaymentSubscription;
|
||||
use Appwrite\Utopia\Response\Model\PaymentUsageEvent;
|
||||
use Appwrite\Utopia\Response\Model\Phone;
|
||||
use Appwrite\Utopia\Response\Model\Platform;
|
||||
use Appwrite\Utopia\Response\Model\Preferences;
|
||||
use Appwrite\Utopia\Response\Model\Project;
|
||||
use Appwrite\Utopia\Response\Model\Provider;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepository;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepositoryFramework;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntime;
|
||||
use Appwrite\Utopia\Response\Model\ResourceToken;
|
||||
use Appwrite\Utopia\Response\Model\Row;
|
||||
use Appwrite\Utopia\Response\Model\Rule;
|
||||
use Appwrite\Utopia\Response\Model\Runtime;
|
||||
use Appwrite\Utopia\Response\Model\Session;
|
||||
use Appwrite\Utopia\Response\Model\Site;
|
||||
use Appwrite\Utopia\Response\Model\Specification;
|
||||
use Appwrite\Utopia\Response\Model\Subscriber;
|
||||
use Appwrite\Utopia\Response\Model\Table;
|
||||
use Appwrite\Utopia\Response\Model\Target;
|
||||
use Appwrite\Utopia\Response\Model\Team;
|
||||
use Appwrite\Utopia\Response\Model\TemplateEmail;
|
||||
use Appwrite\Utopia\Response\Model\TemplateFramework;
|
||||
use Appwrite\Utopia\Response\Model\TemplateFunction;
|
||||
use Appwrite\Utopia\Response\Model\TemplateRuntime;
|
||||
use Appwrite\Utopia\Response\Model\TemplateSite;
|
||||
use Appwrite\Utopia\Response\Model\TemplateSMS;
|
||||
use Appwrite\Utopia\Response\Model\TemplateVariable;
|
||||
use Appwrite\Utopia\Response\Model\Token;
|
||||
use Appwrite\Utopia\Response\Model\Topic;
|
||||
use Appwrite\Utopia\Response\Model\Transaction;
|
||||
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||
use Appwrite\Utopia\Response\Model\UsageCollection;
|
||||
use Appwrite\Utopia\Response\Model\UsageDatabase;
|
||||
use Appwrite\Utopia\Response\Model\UsageDatabases;
|
||||
use Appwrite\Utopia\Response\Model\UsageFunction;
|
||||
use Appwrite\Utopia\Response\Model\UsageFunctions;
|
||||
use Appwrite\Utopia\Response\Model\UsageProject;
|
||||
use Appwrite\Utopia\Response\Model\UsageSite;
|
||||
use Appwrite\Utopia\Response\Model\UsageSites;
|
||||
use Appwrite\Utopia\Response\Model\UsageStorage;
|
||||
use Appwrite\Utopia\Response\Model\UsageTable;
|
||||
use Appwrite\Utopia\Response\Model\UsageUsers;
|
||||
use Appwrite\Utopia\Response\Model\User;
|
||||
use Appwrite\Utopia\Response\Model\Variable;
|
||||
use Appwrite\Utopia\Response\Model\VcsContent;
|
||||
use Appwrite\Utopia\Response\Model\Webhook;
|
||||
|
||||
// General
|
||||
Response::setModel(new None());
|
||||
Response::setModel(new Any());
|
||||
Response::setModel(new Error());
|
||||
Response::setModel(new ErrorDev());
|
||||
|
||||
// Lists
|
||||
Response::setModel(new BaseList('Rows List', Response::MODEL_ROW_LIST, 'rows', Response::MODEL_ROW));
|
||||
Response::setModel(new BaseList('Documents List', Response::MODEL_DOCUMENT_LIST, 'documents', Response::MODEL_DOCUMENT));
|
||||
Response::setModel(new BaseList('Tables List', Response::MODEL_TABLE_LIST, 'tables', Response::MODEL_TABLE));
|
||||
Response::setModel(new BaseList('Collections List', Response::MODEL_COLLECTION_LIST, 'collections', Response::MODEL_COLLECTION));
|
||||
Response::setModel(new BaseList('Databases List', Response::MODEL_DATABASE_LIST, 'databases', Response::MODEL_DATABASE));
|
||||
Response::setModel(new BaseList('Indexes List', Response::MODEL_INDEX_LIST, 'indexes', Response::MODEL_INDEX));
|
||||
Response::setModel(new BaseList('Column Indexes List', Response::MODEL_COLUMN_INDEX_LIST, 'indexes', Response::MODEL_COLUMN_INDEX));
|
||||
Response::setModel(new BaseList('Users List', Response::MODEL_USER_LIST, 'users', Response::MODEL_USER));
|
||||
Response::setModel(new BaseList('Sessions List', Response::MODEL_SESSION_LIST, 'sessions', Response::MODEL_SESSION));
|
||||
Response::setModel(new BaseList('Identities List', Response::MODEL_IDENTITY_LIST, 'identities', Response::MODEL_IDENTITY));
|
||||
Response::setModel(new BaseList('Logs List', Response::MODEL_LOG_LIST, 'logs', Response::MODEL_LOG));
|
||||
Response::setModel(new BaseList('Files List', Response::MODEL_FILE_LIST, 'files', Response::MODEL_FILE));
|
||||
Response::setModel(new BaseList('Buckets List', Response::MODEL_BUCKET_LIST, 'buckets', Response::MODEL_BUCKET));
|
||||
Response::setModel(new BaseList('Resource Tokens List', Response::MODEL_RESOURCE_TOKEN_LIST, 'tokens', Response::MODEL_RESOURCE_TOKEN));
|
||||
Response::setModel(new BaseList('Teams List', Response::MODEL_TEAM_LIST, 'teams', Response::MODEL_TEAM));
|
||||
Response::setModel(new BaseList('Memberships List', Response::MODEL_MEMBERSHIP_LIST, 'memberships', Response::MODEL_MEMBERSHIP));
|
||||
Response::setModel(new BaseList('Sites List', Response::MODEL_SITE_LIST, 'sites', Response::MODEL_SITE));
|
||||
Response::setModel(new BaseList('Site Templates List', Response::MODEL_TEMPLATE_SITE_LIST, 'templates', Response::MODEL_TEMPLATE_SITE));
|
||||
Response::setModel(new BaseList('Functions List', Response::MODEL_FUNCTION_LIST, 'functions', Response::MODEL_FUNCTION));
|
||||
Response::setModel(new BaseList('Function Templates List', Response::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', Response::MODEL_TEMPLATE_FUNCTION));
|
||||
Response::setModel(new BaseList('Installations List', Response::MODEL_INSTALLATION_LIST, 'installations', Response::MODEL_INSTALLATION));
|
||||
Response::setModel(new BaseList('Framework Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, 'frameworkProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK));
|
||||
Response::setModel(new BaseList('Runtime Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, 'runtimeProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME));
|
||||
Response::setModel(new BaseList('Branches List', Response::MODEL_BRANCH_LIST, 'branches', Response::MODEL_BRANCH));
|
||||
Response::setModel(new BaseList('Frameworks List', Response::MODEL_FRAMEWORK_LIST, 'frameworks', Response::MODEL_FRAMEWORK));
|
||||
Response::setModel(new BaseList('Runtimes List', Response::MODEL_RUNTIME_LIST, 'runtimes', Response::MODEL_RUNTIME));
|
||||
Response::setModel(new BaseList('Deployments List', Response::MODEL_DEPLOYMENT_LIST, 'deployments', Response::MODEL_DEPLOYMENT));
|
||||
Response::setModel(new BaseList('Executions List', Response::MODEL_EXECUTION_LIST, 'executions', Response::MODEL_EXECUTION));
|
||||
Response::setModel(new BaseList('Projects List', Response::MODEL_PROJECT_LIST, 'projects', Response::MODEL_PROJECT, true, false));
|
||||
Response::setModel(new BaseList('Webhooks List', Response::MODEL_WEBHOOK_LIST, 'webhooks', Response::MODEL_WEBHOOK, true, false));
|
||||
Response::setModel(new BaseList('API Keys List', Response::MODEL_KEY_LIST, 'keys', Response::MODEL_KEY, true, false));
|
||||
Response::setModel(new BaseList('Dev Keys List', Response::MODEL_DEV_KEY_LIST, 'devKeys', Response::MODEL_DEV_KEY, true, false));
|
||||
Response::setModel(new BaseList('Auth Providers List', Response::MODEL_AUTH_PROVIDER_LIST, 'platforms', Response::MODEL_AUTH_PROVIDER, true, false));
|
||||
Response::setModel(new BaseList('Platforms List', Response::MODEL_PLATFORM_LIST, 'platforms', Response::MODEL_PLATFORM, true, false));
|
||||
Response::setModel(new BaseList('Countries List', Response::MODEL_COUNTRY_LIST, 'countries', Response::MODEL_COUNTRY));
|
||||
Response::setModel(new BaseList('Continents List', Response::MODEL_CONTINENT_LIST, 'continents', Response::MODEL_CONTINENT));
|
||||
Response::setModel(new BaseList('Languages List', Response::MODEL_LANGUAGE_LIST, 'languages', Response::MODEL_LANGUAGE));
|
||||
Response::setModel(new BaseList('Currencies List', Response::MODEL_CURRENCY_LIST, 'currencies', Response::MODEL_CURRENCY));
|
||||
Response::setModel(new BaseList('Phones List', Response::MODEL_PHONE_LIST, 'phones', Response::MODEL_PHONE));
|
||||
Response::setModel(new BaseList('Metric List', Response::MODEL_METRIC_LIST, 'metrics', Response::MODEL_METRIC, true, false));
|
||||
Response::setModel(new BaseList('Variables List', Response::MODEL_VARIABLE_LIST, 'variables', Response::MODEL_VARIABLE));
|
||||
Response::setModel(new BaseList('Status List', Response::MODEL_HEALTH_STATUS_LIST, 'statuses', Response::MODEL_HEALTH_STATUS));
|
||||
Response::setModel(new BaseList('Rule List', Response::MODEL_PROXY_RULE_LIST, 'rules', Response::MODEL_PROXY_RULE));
|
||||
Response::setModel(new BaseList('Locale codes list', Response::MODEL_LOCALE_CODE_LIST, 'localeCodes', Response::MODEL_LOCALE_CODE));
|
||||
Response::setModel(new BaseList('Provider list', Response::MODEL_PROVIDER_LIST, 'providers', Response::MODEL_PROVIDER));
|
||||
Response::setModel(new BaseList('Message list', Response::MODEL_MESSAGE_LIST, 'messages', Response::MODEL_MESSAGE));
|
||||
Response::setModel(new BaseList('Topic list', Response::MODEL_TOPIC_LIST, 'topics', Response::MODEL_TOPIC));
|
||||
Response::setModel(new BaseList('Subscriber list', Response::MODEL_SUBSCRIBER_LIST, 'subscribers', Response::MODEL_SUBSCRIBER));
|
||||
Response::setModel(new BaseList('Target list', Response::MODEL_TARGET_LIST, 'targets', Response::MODEL_TARGET));
|
||||
Response::setModel(new BaseList('Transaction List', Response::MODEL_TRANSACTION_LIST, 'transactions', Response::MODEL_TRANSACTION));
|
||||
Response::setModel(new BaseList('Migrations List', Response::MODEL_MIGRATION_LIST, 'migrations', Response::MODEL_MIGRATION));
|
||||
Response::setModel(new BaseList('Migrations Firebase Projects List', Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', Response::MODEL_MIGRATION_FIREBASE_PROJECT));
|
||||
Response::setModel(new BaseList('Specifications List', Response::MODEL_SPECIFICATION_LIST, 'specifications', Response::MODEL_SPECIFICATION));
|
||||
Response::setModel(new BaseList('VCS Content List', Response::MODEL_VCS_CONTENT_LIST, 'contents', Response::MODEL_VCS_CONTENT));
|
||||
|
||||
// Entities
|
||||
Response::setModel(new Database());
|
||||
|
||||
// Collection API Models
|
||||
Response::setModel(new Collection());
|
||||
Response::setModel(new Attribute());
|
||||
Response::setModel(new AttributeList());
|
||||
Response::setModel(new AttributeString());
|
||||
Response::setModel(new AttributeInteger());
|
||||
Response::setModel(new AttributeFloat());
|
||||
Response::setModel(new AttributeBoolean());
|
||||
Response::setModel(new AttributeEmail());
|
||||
Response::setModel(new AttributeEnum());
|
||||
Response::setModel(new AttributeIP());
|
||||
Response::setModel(new AttributeURL());
|
||||
Response::setModel(new AttributeDatetime());
|
||||
Response::setModel(new AttributeRelationship());
|
||||
Response::setModel(new AttributePoint());
|
||||
Response::setModel(new AttributeLine());
|
||||
Response::setModel(new AttributePolygon());
|
||||
|
||||
// Table API Models
|
||||
Response::setModel(new Table());
|
||||
Response::setModel(new Column());
|
||||
Response::setModel(new ColumnList());
|
||||
Response::setModel(new ColumnString());
|
||||
Response::setModel(new ColumnInteger());
|
||||
Response::setModel(new ColumnFloat());
|
||||
Response::setModel(new ColumnBoolean());
|
||||
Response::setModel(new ColumnEmail());
|
||||
Response::setModel(new ColumnEnum());
|
||||
Response::setModel(new ColumnIP());
|
||||
Response::setModel(new ColumnURL());
|
||||
Response::setModel(new ColumnDatetime());
|
||||
Response::setModel(new ColumnRelationship());
|
||||
Response::setModel(new ColumnPoint());
|
||||
Response::setModel(new ColumnLine());
|
||||
Response::setModel(new ColumnPolygon());
|
||||
Response::setModel(new Index());
|
||||
Response::setModel(new ColumnIndex());
|
||||
Response::setModel(new Row());
|
||||
Response::setModel(new ModelDocument());
|
||||
Response::setModel(new Log());
|
||||
Response::setModel(new User());
|
||||
Response::setModel(new AlgoMd5());
|
||||
Response::setModel(new AlgoSha());
|
||||
Response::setModel(new AlgoPhpass());
|
||||
Response::setModel(new AlgoBcrypt());
|
||||
Response::setModel(new AlgoScrypt());
|
||||
Response::setModel(new AlgoScryptModified());
|
||||
Response::setModel(new AlgoArgon2());
|
||||
Response::setModel(new Account());
|
||||
Response::setModel(new Preferences());
|
||||
Response::setModel(new Session());
|
||||
Response::setModel(new Identity());
|
||||
Response::setModel(new Token());
|
||||
Response::setModel(new JWT());
|
||||
Response::setModel(new Locale());
|
||||
Response::setModel(new LocaleCode());
|
||||
Response::setModel(new File());
|
||||
Response::setModel(new Bucket());
|
||||
Response::setModel(new ResourceToken());
|
||||
Response::setModel(new Team());
|
||||
Response::setModel(new Membership());
|
||||
Response::setModel(new Site());
|
||||
Response::setModel(new TemplateSite());
|
||||
Response::setModel(new TemplateFramework());
|
||||
Response::setModel(new Func());
|
||||
Response::setModel(new TemplateFunction());
|
||||
Response::setModel(new TemplateRuntime());
|
||||
Response::setModel(new TemplateVariable());
|
||||
Response::setModel(new Installation());
|
||||
Response::setModel(new ProviderRepository());
|
||||
Response::setModel(new ProviderRepositoryFramework());
|
||||
Response::setModel(new ProviderRepositoryRuntime());
|
||||
Response::setModel(new DetectionFramework());
|
||||
Response::setModel(new DetectionRuntime());
|
||||
Response::setModel(new DetectionVariable());
|
||||
Response::setModel(new VcsContent());
|
||||
Response::setModel(new Branch());
|
||||
Response::setModel(new Runtime());
|
||||
Response::setModel(new Framework());
|
||||
Response::setModel(new FrameworkAdapter());
|
||||
Response::setModel(new Deployment());
|
||||
Response::setModel(new Execution());
|
||||
Response::setModel(new Project());
|
||||
Response::setModel(new Webhook());
|
||||
Response::setModel(new Key());
|
||||
Response::setModel(new DevKey());
|
||||
Response::setModel(new MockNumber());
|
||||
Response::setModel(new AuthProvider());
|
||||
Response::setModel(new Platform());
|
||||
Response::setModel(new Variable());
|
||||
Response::setModel(new Country());
|
||||
Response::setModel(new Continent());
|
||||
Response::setModel(new Language());
|
||||
Response::setModel(new Currency());
|
||||
Response::setModel(new Phone());
|
||||
Response::setModel(new HealthAntivirus());
|
||||
Response::setModel(new HealthQueue());
|
||||
Response::setModel(new HealthStatus());
|
||||
Response::setModel(new HealthCertificate());
|
||||
Response::setModel(new HealthTime());
|
||||
Response::setModel(new HealthVersion());
|
||||
Response::setModel(new Metric());
|
||||
Response::setModel(new MetricBreakdown());
|
||||
Response::setModel(new UsageDatabases());
|
||||
Response::setModel(new UsageDatabase());
|
||||
Response::setModel(new UsageTable());
|
||||
Response::setModel(new UsageCollection());
|
||||
Response::setModel(new UsageUsers());
|
||||
Response::setModel(new UsageStorage());
|
||||
Response::setModel(new UsageBuckets());
|
||||
Response::setModel(new UsageFunctions());
|
||||
Response::setModel(new UsageFunction());
|
||||
Response::setModel(new UsageSites());
|
||||
Response::setModel(new UsageSite());
|
||||
Response::setModel(new UsageProject());
|
||||
Response::setModel(new Headers());
|
||||
Response::setModel(new Specification());
|
||||
Response::setModel(new Rule());
|
||||
Response::setModel(new TemplateSMS());
|
||||
Response::setModel(new TemplateEmail());
|
||||
Response::setModel(new ConsoleVariables());
|
||||
Response::setModel(new MFAChallenge());
|
||||
Response::setModel(new MFARecoveryCodes());
|
||||
Response::setModel(new MFAType());
|
||||
Response::setModel(new MFAFactors());
|
||||
Response::setModel(new Provider());
|
||||
Response::setModel(new Message());
|
||||
Response::setModel(new Topic());
|
||||
Response::setModel(new Transaction());
|
||||
Response::setModel(new Subscriber());
|
||||
Response::setModel(new Target());
|
||||
Response::setModel(new Migration());
|
||||
Response::setModel(new MigrationReport());
|
||||
Response::setModel(new MigrationFirebaseProject());
|
||||
|
||||
// Payments
|
||||
Response::setModel(new PaymentPlan());
|
||||
Response::setModel(new PaymentFeature());
|
||||
Response::setModel(new PaymentSubscription());
|
||||
Response::setModel(new PaymentProviderConfig());
|
||||
Response::setModel(new PaymentPlanFeature());
|
||||
Response::setModel(new PaymentInvoice());
|
||||
Response::setModel(new PaymentUsageEvent());
|
||||
Response::setModel(new BaseList('Payment Subscription List', Response::MODEL_PAYMENT_SUBSCRIPTION_LIST, 'subscriptions', Response::MODEL_PAYMENT_SUBSCRIPTION, true, true));
|
||||
Response::setModel(new BaseList('Payment Plan List', Response::MODEL_PAYMENT_PLAN_LIST, 'plans', Response::MODEL_PAYMENT_PLAN, true, true));
|
||||
Response::setModel(new BaseList('Payment Feature List', Response::MODEL_PAYMENT_FEATURE_LIST, 'features', Response::MODEL_PAYMENT_FEATURE, true, true));
|
||||
Response::setModel(new BaseList('Payment Plan Feature List', Response::MODEL_PAYMENT_PLAN_FEATURE_LIST, 'features', Response::MODEL_PAYMENT_PLAN_FEATURE, true, true));
|
||||
Response::setModel(new BaseList('Payment Invoice List', Response::MODEL_PAYMENT_INVOICE_LIST, 'invoices', Response::MODEL_PAYMENT_INVOICE, true, true));
|
||||
Response::setModel(new BaseList('Payment Usage Event List', Response::MODEL_PAYMENT_USAGE_EVENT_LIST, 'events', Response::MODEL_PAYMENT_USAGE_EVENT, true, true));
|
||||
|
||||
// Tests (keep last)
|
||||
Response::setModel(new Mock());
|
||||
@@ -360,6 +360,8 @@ $register->set('smtp', function () {
|
||||
$mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', '');
|
||||
$mail->SMTPAutoTLS = false;
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->Timeout = 10; /* Connection timeout */
|
||||
$mail->getSMTPInstance()->Timelimit = 30; /* Timeout for each individual SMTP command (e.g. HELO, EHLO, etc.) */
|
||||
|
||||
$from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
|
||||
$email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
@@ -386,3 +388,8 @@ $register->set('promiseAdapter', function () {
|
||||
$register->set('hooks', function () {
|
||||
return new Hooks();
|
||||
});
|
||||
$register->set('registryPayments', function () {
|
||||
$registry = new \Appwrite\Payments\Provider\Registry();
|
||||
$registry->register('stripe', \Appwrite\Payments\Provider\StripeAdapter::class);
|
||||
return $registry;
|
||||
});
|
||||
|
||||
+17
-17
@@ -4,7 +4,7 @@ use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Key;
|
||||
use Appwrite\Databases\TransactionState;
|
||||
use Appwrite\Event\Audit;
|
||||
use Appwrite\Event\Audit as AuditEvent;
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
@@ -30,6 +30,8 @@ use Appwrite\Utopia\Response;
|
||||
use Executor\Executor;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Code;
|
||||
@@ -80,6 +82,9 @@ App::setResource('hooks', function ($register) {
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('register', fn () => $register);
|
||||
App::setResource('registryPayments', function ($register) {
|
||||
return $register->get('registryPayments');
|
||||
}, ['register']);
|
||||
App::setResource('locale', function () {
|
||||
$locale = new Locale(System::getEnv('_APP_LOCALE', 'en'));
|
||||
$locale->setFallback(System::getEnv('_APP_LOCALE', 'en'));
|
||||
@@ -146,7 +151,7 @@ App::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForAudits', function (Publisher $publisher) {
|
||||
return new Audit($publisher);
|
||||
return new AuditEvent($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
@@ -164,21 +169,9 @@ App::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
/**
|
||||
* Platform configuration
|
||||
*/
|
||||
App::setResource('platform', function (Request $request) {
|
||||
$platform = Config::getParam('platform', []);
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
|
||||
$port = '';
|
||||
if ($request->getPort() === '443' && $protocol !== 'https') {
|
||||
$port = ':443';
|
||||
}
|
||||
if ($request->getPort() === '80' && $protocol !== 'http') {
|
||||
$port = ':80';
|
||||
}
|
||||
$platform['endpoint'] = "$protocol://{$platform['apiHostname']}{$port}/v1";
|
||||
|
||||
return $platform;
|
||||
}, ['request']);
|
||||
App::setResource('platform', function () {
|
||||
return Config::getParam('platform', []);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* List of allowed request hostnames for the request.
|
||||
@@ -287,12 +280,14 @@ App::setResource('cors', fn (array $allowedHostnames) => new Cors(
|
||||
'X-Appwrite-ID',
|
||||
'X-Appwrite-Timestamp',
|
||||
'X-Appwrite-Session',
|
||||
'X-Appwrite-Platform', // for `$platform` injection and SDK generator
|
||||
// SDK generator
|
||||
'X-SDK-Version',
|
||||
'X-SDK-Name',
|
||||
'X-SDK-Language',
|
||||
'X-SDK-Platform',
|
||||
'X-SDK-GraphQL',
|
||||
'X-SDK-Profile',
|
||||
// Caching
|
||||
'Range',
|
||||
'Cache-Control',
|
||||
@@ -652,6 +647,11 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
};
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
App::setResource('audit', function ($dbForProject) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
return new Audit($adapter);
|
||||
}, ['dbForProject']);
|
||||
|
||||
App::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
|
||||
|
||||
@@ -11,6 +11,7 @@ $httpsPort = $this->getParam('httpsPort', '');
|
||||
$version = $this->getParam('version', '');
|
||||
$organization = $this->getParam('organization', '');
|
||||
$image = $this->getParam('image', '');
|
||||
$enableAssistant = $this->getParam('enableAssistant', false);
|
||||
?>services:
|
||||
traefik:
|
||||
image: traefik:2.11
|
||||
@@ -179,7 +180,7 @@ $image = $this->getParam('image', '');
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: <?php echo $organization; ?>/console:7.4.7
|
||||
image: <?php echo $organization; ?>/console:7.5.7
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -848,6 +849,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
|
||||
<?php if ($enableAssistant): ?>
|
||||
appwrite-assistant:
|
||||
image: appwrite/assistant:0.8.4
|
||||
container_name: appwrite-assistant
|
||||
@@ -857,6 +859,7 @@ $image = $this->getParam('image', '');
|
||||
- appwrite
|
||||
environment:
|
||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||
<?php endif; ?>
|
||||
|
||||
appwrite-browser:
|
||||
image: appwrite/browser:0.3.2
|
||||
|
||||
+20
-1
@@ -21,6 +21,8 @@ use Appwrite\Utopia\Database\Documents\User;
|
||||
use Executor\Executor;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit as UtopiaAudit;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -183,7 +185,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
->setSharedTables(true)
|
||||
->setNamespace('logsV1')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES_WORKER);
|
||||
|
||||
// set tenant
|
||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
@@ -380,6 +382,10 @@ Server::setResource('plan', function (array $plan = []) {
|
||||
return [];
|
||||
});
|
||||
|
||||
Server::setResource('registryPayments', function (Registry $register) {
|
||||
return $register->get('registryPayments');
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('certificates', function () {
|
||||
$email = System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'));
|
||||
if (empty($email)) {
|
||||
@@ -450,6 +456,19 @@ Server::setResource('logError', function (Registry $register, Document $project)
|
||||
|
||||
Server::setResource('executor', fn () => new Executor());
|
||||
|
||||
Server::setResource('getAudit', function (Database $dbForPlatform, callable $getProjectDB) {
|
||||
return function (Document $project) use ($dbForPlatform, $getProjectDB) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
$adapter = new AdapterDatabase($dbForPlatform);
|
||||
return new UtopiaAudit($adapter);
|
||||
}
|
||||
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
return new UtopiaAudit($adapter);
|
||||
};
|
||||
}, ['dbForPlatform', 'getProjectDB']);
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$platform = new Appwrite();
|
||||
$args = $platform->getEnv('argv');
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php doctor $@
|
||||
exec php /usr/src/code/app/cli.php doctor "$@"
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php install $@
|
||||
exec php /usr/src/code/app/cli.php install "$@"
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec php /usr/src/code/app/cli.php interval "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php maintenance $@
|
||||
exec php /usr/src/code/app/cli.php maintenance "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php migrate $@
|
||||
exec php /usr/src/code/app/cli.php migrate "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-count --type=failed $@
|
||||
exec php /usr/src/code/app/cli.php queue-count --type=failed "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-count --type=processing $@
|
||||
exec php /usr/src/code/app/cli.php queue-count --type=processing "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-count --type=success $@
|
||||
exec php /usr/src/code/app/cli.php queue-count --type=success "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-retry $@
|
||||
exec php /usr/src/code/app/cli.php queue-retry "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/realtime.php $@
|
||||
exec php /usr/src/code/app/realtime.php "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php schedule-executions $@
|
||||
exec php /usr/src/code/app/cli.php schedule-executions "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php schedule-functions $@
|
||||
exec php /usr/src/code/app/cli.php schedule-functions "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php schedule-messages $@
|
||||
exec php /usr/src/code/app/cli.php schedule-messages "$@"
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php schedule-payments-usage $@
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php screenshot $@
|
||||
exec php /usr/src/code/app/cli.php screenshot "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php sdks $@
|
||||
exec php /usr/src/code/app/cli.php sdks "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php specs $@
|
||||
exec php /usr/src/code/app/cli.php specs "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php ssl $@
|
||||
exec php /usr/src/code/app/cli.php ssl "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php stats-resources $@
|
||||
exec php /usr/src/code/app/cli.php stats-resources "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/src/code/vendor/bin/phpunit --configuration /usr/src/code/phpunit.xml $@
|
||||
exec /usr/src/code/vendor/bin/phpunit --configuration /usr/src/code/phpunit.xml "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php upgrade $@
|
||||
exec php /usr/src/code/app/cli.php upgrade "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php vars $@
|
||||
exec php /usr/src/code/app/cli.php vars "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php audits $@
|
||||
exec php /usr/src/code/app/worker.php audits "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php builds $@
|
||||
exec php /usr/src/code/app/worker.php builds "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php certificates $@
|
||||
exec php /usr/src/code/app/worker.php certificates "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php databases $@
|
||||
exec php /usr/src/code/app/worker.php databases "$@"
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php deletes $@
|
||||
exec php /usr/src/code/app/worker.php deletes "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php functions $@
|
||||
exec php /usr/src/code/app/worker.php functions "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php mails $@
|
||||
exec php /usr/src/code/app/worker.php mails "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php messaging $@
|
||||
exec php /usr/src/code/app/worker.php messaging "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php migrations $@
|
||||
exec php /usr/src/code/app/worker.php migrations "$@"
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php payments-usage-sync $@
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php stats-resources $@
|
||||
exec php /usr/src/code/app/worker.php stats-resources "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php stats-usage $@
|
||||
exec php /usr/src/code/app/worker.php stats-usage "$@"
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php webhooks $@
|
||||
exec php /usr/src/code/app/worker.php webhooks "$@"
|
||||
+6
-6
@@ -45,26 +45,26 @@
|
||||
"ext-sockets": "*",
|
||||
"appwrite/php-runtimes": "0.19.*",
|
||||
"appwrite/php-clamav": "2.0.*",
|
||||
"utopia-php/abuse": "1.*",
|
||||
"utopia-php/abuse": "1.*.*",
|
||||
"utopia-php/analytics": "0.10.*",
|
||||
"utopia-php/audit": "1.*",
|
||||
"utopia-php/audit": "2.0.2-rc3",
|
||||
"utopia-php/auth": "0.5.*",
|
||||
"utopia-php/cache": "0.13.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "1.*.*",
|
||||
"utopia-php/database": "3.*",
|
||||
"utopia-php/database": "3.*.*",
|
||||
"utopia-php/detector": "0.2.*",
|
||||
"utopia-php/domains": "0.9.*",
|
||||
"utopia-php/emails": "0.6.*",
|
||||
"utopia-php/dns": "1.4.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.4.*",
|
||||
"utopia-php/fetch": "0.5.*",
|
||||
"utopia-php/image": "0.8.*",
|
||||
"utopia-php/locale": "0.8.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.20.*",
|
||||
"utopia-php/migration": "1.3.*",
|
||||
"utopia-php/migration": "1.*.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.8.*",
|
||||
@@ -109,4 +109,4 @@
|
||||
"tbachert/spi": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+8149
-8957
File diff suppressed because it is too large
Load Diff
+123
-22
@@ -63,6 +63,8 @@ services:
|
||||
- 9501:80
|
||||
networks:
|
||||
- appwrite
|
||||
dns:
|
||||
- 172.16.238.100
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.constraint-label-stack=appwrite"
|
||||
@@ -99,6 +101,7 @@ services:
|
||||
depends_on:
|
||||
- mariadb
|
||||
- redis
|
||||
- coredns
|
||||
# - clamav
|
||||
entrypoint:
|
||||
- php
|
||||
@@ -227,7 +230,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:7.4.11
|
||||
image: appwrite/console:7.5.7
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -552,6 +555,7 @@ services:
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_DOMAIN_SITES
|
||||
- _APP_EMAIL_CERTIFICATES
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
@@ -750,6 +754,7 @@ services:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- mariadb
|
||||
- redis
|
||||
environment:
|
||||
- _APP_ENV
|
||||
@@ -782,6 +787,43 @@ services:
|
||||
- _APP_MAINTENANCE_START_TIME
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-task-interval:
|
||||
entrypoint: interval
|
||||
<<: *x-logging
|
||||
container_name: appwrite-task-interval
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- mariadb
|
||||
- redis
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_DOMAIN
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_DOMAIN_SITES
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_INTERVAL_DOMAIN_VERIFICATION
|
||||
|
||||
appwrite-task-stats-resources:
|
||||
container_name: appwrite-task-stats-resources
|
||||
entrypoint: stats-resources
|
||||
@@ -875,6 +917,65 @@ services:
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-worker-payments-usage:
|
||||
entrypoint: worker-payments-usage
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-payments-usage
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-task-scheduler-payments-usage:
|
||||
entrypoint: schedule-payments-usage
|
||||
<<: *x-logging
|
||||
container_name: appwrite-task-scheduler-payments-usage
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- mariadb
|
||||
- redis
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_PAYMENTS_USAGE_SYNC_INTERVAL
|
||||
|
||||
appwrite-task-scheduler-functions:
|
||||
entrypoint: schedule-functions
|
||||
<<: *x-logging
|
||||
@@ -1025,27 +1126,6 @@ services:
|
||||
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION
|
||||
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
|
||||
|
||||
openruntimes-proxy:
|
||||
container_name: openruntimes-proxy
|
||||
hostname: proxy
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/proxy:0.5.5
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
environment:
|
||||
- OPR_PROXY_WORKER_PER_CORE=$_APP_WORKER_PER_CORE
|
||||
- OPR_PROXY_ENV=$_APP_ENV
|
||||
- OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
|
||||
- OPR_PROXY_SECRET=$_APP_EXECUTOR_SECRET
|
||||
- OPR_PROXY_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
|
||||
- OPR_PROXY_ALGORITHM=random
|
||||
- OPR_PROXY_EXECUTORS=exc1
|
||||
- OPR_PROXY_HEALTHCHECK_INTERVAL=10000
|
||||
- OPR_PROXY_MAX_TIMEOUT=600
|
||||
- OPR_PROXY_HEALTHCHECK=enabled
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
container_name: appwrite-mariadb
|
||||
@@ -1080,6 +1160,21 @@ services:
|
||||
volumes:
|
||||
- appwrite-redis:/data:rw
|
||||
|
||||
coredns: # DNS server for testing purposes (Proxy APIs)
|
||||
image: coredns/coredns:1.12.4
|
||||
container_name: appwrite-coredns
|
||||
restart: unless-stopped
|
||||
<<: *x-logging
|
||||
command: ["-conf", "/mnt/resources/Corefile"]
|
||||
# If you need to debug CoreDNS, do it from "appwrite container", or port forward:
|
||||
# ports:
|
||||
# - "53:53"
|
||||
networks:
|
||||
appwrite:
|
||||
ipv4_address: 172.16.238.100
|
||||
volumes:
|
||||
- ./tests/resources/coredns:/mnt/resources:ro
|
||||
|
||||
# Dev Tools Start ------------------------------------------------------------------------------------------
|
||||
#
|
||||
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
|
||||
@@ -1113,6 +1208,9 @@ services:
|
||||
- "traefik.http.routers.appwrite_maildev_https.rule=Host(`mail.localhost`)"
|
||||
- "traefik.http.routers.appwrite_maildev_https.service=appwrite_maildev"
|
||||
- "traefik.http.routers.appwrite_maildev_https.tls=true"
|
||||
environment:
|
||||
- MAILDEV_INCOMING_USER=${_APP_SMTP_USERNAME}
|
||||
- MAILDEV_INCOMING_PASS=${_APP_SMTP_PASSWORD}
|
||||
|
||||
request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook)
|
||||
image: appwrite/requestcatcher:1.0.0
|
||||
@@ -1210,6 +1308,9 @@ networks:
|
||||
name: gateway
|
||||
appwrite:
|
||||
name: appwrite
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.16.238.0/24
|
||||
runtimes:
|
||||
name: runtimes
|
||||
|
||||
|
||||
@@ -14,7 +14,13 @@ databases.updateDocument(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
Map.of("a", "b"), // data (optional)
|
||||
Map.of(
|
||||
"username", "walter.obrien",
|
||||
"email", "walter.obrien@example.com",
|
||||
"fullName", "Walter O'Brien",
|
||||
"age", 33,
|
||||
"isAdmin", false
|
||||
), // data (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -14,7 +14,13 @@ databases.upsertDocument(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
Map.of("a", "b"), // data
|
||||
Map.of(
|
||||
"username", "walter.obrien",
|
||||
"email", "walter.obrien@example.com",
|
||||
"fullName", "Walter O'Brien",
|
||||
"age", 30,
|
||||
"isAdmin", false
|
||||
), // data (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -14,7 +14,13 @@ tablesDB.updateRow(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<TABLE_ID>", // tableId
|
||||
"<ROW_ID>", // rowId
|
||||
Map.of("a", "b"), // data (optional)
|
||||
Map.of(
|
||||
"username", "walter.obrien",
|
||||
"email", "walter.obrien@example.com",
|
||||
"fullName", "Walter O'Brien",
|
||||
"age", 33,
|
||||
"isAdmin", false
|
||||
), // data (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -14,7 +14,13 @@ tablesDB.upsertRow(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<TABLE_ID>", // tableId
|
||||
"<ROW_ID>", // rowId
|
||||
Map.of("a", "b"), // data (optional)
|
||||
Map.of(
|
||||
"username", "walter.obrien",
|
||||
"email", "walter.obrien@example.com",
|
||||
"fullName", "Walter O'Brien",
|
||||
"age", 33,
|
||||
"isAdmin", false
|
||||
), // data (optional)
|
||||
List.of(Permission.read(Role.any())), // permissions (optional)
|
||||
"<TRANSACTION_ID>", // transactionId (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
|
||||
@@ -14,7 +14,13 @@ val result = databases.updateDocument(
|
||||
databaseId = "<DATABASE_ID>",
|
||||
collectionId = "<COLLECTION_ID>",
|
||||
documentId = "<DOCUMENT_ID>",
|
||||
data = mapOf( "a" to "b" ), // (optional)
|
||||
data = mapOf(
|
||||
"username" to "walter.obrien",
|
||||
"email" to "walter.obrien@example.com",
|
||||
"fullName" to "Walter O'Brien",
|
||||
"age" to 33,
|
||||
"isAdmin" to false
|
||||
), // (optional)
|
||||
permissions = listOf(Permission.read(Role.any())), // (optional)
|
||||
transactionId = "<TRANSACTION_ID>", // (optional)
|
||||
)
|
||||
@@ -14,7 +14,13 @@ val result = databases.upsertDocument(
|
||||
databaseId = "<DATABASE_ID>",
|
||||
collectionId = "<COLLECTION_ID>",
|
||||
documentId = "<DOCUMENT_ID>",
|
||||
data = mapOf( "a" to "b" ),
|
||||
data = mapOf(
|
||||
"username" to "walter.obrien",
|
||||
"email" to "walter.obrien@example.com",
|
||||
"fullName" to "Walter O'Brien",
|
||||
"age" to 30,
|
||||
"isAdmin" to false
|
||||
), // (optional)
|
||||
permissions = listOf(Permission.read(Role.any())), // (optional)
|
||||
transactionId = "<TRANSACTION_ID>", // (optional)
|
||||
)
|
||||
@@ -14,7 +14,13 @@ val result = tablesDB.updateRow(
|
||||
databaseId = "<DATABASE_ID>",
|
||||
tableId = "<TABLE_ID>",
|
||||
rowId = "<ROW_ID>",
|
||||
data = mapOf( "a" to "b" ), // (optional)
|
||||
data = mapOf(
|
||||
"username" to "walter.obrien",
|
||||
"email" to "walter.obrien@example.com",
|
||||
"fullName" to "Walter O'Brien",
|
||||
"age" to 33,
|
||||
"isAdmin" to false
|
||||
), // (optional)
|
||||
permissions = listOf(Permission.read(Role.any())), // (optional)
|
||||
transactionId = "<TRANSACTION_ID>", // (optional)
|
||||
)
|
||||
@@ -14,7 +14,13 @@ val result = tablesDB.upsertRow(
|
||||
databaseId = "<DATABASE_ID>",
|
||||
tableId = "<TABLE_ID>",
|
||||
rowId = "<ROW_ID>",
|
||||
data = mapOf( "a" to "b" ), // (optional)
|
||||
data = mapOf(
|
||||
"username" to "walter.obrien",
|
||||
"email" to "walter.obrien@example.com",
|
||||
"fullName" to "Walter O'Brien",
|
||||
"age" to 33,
|
||||
"isAdmin" to false
|
||||
), // (optional)
|
||||
permissions = listOf(Permission.read(Role.any())), // (optional)
|
||||
transactionId = "<TRANSACTION_ID>", // (optional)
|
||||
)
|
||||
@@ -10,7 +10,13 @@ let document = try await databases.updateDocument(
|
||||
databaseId: "<DATABASE_ID>",
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
documentId: "<DOCUMENT_ID>",
|
||||
data: [:], // optional
|
||||
data: [
|
||||
"username": "walter.obrien",
|
||||
"email": "walter.obrien@example.com",
|
||||
"fullName": "Walter O'Brien",
|
||||
"age": 33,
|
||||
"isAdmin": false
|
||||
], // optional
|
||||
permissions: [Permission.read(Role.any())], // optional
|
||||
transactionId: "<TRANSACTION_ID>" // optional
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user