1
0
Fork 0
mirror of synced 2024-06-29 11:40:45 +12:00

Merge pull request #8295 from appwrite/sync-with-main

Sync with main
This commit is contained in:
Christy Jacob 2024-06-25 19:13:32 +04:00 committed by GitHub
commit 91fe8b7a8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
313 changed files with 3805 additions and 1340 deletions

6
.env
View file

@ -1,4 +1,5 @@
_APP_ENV=development
_APP_EDITION=self-hosted
_APP_LOCALE=en
_APP_WORKER_PER_CORE=6
_APP_CONSOLE_WHITELIST_ROOT=disabled
@ -8,14 +9,15 @@ _APP_CONSOLE_COUNTRIES_DENYLIST=AQ
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
_APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io
_APP_EMAIL_SECURITY=security@appwrite.io
_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
_APP_OPTIONS_ABUSE=disabled
_APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
_APP_DOMAIN=traefik
_APP_DOMAIN=localhost
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_TARGET=localhost
_APP_REDIS_HOST=redis

View file

@ -145,3 +145,6 @@ jobs:
- name: Run ${{matrix.service}} Tests
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
- name: Run ${{matrix.service}} Shared Tables Tests
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug

1
.gitignore vendored
View file

@ -14,3 +14,4 @@ app/sdks
dev/yasd_init.php
.phpunit.result.cache
Makefile
appwrite.json

2
.gitmodules vendored
View file

@ -1,4 +1,4 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
branch = 4.0.6
branch = 4.3.5

View file

@ -1,3 +1,121 @@
# Version 1.5.7
## What's Changed
### Fixes
* Fix database exception wrapping by @abnegate in https://github.com/appwrite/appwrite/pull/7787
* Fix exception wrap order by @abnegate in https://github.com/appwrite/appwrite/pull/7818
* Fix membership query to use internalId by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7834
* Fix vcs silent mode by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7683
* Fix function domain permissions by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7852
* Fix tests required for Cloud by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7777
* Fix OAuth error code by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7893
* Fix connection reclaim logic. by @eldadfux in https://github.com/appwrite/appwrite/pull/6886
* Fix shared queue name by @abnegate in https://github.com/appwrite/appwrite/pull/8092
* Fix syntax error by @abnegate in https://github.com/appwrite/appwrite/pull/8093
* Fix missing id attribute error by @abnegate in https://github.com/appwrite/appwrite/pull/8094
* Fix tests for CL by @lohanidamodar in https://github.com/appwrite/appwrite/pull/8076
* Fix project deletes for shared tables by @abnegate in https://github.com/appwrite/appwrite/pull/8107
* Handle SQL error code 'HY000' in realtime by @stnguyen90 in https://github.com/appwrite/appwrite/pull/8106
* Fix: Don't Override `robots.txt` for Other Domains by @ItzNotABug in https://github.com/appwrite/appwrite/pull/8185
* Escape function build command by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7808
* Create failed execution from worker if deployment doesn't exist by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7896
* Fix: admin mode on console by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7951
* Fix file size default limit by @shimonewman in https://github.com/appwrite/appwrite/pull/7843
* Fix: Python failing builds by @Meldiron in https://github.com/appwrite/appwrite/pull/8078
* Fix shared project delete by @abnegate in https://github.com/appwrite/appwrite/pull/8142
* Fix TextMagic class name by @stnguyen90 in https://github.com/appwrite/appwrite/pull/8132
* Prevent functions domain and subdomain to be added as custom domain by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7933
* Fix don't publish max users exceed by @vermakhushboo in https://github.com/appwrite/appwrite/pull/8067
* Fix invalid cache document id by @stnguyen90 in https://github.com/appwrite/appwrite/pull/8183
* Fix not hiding tokens for clients via realtime by @abnegate in https://github.com/appwrite/appwrite/pull/7870
### Miscellaneous
* Upload 400s to separate error logger by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7784
* Admin mode use teamInternalId by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7835
* Chore: update avatars API by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7840
* Use internal ids for query by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7838
* Remove cloud related scripts by @shimonewman in https://github.com/appwrite/appwrite/pull/7414
* Update VCS Comment by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7854
* Transaction and reconnection fixes by @fogelito in https://github.com/appwrite/appwrite/pull/7877
* Feat configurable collections by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7882
* Remove var_dump calls by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7884
* Storage DO adapter http version by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7905
* Update executor version by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7910
* Comment timer tick by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7911
* Update db for relationships and object as array attributes fixes by @abnegate in https://github.com/appwrite/appwrite/pull/7917
* Bump executor version to 0.5.1 by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7925
* Update database by @abnegate in https://github.com/appwrite/appwrite/pull/7937
* Reclaim only current connection by @abnegate in https://github.com/appwrite/appwrite/pull/7941
* Match memberships on internal ID by @abnegate in https://github.com/appwrite/appwrite/pull/7953
* Chore: queue retry update by @shimonewman in https://github.com/appwrite/appwrite/pull/7991
* Chore task addition by @shimonewman in https://github.com/appwrite/appwrite/pull/7992
* Databases.php collection not found by @fogelito in https://github.com/appwrite/appwrite/pull/7341
* Update database by @abnegate in https://github.com/appwrite/appwrite/pull/8036
* Feat upgrade db by @abnegate in https://github.com/appwrite/appwrite/pull/8050
* Handle string error codes by @fogelito in https://github.com/appwrite/appwrite/pull/7878
* Migration Logging Improvements by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/8057
* Remove logger code from avatars.php by @vermakhushboo in https://github.com/appwrite/appwrite/pull/8065
* Update chunk size to 7 MB by @vermakhushboo in https://github.com/appwrite/appwrite/pull/8060
* Shared tables support by @abnegate in https://github.com/appwrite/appwrite/pull/7206
* Ensure namespace is set if override equals shared tables by @abnegate in https://github.com/appwrite/appwrite/pull/8091
* Update database by @abnegate in https://github.com/appwrite/appwrite/pull/8095
* Disable sending realtime stats by @stnguyen90 in https://github.com/appwrite/appwrite/pull/8104
* Increase chunk size to 10 MB by @vermakhushboo in https://github.com/appwrite/appwrite/pull/8099
* Update db by @abnegate in https://github.com/appwrite/appwrite/pull/8113
* Update executor image name to exc-1 by @vermakhushboo in https://github.com/appwrite/appwrite/pull/8123
* Catch DB errors on delete by @abnegate in https://github.com/appwrite/appwrite/pull/8143
* Update Logger and migrations, implement sampler. by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/8146
* Increase shared tables projects by @abnegate in https://github.com/appwrite/appwrite/pull/8161
* Feat: improve cold start error, merge to cloud by @loks0n in https://github.com/appwrite/appwrite/pull/8165
* Add tests for scheduled functions by @vermakhushboo in https://github.com/appwrite/appwrite/pull/8164
* Remove throw PdoException in Error hook by @fogelito in https://github.com/appwrite/appwrite/pull/8169
* Refactor localdevice injection by @byawitz in https://github.com/appwrite/appwrite/pull/8173
* Usage sms per country code count by @shimonewman in https://github.com/appwrite/appwrite/pull/7592
* GetEnv on worker.php by @shimonewman in https://github.com/appwrite/appwrite/pull/8026
* Feat get env by @shimonewman in https://github.com/appwrite/appwrite/pull/8180
* Chore: remove compose version by @loks0n in https://github.com/appwrite/appwrite/pull/8148
* Chore update executor host default var by @abnegate in https://github.com/appwrite/appwrite/pull/8190
* Wrap realtime stats in an edition check by @abnegate in https://github.com/appwrite/appwrite/pull/8192
* Update executor image name by @vermakhushboo in https://github.com/appwrite/appwrite/pull/8147
* Feat: improve header demo values by @loks0n in https://github.com/appwrite/appwrite/pull/8089
* Feat: add warning header by @loks0n in https://github.com/appwrite/appwrite/pull/8063
# Version 1.5.6
## What's Changed
### Notable Changes
* Prevent functions domain to be used as custom domain in [#7934](https://github.com/appwrite/appwrite/pull/7934)
### Fixes
* Fix auth mode check in [#7980](https://github.com/appwrite/appwrite/pull/7980)
* Fix templates not copying hidden files in [#7610](https://github.com/appwrite/appwrite/pull/7610)
* Use `resourceInternalId` for Querying Function Deployments in [#8038](https://github.com/appwrite/appwrite/pull/8038)
* Fix Email OTP not verifying account in [#8084](https://github.com/appwrite/appwrite/pull/8084)
* Fix MFA email verification code font in [#8082](https://github.com/appwrite/appwrite/pull/8082)
* Don't kick user and require verification after enabling MFA in [#8081](https://github.com/appwrite/appwrite/pull/8081)
* Fix typo in credit-cards.php credit card image filename in [#8074](https://github.com/appwrite/appwrite/pull/8074)
* Fix Deprecated Warning in Doctor.php in [#8105](https://github.com/appwrite/appwrite/pull/8105)
* Set limit to retrieve all stats for the usage range in [#8117](https://github.com/appwrite/appwrite/pull/8117)
* Fix email used for name when user is created via Apple OAuth2 in [#8102](https://github.com/appwrite/appwrite/pull/8102)
### Miscellaneous
* Add GitHub action to close stale issues in [#7927](https://github.com/appwrite/appwrite/pull/7927)
* Document the standard we follow for country codes in [#8014](https://github.com/appwrite/appwrite/pull/8014)
* Add OSV Scanner for vulnerability scans in [#6506](https://github.com/appwrite/appwrite/pull/6506)
* Fix stale action close reason in [#8046](https://github.com/appwrite/appwrite/pull/8046)
* Add OSV Scanner for vulnerability scans in [#8021](https://github.com/appwrite/appwrite/pull/8021)
* Fix some typos in comments in [#7993](https://github.com/appwrite/appwrite/pull/7993)
* Replace missing domain paths in README.md in [#8049](https://github.com/appwrite/appwrite/pull/8049)
* Add the React Native SDK in [#7776](https://github.com/appwrite/appwrite/pull/7776)
* Bump database in [#8080](https://github.com/appwrite/appwrite/pull/8080)
* Add documentation for metrics in [#8088](https://github.com/appwrite/appwrite/pull/8088)
* Add new country Palestine with its translations in [#8031](https://github.com/appwrite/appwrite/pull/8031)
* Update users create token description in [#8129](https://github.com/appwrite/appwrite/pull/8129)
* Bump dependencies in [#8130](https://github.com/appwrite/appwrite/pull/8130)
# Version 1.5.5
## What's Changed
### Notable changes

View file

@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
RUN npm ci
RUN npm run build
FROM appwrite/base:0.9.0 as final
FROM appwrite/base:0.9.1 as final
LABEL maintainer="team@appwrite.io"

View file

@ -67,7 +67,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.5.5
appwrite/appwrite:1.5.7
```
### Windows
@ -79,7 +79,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.5.5
appwrite/appwrite:1.5.7
```
#### PowerShell
@ -89,7 +89,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.5.5
appwrite/appwrite:1.5.7
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。

View file

@ -75,7 +75,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.5.5
appwrite/appwrite:1.5.7
```
### Windows
@ -87,7 +87,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.5.5
appwrite/appwrite:1.5.7
```
#### PowerShell
@ -97,7 +97,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.5.5
appwrite/appwrite:1.5.7
```
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.

View file

@ -15,6 +15,7 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Logger\Log;
use Utopia\Platform\Service;
use Utopia\Pools\Group;
@ -98,25 +99,53 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
return $dbForConsole;
}
$databaseName = $project->getAttribute('database');
try {
$dsn = new DSN($project->getAttribute('database'));
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
if (isset($databases[$databaseName])) {
$database = $databases[$databaseName];
$database->setNamespace('_' . $project->getInternalId());
return $database;
}
$dbAdapter = $pools
->get($databaseName)
->get($dsn->getHost())
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$databases[$databaseName] = $database;
$databases[$dsn->getHost()] = $database;
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
$database
->setNamespace('_' . $project->getInternalId())
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId());
@ -173,7 +202,7 @@ CLI::setResource('logError', function (Registry $register) {
}, ['register']);
$platform = new Appwrite();
$platform->init(Service::TYPE_CLI);
$platform->init(Service::TYPE_TASK);
$cli = $platform->getCli();

View file

@ -390,7 +390,7 @@ $commonCollections = [
'$id' => ID::custom('_key_email'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['email'],
'lengths' => [320],
'lengths' => [256],
'orders' => [Database::ORDER_ASC],
],
[
@ -996,7 +996,7 @@ $commonCollections = [
'$id' => ID::custom('_key_provider_providerUid'),
'type' => Database::INDEX_KEY,
'attributes' => ['provider', 'providerUid'],
'lengths' => [100, 100],
'lengths' => [128, 128],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
@ -1120,14 +1120,14 @@ $commonCollections = [
'$id' => ID::custom('_key_userInternalId_provider_providerUid'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['userInternalId', 'provider', 'providerUid'],
'lengths' => [Database::LENGTH_KEY, 100, 385],
'lengths' => [11, 128, 128],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_provider_providerUid'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['provider', 'providerUid'],
'lengths' => [100, 640],
'lengths' => [128, 128],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
@ -1148,7 +1148,7 @@ $commonCollections = [
'$id' => ID::custom('_key_provider'),
'type' => Database::INDEX_KEY,
'attributes' => ['provider'],
'lengths' => [100],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
@ -3079,7 +3079,7 @@ $projectCollections = array_merge([
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [768],
'lengths' => [256],
'orders' => [Database::ORDER_ASC],
],
[
@ -3128,7 +3128,7 @@ $projectCollections = array_merge([
'$id' => ID::custom('_key_runtime'),
'type' => Database::INDEX_KEY,
'attributes' => ['runtime'],
'lengths' => [768],
'lengths' => [64],
'orders' => [Database::ORDER_ASC],
],
[
@ -3868,14 +3868,14 @@ $projectCollections = array_merge([
'$id' => ID::custom('_key_trigger'),
'type' => Database::INDEX_KEY,
'attributes' => ['trigger'],
'lengths' => [128],
'lengths' => [32],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_status'),
'type' => Database::INDEX_KEY,
'attributes' => ['status'],
'lengths' => [128],
'lengths' => [32],
'orders' => [Database::ORDER_ASC],
],
[
@ -5865,14 +5865,14 @@ $bucketCollections = [
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [768],
'lengths' => [256],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_signature'),
'type' => Database::INDEX_KEY,
'attributes' => ['signature'],
'lengths' => [768],
'lengths' => [256],
'orders' => [Database::ORDER_ASC],
],
[

View file

@ -134,7 +134,7 @@ return [
Exception::USER_COUNT_EXCEEDED => [
'name' => Exception::USER_COUNT_EXCEEDED,
'description' => 'The current project has exceeded the maximum number of users. Please check your user limit in the Appwrite console.',
'code' => 501,
'code' => 400,
],
Exception::USER_CONSOLE_COUNT_EXCEEDED => [
'name' => Exception::USER_CONSOLE_COUNT_EXCEEDED,
@ -524,6 +524,11 @@ return [
'description' => 'Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".',
'code' => 404,
],
Exception::FUNCTION_SYNCHRONOUS_TIMEOUT => [
'name' => Exception::FUNCTION_SYNCHRONOUS_TIMEOUT,
'description' => 'Synchronous function execution timed out. Use asynchronous execution instead, or ensure the execution duration doesn\'t exceed 30 seconds.',
'code' => 408,
],
/** Builds */
Exception::BUILD_NOT_FOUND => [
@ -678,6 +683,11 @@ return [
'description' => 'The attribute type is invalid.',
'code' => 400,
],
Exception::RELATIONSHIP_VALUE_INVALID => [
'name' => Exception::RELATIONSHIP_VALUE_INVALID,
'description' => 'The relationship value is invalid.',
'code' => 400,
],
/** Indexes */
Exception::INDEX_NOT_FOUND => [
@ -720,7 +730,7 @@ return [
Exception::PROJECT_PROVIDER_UNSUPPORTED => [
'name' => Exception::PROJECT_PROVIDER_UNSUPPORTED,
'description' => 'The chosen OAuth provider is unsupported. Please check the <a href="/docs/client/account?sdk=web-default#accountCreateOAuth2Session">Create OAuth2 Session docs</a> for the complete list of supported OAuth providers.',
'code' => 501,
'code' => 400,
],
Exception::PROJECT_INVALID_SUCCESS_URL => [
'name' => Exception::PROJECT_INVALID_SUCCESS_URL,

View file

@ -5,7 +5,7 @@
<table border="0" cellspacing="0" cellpadding="0" style="padding-top: 10px; padding-bottom: 10px; display: inline-block;">
<tr>
<td align="center" style="border-radius: 8px; background-color: #ffffff;">
<p style="font-size: 24px; text-indent: 18px; letter-spacing: 18px; font-family: Inter; color: #414146; text-decoration: none; border-radius: 8px; padding: 24px 12px; border: 1px solid #EDEDF0; display: inline-block; font-weight: bold; ">{{otp}}</p>
<p style="font-size: 24px; text-indent: 18px; letter-spacing: 18px; font-family: 'Inter', sans-serif; color: #414146; text-decoration: none; border-radius: 8px; padding: 24px 12px; border: 1px solid #EDEDF0; display: inline-block; font-weight: bold; ">{{otp}}</p>
</td>
</tr>
</table>

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroasië",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "غرينادا",
"countries.gt": "غواتيمالا",
"countries.gy": "غيانا",
"countries.hk": "هونغ كونغ",
"countries.hn": "هندوراس",
"countries.hr": "كرواتيا",
"countries.ht": "هايتي",

View file

@ -100,6 +100,7 @@
"countries.gd": "গ্ৰেনাডা",
"countries.gt": "গুয়াতেমালা",
"countries.gy": "গায়ানা",
"countries.hk": "হং কং",
"countries.hn": "হণ্ডুৰাছ",
"countries.hr": "ক্ৰোৱেছিয়া",
"countries.ht": "হাইতি",

View file

@ -100,6 +100,7 @@
"countries.gd": "Qrenada",
"countries.gt": "Qvatemala",
"countries.gy": "Guyana",
"countries.hk": "Honq Konq",
"countries.hn": "Honduras",
"countries.hr": "Xorvatiya",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Грэнада",
"countries.gt": "Гватэмала",
"countries.gy": "Гаяна",
"countries.hk": "Гонконг",
"countries.hn": "Гандурас",
"countries.hr": "Харватыя",
"countries.ht": "Гаіці",

View file

@ -100,6 +100,7 @@
"countries.gd": "Гренада",
"countries.gt": "Гватемала",
"countries.gy": "Гвиана",
"countries.hk": "Хонг Конг",
"countries.hn": "Хондурас",
"countries.hr": "Хърватия",
"countries.ht": "Хаити",

View file

@ -100,6 +100,7 @@
"countries.gd": "ग्रेनाडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गयाना",
"countries.hk": "हांगकांग",
"countries.hn": "होंडुरस",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",

View file

@ -100,6 +100,7 @@
"countries.gd": "গ্রেনাডা",
"countries.gt": "গুয়াতেমালা",
"countries.gy": "গায়ানা",
"countries.hk": "হংকং",
"countries.hn": "হন্ডুরাস",
"countries.hr": "ক্রোয়েশিয়া",
"countries.ht": "হাইতি",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Gvajana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hrvatska",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guaiana",
"countries.hk": "Hong Kong",
"countries.hn": "Hondures",
"countries.hr": "Croàcia",
"countries.ht": "Haití",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Chorvatsko",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatien",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatien",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Γρενάδα",
"countries.gt": "Γουατεμάλα",
"countries.gy": "Γουιάνα",
"countries.hk": "Χονγκ Κονγκ",
"countries.hn": "Ονδούρα",
"countries.hr": "Κροατία",
"countries.ht": "Αϊτή",

View file

@ -117,6 +117,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",

View file

@ -99,6 +99,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guayana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croacia",
"countries.ht": "Haití",

View file

@ -100,6 +100,7 @@
"countries.gd": "گرنادا",
"countries.gt": "گواتمالا",
"countries.gy": "گویان",
"countries.hk": "هنگ کنگ",
"countries.hn": "هندوراس",
"countries.hr": "کرواسی",
"countries.ht": "هائیتی",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Gujana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenade",
"countries.gt": "Guatemala",
"countries.gy": "Guyane",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatie",
"countries.ht": "Haïti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Greanáda",
"countries.gt": "Guatamala",
"countries.gy": "An Ghuáin",
"countries.hk": "Hong Kong",
"countries.hn": "Hondúras",
"countries.hr": "An Chróit",
"countries.ht": "Háítí",

View file

@ -100,6 +100,7 @@
"countries.gd": "ગ્રેનાડા",
"countries.gt": "ગ્વાટેમાલા",
"countries.gy": "ગુયાના",
"countries.hk": "હોંગ કોંગ",
"countries.hn": "હોન્ડુરાસ",
"countries.hr": "ક્રોએશિયા",
"countries.ht": "હૈતી",

View file

@ -100,6 +100,7 @@
"countries.gd": "גרנדה",
"countries.gt": "גואטמלה",
"countries.gy": "גיאנה",
"countries.hk": "הונג קונג",
"countries.hn": "הונדורס",
"countries.hr": "קרואטיה",
"countries.ht": "האיטי",

View file

@ -100,6 +100,7 @@
"countries.gd": "ग्रेनाडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गयाना",
"countries.hk": "हांग कांग",
"countries.hn": "होंडुरस",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gvajana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hrvatska",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Horvátország",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Գրենադա",
"countries.gt": "Գվատեմալա",
"countries.gy": "Գայանա",
"countries.hk": "Հոնգ Կոնգ",
"countries.hn": "Գոնդուրաս",
"countries.hr": "Խորվաթիա",
"countries.ht": "Հաիթի",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroasia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Hondúras",
"countries.hr": "Króatía",
"countries.ht": "Haítí",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croazia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "グレナダ",
"countries.gt": "グアテマラ",
"countries.gy": "ガイアナ",
"countries.hk": "香港",
"countries.hn": "ホンジュラス",
"countries.hr": "クロアチア",
"countries.ht": "ハイチ",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "ហ្គ្រេណាដា",
"countries.gt": "ហ្គាតេម៉ាឡា",
"countries.gy": "ហ្គីយ៉ាណា",
"countries.hk": "ហុងកុង",
"countries.hn": "ហុងឌូរ៉ាស",
"countries.hr": "ក្រូអាត",
"countries.ht": "ហៃទី",

View file

@ -100,6 +100,7 @@
"countries.gd": "ಗ್ರೆನಡಾ",
"countries.gt": "ಗ್ವಾಟೆಮಾಲಾ",
"countries.gy": "ಗಯಾನಾ",
"countries.hk": "ಹಾಂಗ್ ಕಾಂಗ್",
"countries.hn": "ಹೊಂಡುರಾಸ್",
"countries.hr": "ಕ್ರೊಯೇಷಿಯಾ",
"countries.ht": "ಹೈಟಿ",

View file

@ -100,6 +100,7 @@
"countries.gd": "그레나다",
"countries.gt": "과테말라",
"countries.gy": "기아나",
"countries.hk": "홍콩",
"countries.hn": "온두라스",
"countries.hr": "크로아티아",
"countries.ht": "아이티",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
@ -250,4 +251,4 @@
"emails.certificate.thanks": "Gratias",
"emails.certificate.signature": "team {{project}}",
"sms.verification.body": "{{secret}}"
}
}

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gajana",
"countries.hk": "Honkongas",
"countries.hn": "Hondūras",
"countries.hr": "Kroatija",
"countries.ht": "Haitis",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gajāna",
"countries.hk": "Honkonga",
"countries.hn": "Hondurasa",
"countries.hr": "Horvātija",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "ഗ്രെനാഡ",
"countries.gt": "ഗ്വാട്ടിമാല",
"countries.gy": "ഗയാന",
"countries.hk": "ഹോങ്കോങ്",
"countries.hn": "ഹോണ്ടുറാസ്",
"countries.hr": "ക്രോയേഷ്യ",
"countries.ht": "ഹെയ്തി",

View file

@ -100,6 +100,7 @@
"countries.gd": "ग्रेनाडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गुयाना",
"countries.hk": "हाँगकांग",
"countries.hn": "होंडुरास",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",

View file

@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "ग्रेनेडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गुयाना",
"countries.hk": "हाँगकाँग",
"countries.hn": "होन्डुरस",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hongkong",
"countries.hn": "Honduras",
"countries.hr": "Croatië",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "ଗ୍ରେନାଡା",
"countries.gt": "ଗୁଆଟେମାଲା",
"countries.gy": "ଗୁଇନ୍ଦା",
"countries.hk": "ହଂ କଙ୍ଗ",
"countries.hn": "ହୋଣ୍ଡୁରାସ୍",
"countries.hr": "କ୍ରୋଏସିଆ",
"countries.ht": "ହାଇତି",

View file

@ -100,6 +100,7 @@
"countries.gd": "ਗ੍ਰੇਨਾਡਾ",
"countries.gt": "ਗੁਆਟੇਮਾਲਾ",
"countries.gy": "ਗੇਆਨਾ",
"countries.hk": "ਹਾਂਗ ਕਾਂਗ",
"countries.hn": "ਹੌਂਡੂਰਸ",
"countries.hr": "ਕਰੋਸ਼ੀਆ",
"countries.ht": "ਹੈਤੀ",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gwatemala",
"countries.gy": "Gujana",
"countries.hk": "Hongkong",
"countries.hn": "Honduras",
"countries.hr": "Chorwacja",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guiana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croácia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guiana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croácia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croația",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Гренада",
"countries.gt": "Гватемала",
"countries.gy": "Гайана",
"countries.hk": "Гонконг",
"countries.hn": "Гондурас",
"countries.hr": "Хорватия",
"countries.ht": "Гаити",

View file

@ -100,6 +100,7 @@
"countries.gd": "ग्रेनेडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गुयाना",
"countries.hk": "हांगकांग",
"countries.hn": "होंडुरस्‌",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",

View file

@ -100,6 +100,7 @@
"countries.gd": "گرينڊا",
"countries.gt": "گٽيمالا",
"countries.gy": "گيانا",
"countries.hk": "هانگ کانگ",
"countries.hn": "هونڊرس",
"countries.hr": "ڪوريٽيا",
"countries.ht": "هيٽي",

View file

@ -100,6 +100,7 @@
"countries.gd": "ග්‍රෙනාඩා",
"countries.gt": "ග්වාතමාලාව",
"countries.gy": "ගයනා",
"countries.hk": "හොංකොං",
"countries.hn": "හොන්ඩුරාස්",
"countries.hr": "ක්‍රොඒෂියාව",
"countries.ht": "හයිටි",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hongkong",
"countries.hn": "Honduras",
"countries.hr": "Chorvátsko",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gvajana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hrvaška",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guajana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroacia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatien",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "கிரெனடா",
"countries.gt": "குவாத்தமாலா",
"countries.gy": "கயானா",
"countries.hk": "ஹாங்காங்",
"countries.hn": "ஹொண்டூராஸ்",
"countries.hr": "குரோவாசியா",
"countries.ht": "ஹைத்தி",

View file

@ -100,6 +100,7 @@
"countries.gd": "గ్రెనడా",
"countries.gt": "గ్వాటెమాల",
"countries.gy": "గయానా",
"countries.hk": "హాంగ్ కొంగ",
"countries.hn": "హోండురాస్",
"countries.hr": "క్రొయేషియా",
"countries.ht": "హైతీ",

View file

@ -100,6 +100,7 @@
"countries.gd": "เกรเนดา",
"countries.gt": "กัวเตมาลา",
"countries.gy": "กายอานา",
"countries.hk": "ฮ่องกง",
"countries.hn": "ฮอนดูรัส",
"countries.hr": "โครเอเชีย",
"countries.ht": "ไฮติ",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guwatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroasya",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hırvatistan",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "Гренада",
"countries.gt": "Гватемала",
"countries.gy": "Гайана",
"countries.hk": "Гонконг",
"countries.hn": "Гондурас",
"countries.hr": "Хорватія",
"countries.ht": "Гаїті",

View file

@ -100,6 +100,7 @@
"countries.gd": "گریناڈا",
"countries.gt": "گوئٹے مالا",
"countries.gy": "گیانا",
"countries.hk": "ہانگ کانگ",
"countries.hn": "ہونڈوراس",
"countries.hr": "کروشیا",
"countries.ht": "ہیٹی",

View file

@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",

View file

@ -100,6 +100,7 @@
"countries.gd": "格林纳达",
"countries.gt": "危地马拉",
"countries.gy": "圭亚那",
"countries.hk": "香港",
"countries.hn": "洪都拉斯",
"countries.hr": "克罗地亚",
"countries.ht": "海地",

View file

@ -100,6 +100,7 @@
"countries.gd": "格瑞那達",
"countries.gt": "瓜地馬拉",
"countries.gy": "蓋亞那",
"countries.hk": "香港",
"countries.hn": "宏都拉斯",
"countries.hr": "克羅埃西亞",
"countries.ht": "海地",

View file

@ -15,7 +15,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '14.0.0',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@ -63,7 +63,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '12.0.3',
'version' => '12.0.4',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@ -81,7 +81,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '5.0.0',
'version' => '6.0.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@ -116,7 +116,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '5.0.0',
'version' => '5.1.1',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@ -135,6 +135,24 @@ return [
'Java' => 'java',
],
],
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.4.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
'beta' => true,
'dev' => false,
'hidden' => false,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/client-react-native'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-react-native.git',
'gitRepoName' => 'sdk-for-react-native',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
],
[
'key' => 'graphql',
'name' => 'GraphQL',
@ -185,7 +203,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '0.6.1',
'version' => '0.6.3',
'url' => 'https://github.com/appwrite/sdk-for-console',
'package' => '',
'enabled' => true,
@ -203,7 +221,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '5.0.2',
'version' => '5.0.5',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@ -231,7 +249,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '12.0.1',
'version' => '13.0.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@ -249,7 +267,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '10.0.1',
'version' => '11.0.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@ -267,7 +285,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '11.0.1',
'version' => '11.0.2',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@ -285,7 +303,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '5.0.2',
'version' => '5.0.3',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@ -303,7 +321,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '11.0.1',
'version' => '11.0.2',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@ -321,7 +339,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '4.0.0',
'version' => '4.0.1',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => '',
'enabled' => false,
@ -339,7 +357,7 @@ return [
[
'key' => 'java',
'name' => 'Java',
'version' => '4.0.1',
'version' => '4.0.2',
'url' => 'https://github.com/appwrite/sdk-for-java',
'package' => '',
'enabled' => false,
@ -357,7 +375,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.8.1',
'version' => '0.8.2',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@ -375,7 +393,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '11.0.2',
'version' => '11.0.3',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@ -393,7 +411,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '5.0.1',
'version' => '5.0.2',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@ -415,7 +433,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '5.0.1',
'version' => '5.0.2',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -162,13 +162,31 @@ return [
],
[
'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS',
'description' => 'This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload.',
'description' => 'Deprecated since 1.5.1 use _APP_EMAIL_SECURITY and _APP_EMAIL_CERTIFICATES instead',
'introduction' => '0.7.0',
'default' => 'certs@appwrite.io',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_EMAIL_SECURITY',
'description' => 'This is the email address used as the user agent in your webhooks payload.',
'introduction' => '1.5.1',
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_EMAIL_CERTIFICATES',
'description' => 'This is the email address used to issue SSL certificates for custom domains',
'introduction' => '1.5.1',
'default' => '',
'required' => true,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_USAGE_STATS',
'description' => 'This variable allows you to disable the collection and displaying of usage stats. This value is set to \'enabled\' by default, to disable the usage stats set the value to \'disabled\'. When disabled, it\'s recommended to turn off the Worker Usage container to reduce resource usage.',
@ -450,7 +468,7 @@ return [
],
[
'name' => '_APP_SMS_FROM',
'description' => 'Phone number used for sending out messages. Must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789).',
'description' => 'Phone number used for sending out messages. If using Twilio, this may be a Messaging Service SID, starting with MG. Otherwise, the number must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789). ',
'introduction' => '0.15.0',
'default' => '',
'required' => false,
@ -756,7 +774,7 @@ return [
],
[
'name' => '_APP_EXECUTOR_SECRET',
'description' => 'The secret key used by Appwrite to communicate with the function executor. Make sure to change this!',
'description' => 'The secret key used by Appwrite to communicate with the function executor. Make sure to change this.',
'introduction' => '0.13.0',
'default' => 'your-secret-key',
'required' => false,
@ -765,9 +783,9 @@ return [
],
[
'name' => '_APP_EXECUTOR_HOST',
'description' => 'The host used by Appwrite to communicate with the function executor!',
'description' => 'The host used by Appwrite to communicate with the function executor.',
'introduction' => '0.13.0',
'default' => 'http://appwrite-executor/v1',
'default' => 'http://exc1/v1',
'required' => false,
'overwrite' => true,
'question' => '',
@ -775,7 +793,7 @@ return [
],
[
'name' => '_APP_EXECUTOR_RUNTIME_NETWORK',
'description' => 'Deprecated with 0.14.0, use \'OPEN_RUNTIMES_NETWORK\' instead!',
'description' => 'Deprecated with 0.14.0, use \'OPEN_RUNTIMES_NETWORK\' instead.',
'introduction' => '0.13.0',
'default' => 'appwrite_runtimes',
'required' => false,
@ -784,7 +802,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_ENVS',
'description' => 'Deprecated with 0.8.0, use \'_APP_FUNCTIONS_RUNTIMES\' instead!',
'description' => 'Deprecated with 0.8.0, use \'_APP_FUNCTIONS_RUNTIMES\' instead.',
'introduction' => '0.7.0',
'default' => 'node-16.0,php-7.4,python-3.9,ruby-3.0',
'required' => false,
@ -802,7 +820,7 @@ return [
],
[
'name' => 'DOCKERHUB_PULL_USERNAME',
'description' => 'Deprecated with 1.2.0, use \'_APP_DOCKER_HUB_USERNAME\' instead!',
'description' => 'Deprecated with 1.2.0, use \'_APP_DOCKER_HUB_USERNAME\' instead.',
'introduction' => '0.10.0',
'default' => '',
'required' => false,
@ -811,7 +829,7 @@ return [
],
[
'name' => 'DOCKERHUB_PULL_PASSWORD',
'description' => 'Deprecated with 1.2.0, use \'_APP_DOCKER_HUB_PASSWORD\' instead!',
'description' => 'Deprecated with 1.2.0, use \'_APP_DOCKER_HUB_PASSWORD\' instead.',
'introduction' => '0.10.0',
'default' => '',
'required' => false,
@ -829,7 +847,7 @@ return [
],
[
'name' => 'OPEN_RUNTIMES_NETWORK',
'description' => 'Deprecated with 1.2.0, use \'_APP_FUNCTIONS_RUNTIMES_NETWORK\' instead!',
'description' => 'Deprecated with 1.2.0, use \'_APP_FUNCTIONS_RUNTIMES_NETWORK\' instead.',
'introduction' => '0.13.0',
'default' => 'appwrite_runtimes',
'required' => false,

View file

@ -86,8 +86,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$factor = (match ($verifiedToken->getAttribute('type')) {
Auth::TOKEN_TYPE_MAGIC_URL,
Auth::TOKEN_TYPE_OAUTH2,
Auth::TOKEN_TYPE_EMAIL => 'email',
Auth::TOKEN_TYPE_PHONE => 'phone',
Auth::TOKEN_TYPE_EMAIL => Type::EMAIL,
Auth::TOKEN_TYPE_PHONE => Type::PHONE,
Auth::TOKEN_TYPE_GENERIC => 'token',
default => throw new Exception(Exception::USER_INVALID_TOKEN)
});
@ -290,7 +290,9 @@ App::post('/v1/account')
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
if($existingTarget) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
$dbForProject->purgeCachedDocument('users', $user->getId());
@ -1070,17 +1072,15 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
$domain = $request->getHostname();
$protocol = $request->getProtocol();
$params = $request->getParams();
$params['project'] = $projectId;
unset($params['projectId']);
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
. \http_build_query([
'project' => $projectId,
'code' => $code,
'state' => $state,
'error' => $error,
'error_description' => $error_description
]));
. \http_build_query($params));
});
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
@ -1103,17 +1103,15 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
$domain = $request->getHostname();
$protocol = $request->getProtocol();
$params = $request->getParams();
$params['project'] = $projectId;
unset($params['projectId']);
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
. \http_build_query([
'project' => $projectId,
'code' => $code,
'state' => $state,
'error' => $error,
'error_description' => $error_description
]));
. \http_build_query($params));
});
App::get('/v1/account/sessions/oauth2/:provider/redirect')
@ -1240,7 +1238,17 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$failureRedirect(Exception::USER_MISSING_ID);
}
$name = $oauth2->getUserName($accessToken);
$name = '';
$nameOAuth = $oauth2->getUserName($accessToken);
$userParam = \json_decode($request->getParam('user'), true);
if (!empty($nameOAuth)) {
$name = $nameOAuth;
} elseif (is_array($userParam)) {
$nameParam = $userParam['name'];
if (is_array($nameParam) && isset($nameParam['firstName']) && isset($nameParam['lastName'])) {
$name = $nameParam['firstName'] . ' ' . $nameParam['lastName'];
}
}
$email = $oauth2->getUserEmail($accessToken);
// Check if this identity is connected to a different user
@ -1500,7 +1508,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'factors' => ['email'],
'factors' => [TYPE::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => DateTime::addSeconds(new \DateTime(), $duration)
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
@ -1544,7 +1552,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$target
->setAttribute('sessionId', $session->getId())
->setAttrubte('sessionInternalId', $session->getInternalId());
->setAttribute('sessionInternalId', $session->getInternalId());
$dbForProject->updateDocument('targets', $target->getId(), $target);
}
@ -1857,15 +1865,16 @@ App::post('/v1/account/tokens/magic-url')
->setRecipient($email)
->trigger();
$queueForEvents->setPayload(
$response->output(
$token->setAttribute('secret', $tokenSecret),
Response::MODEL_TOKEN
)
);
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
@ -1873,8 +1882,7 @@ App::post('/v1/account/tokens/magic-url')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($token, Response::MODEL_TOKEN)
;
->dynamic($token, Response::MODEL_TOKEN);
});
App::post('/v1/account/tokens/email')
@ -2086,15 +2094,16 @@ App::post('/v1/account/tokens/email')
->setRecipient($email)
->trigger();
$queueForEvents->setPayload(
$response->output(
$token->setAttribute('secret', $tokenSecret),
Response::MODEL_TOKEN
)
);
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
@ -2102,8 +2111,7 @@ App::post('/v1/account/tokens/email')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($token, Response::MODEL_TOKEN)
;
->dynamic($token, Response::MODEL_TOKEN);
});
App::put('/v1/account/sessions/magic-url')
@ -2320,20 +2328,18 @@ App::post('/v1/account/tokens/phone')
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
$queueForEvents->setPayload(
$response->output(
$token->setAttribute('secret', $secret),
Response::MODEL_TOKEN
)
);
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $secret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '');
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($token, Response::MODEL_TOKEN)
;
->dynamic($token, Response::MODEL_TOKEN);
});
App::post('/v1/account/jwts')
@ -2517,6 +2523,7 @@ App::patch('/v1/account/password')
->label('sdk.response.model', Response::MODEL_USER)
->label('sdk.offline.model', '/account')
->label('sdk.offline.key', 'current')
->label('abuse-limit', 10)
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true)
->inject('requestTimestamp')
@ -2614,7 +2621,7 @@ App::patch('/v1/account/email')
// Makes sure this email is not already used in another identity
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Query::equal('providerEmail', [$email]),
Query::notEqual('userId', $user->getId()),
Query::notEqual('userInternalId', $user->getInternalId()),
]);
if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
@ -2978,18 +2985,19 @@ App::post('/v1/account/recovery')
->setSubject($subject)
->trigger();
// Set to unhashed secret for events and server responses
$recovery->setAttribute('secret', $secret);
$queueForEvents
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
->setUser($profile)
->setPayload($response->output(
$recovery->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
;
->setPayload($response->output($recovery, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$recovery->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $secret : '');
if (!$isPrivilegedUser && !$isAppUser) {
$recovery->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@ -3160,6 +3168,7 @@ App::post('/v1/account/verification')
->setParam('{{footer}}', $locale->getText("emails.verification.footer"))
->setParam('{{thanks}}', $locale->getText("emails.verification.thanks"))
->setParam('{{signature}}', $locale->getText("emails.verification.signature"));
$body = $message->render();
$smtp = $project->getAttribute('smtp', []);
@ -3226,16 +3235,18 @@ App::post('/v1/account/verification')
->setName($user->getAttribute('name') ?? '')
->trigger();
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $verificationSecret);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload($response->output(
$verification->setAttribute('secret', $verificationSecret),
Response::MODEL_TOKEN
));
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$verification->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $verificationSecret : '');
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@ -3285,7 +3296,7 @@ App::put('/v1/account/verification')
$user->setAttributes($profile->getArrayCopy());
$verificationDocument = $dbForProject->getDocument('tokens', $verifiedToken->getId());
$verification = $dbForProject->getDocument('tokens', $verifiedToken->getId());
/**
* We act like we're updating and validating
@ -3296,10 +3307,9 @@ App::put('/v1/account/verification')
$queueForEvents
->setParam('userId', $userId)
->setParam('tokenId', $verificationDocument->getId())
;
->setParam('tokenId', $verification->getId());
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
$response->dynamic($verification, Response::MODEL_TOKEN);
});
App::post('/v1/account/verification/phone')
@ -3397,17 +3407,18 @@ App::post('/v1/account/verification/phone')
->setRecipients([$user->getAttribute('phone')])
->setProviderType(MESSAGE_TYPE_SMS);
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $secret);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload($response->output(
$verification->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
;
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$verification->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $secret : '');
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@ -3493,14 +3504,33 @@ App::patch('/v1/account/mfa')
->inject('requestTimestamp')
->inject('response')
->inject('user')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (bool $mfa, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
->action(function (bool $mfa, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
$user->setAttribute('mfa', $mfa);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
if ($mfa) {
$factors = $session->getAttribute('factors', []);
$totp = TOTP::getAuthenticatorFromUser($user);
if ($totp !== null && $totp->getAttribute('verified', false)) {
$factors[] = Type::TOTP;
}
if ($user->getAttribute('email', false) && $user->getAttribute('emailVerification', false)) {
$factors[] = Type::EMAIL;
}
if ($user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false)) {
$factors[] = Type::PHONE;
}
$factors = \array_unique($factors);
$session->setAttribute('factors', $factors);
$dbForProject->updateDocument('sessions', $session->getId(), $session);
}
$queueForEvents->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_ACCOUNT);
@ -3631,10 +3661,10 @@ App::put('/v1/account/mfa/authenticators/:type')
->param('otp', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
->inject('project')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $type, string $otp, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
->action(function (string $type, string $otp, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
$authenticator = (match ($type) {
Type::TOTP => TOTP::getAuthenticatorFromUser($user),
@ -3663,10 +3693,12 @@ App::put('/v1/account/mfa/authenticators/:type')
$dbForProject->updateDocument('authenticators', $authenticator->getId(), $authenticator);
$dbForProject->purgeCachedDocument('users', $user->getId());
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$sessionId = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration);
$session = $dbForProject->getDocument('sessions', $sessionId);
$dbForProject->updateDocument('sessions', $sessionId, $session->setAttribute('factors', $type, Document::SET_TYPE_APPEND));
$factors = $session->getAttribute('factors', []);
$factors[] = $type;
$factors = \array_unique($factors);
$session->setAttribute('factors', $factors);
$dbForProject->updateDocument('sessions', $session->getId(), $session);
$queueForEvents->setParam('userId', $user->getId());
@ -4055,9 +4087,10 @@ App::put('/v1/account/mfa/challenge')
->inject('project')
->inject('response')
->inject('user')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $challengeId, string $otp, Document $project, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
->action(function (string $challengeId, string $otp, Document $project, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
$challenge = $dbForProject->getDocument('challenges', $challengeId);
@ -4103,15 +4136,15 @@ App::put('/v1/account/mfa/challenge')
$dbForProject->deleteDocument('challenges', $challengeId);
$dbForProject->purgeCachedDocument('users', $user->getId());
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$sessionId = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration);
$session = $dbForProject->getDocument('sessions', $sessionId);
$factors = $session->getAttribute('factors', []);
$factors[] = $type;
$factors = \array_unique($factors);
$session = $session
->setAttribute('factors', $type, Document::SET_TYPE_APPEND)
$session
->setAttribute('factors', $factors)
->setAttribute('mfaUpdatedAt', DateTime::now());
$dbForProject->updateDocument('sessions', $sessionId, $session);
$dbForProject->updateDocument('sessions', $session->getId(), $session);
$queueForEvents
->setParam('userId', $user->getId())

View file

@ -6,7 +6,6 @@ use Appwrite\Utopia\Response;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@ -14,8 +13,8 @@ use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
use Utopia\Fetch\Client;
use Utopia\Image\Image;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Utopia\System\System;
use Utopia\Validator\Boolean;
@ -155,40 +154,8 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
'id' => $githubId
];
} catch (Exception $error) {
if ($logger) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace('console');
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
$log->setMessage($error->getMessage());
$log->addTag('code', $error->getCode());
$log->addTag('verboseType', get_class($error));
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('detailedTrace', $error->getTrace());
$log->setAction('avatarsGetGitHub');
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('GitHub error log pushed with status code: ' . $responseCode);
}
Console::warning("Failed: {$error->getMessage()}");
Console::warning($error->getTraceAsString());
return [];
}
return [];
};
App::get('/v1/avatars/credit-cards/:code')
@ -284,14 +251,21 @@ App::get('/v1/avatars/image')
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
$fetch = @\file_get_contents($url);
$client = new Client();
try {
$res = $client
->setAllowRedirects(false)
->fetch($url);
} catch (\Throwable) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
if (!$fetch) {
if ($res->getStatusCode() !== 200) {
throw new Exception(Exception::AVATAR_IMAGE_NOT_FOUND);
}
try {
$image = new Image($fetch);
$image = new Image($res->getBody());
} catch (\Throwable $exception) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unable to parse image');
}
@ -340,31 +314,27 @@ App::get('/v1/avatars/favicon')
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
$curl = \curl_init();
$client = new Client();
try {
$res = $client
->setAllowRedirects(false)
->setUserAgent(\sprintf(
APP_USERAGENT,
System::getEnv('_APP_VERSION', 'UNKNOWN'),
System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY))
))
->fetch($url);
} catch (\Throwable) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
\curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
CURLOPT_USERAGENT => \sprintf(
APP_USERAGENT,
System::getEnv('_APP_VERSION', 'UNKNOWN'),
System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
]);
$html = \curl_exec($curl);
\curl_close($curl);
if (!$html) {
if ($res->getStatusCode() !== 200) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($html);
@$doc->loadHTML($res->getBody());
$links = $doc->getElementsByTagName('link');
$outputHref = '';
@ -419,9 +389,22 @@ App::get('/v1/avatars/favicon')
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
$data = @\file_get_contents($outputHref, false);
$client = new Client();
try {
$res = $client
->setAllowRedirects(false)
->fetch($outputHref);
} catch (\Throwable) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
if ($res->getStatusCode() !== 200) {
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND);
}
$data = $res->getBody();
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
}
@ -431,13 +414,7 @@ App::get('/v1/avatars/favicon')
->file($data);
}
$fetch = @\file_get_contents($outputHref, false);
if (!$fetch) {
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND);
}
$image = new Image($fetch);
$image = new Image($data);
$image->crop((int) $width, (int) $height);
$output = (empty($output)) ? $type : $output;
$data = $image->output($output, $quality);

View file

@ -24,7 +24,6 @@ use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Conflict as ConflictException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Helpers\ID;
@ -666,16 +665,10 @@ App::put('/v1/databases/:databaseId')
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
try {
$database = $dbForProject->updateDocument('databases', $databaseId, $database
->setAttribute('name', $name)
->setAttribute('enabled', $enabled)
->setAttribute('search', implode(' ', [$databaseId, $name])));
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (StructureException $exception) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage());
}
$database = $dbForProject->updateDocument('databases', $databaseId, $database
->setAttribute('name', $name)
->setAttribute('enabled', $enabled)
->setAttribute('search', implode(' ', [$databaseId, $name])));
$queueForEvents->setParam('databaseId', $database->getId());
@ -1036,19 +1029,14 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
$enabled ??= $collection->getAttribute('enabled', true);
try {
$collection = $dbForProject->updateDocument('database_' . $database->getInternalId(), $collectionId, $collection
->setAttribute('name', $name)
->setAttribute('$permissions', $permissions)
->setAttribute('documentSecurity', $documentSecurity)
->setAttribute('enabled', $enabled)
->setAttribute('search', implode(' ', [$collectionId, $name])));
$dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (StructureException $exception) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage());
}
$collection = $dbForProject->updateDocument('database_' . $database->getInternalId(), $collectionId, $collection
->setAttribute('name', $name)
->setAttribute('$permissions', $permissions)
->setAttribute('documentSecurity', $documentSecurity)
->setAttribute('enabled', $enabled)
->setAttribute('search', implode(' ', [$collectionId, $name])));
$dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity);
$queueForEvents
->setContext('database', $database)
@ -3599,16 +3587,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
}
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) {
try {
$dbForProject->deleteDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$documentId
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (RestrictedException) {
throw new Exception(Exception::DOCUMENT_DELETE_RESTRICTED);
}
$dbForProject->deleteDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$documentId
);
});
// Add $collectionId and $databaseId for all documents

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