1
0
Fork 0
mirror of synced 2024-05-16 18:52:33 +12:00

Merge remote-tracking branch 'origin/main' into feat-eldad2-coroutines

This commit is contained in:
Eldad Fux 2024-04-02 02:13:15 +02:00
commit 5ce3de4933
240 changed files with 3377 additions and 6516 deletions

2
.env
View file

@ -80,7 +80,7 @@ _APP_MAINTENANCE_RETENTION_CACHE=2592000
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_USAGE_AGGREGATION_INTERVAL=20
_APP_USAGE_AGGREGATION_INTERVAL=30
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_USAGE_STATS=enabled

2
.gitmodules vendored
View file

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

View file

@ -1,3 +1,354 @@
# Version 1.5.4
## What's Changed
### Fixes
* Fix function build command by @abnegate in https://github.com/appwrite/appwrite/pull/7813
# Version 1.5.3
## What's Changed
### Fixes
* Fix Attribute not found when migrating users collection in [#7782](https://github.com/appwrite/appwrite/pull/7782)
* Fix git deployments in [#7780](https://github.com/appwrite/appwrite/pull/7780)
* Allow wildcards for url validation like OAuth2 success in [#7791](https://github.com/appwrite/appwrite/pull/7791)
# Version 1.5.2
## What's Changed
* Fix stats migration by @abnegate in https://github.com/appwrite/appwrite/pull/7760
* Fix index migrations by @abnegate in https://github.com/appwrite/appwrite/pull/7769
* Fix Flutter/Dart SDKs by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7765
* Fix push notifications with no image by @abnegate in https://github.com/appwrite/appwrite/pull/7771
* Fix Python SDK by @abnegate in https://github.com/appwrite/appwrite/pull/7770
* Fix Android SDK deployment by @abnegate in https://github.com/appwrite/appwrite/pull/7770
**Full Changelog**: https://github.com/appwrite/appwrite/compare/1.5.1...1.5.2
# Version 1.5.1
## What's Changed
* fix: usage containers by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7757
**Full Changelog**: https://github.com/appwrite/appwrite/compare/1.5.0...1.5.
# Version 1.5.0
## What's Changed
### New features
- SSR support added. You can now handle sessions on your server app. [Learn more in docs](https://appwrite.io/docs/products/auth/server-side-rendering)
- 2FA support is now added for Appwrite Auth and for Console users. [Learn about adding 2FA to your app](https://appwrite.io/docs/products/auth/2fa) [Learn about 2FA on Console](https://appwrite.io/docs/advanced/security/2fa)
- Appwrite Messaging added. You can now send emails, SMS messages, and push notifications. [Learn more in docs](https://appwrite.io/docs/products/messaging)
- Appwrite now has enums for all config strings for OAuth, messaging adaptors, and more. [Learn more in the docs](https://appwrite.io/docs/sdks)
- New runtime versions for Dart, Bun, Ruby, Node, Deno, Python, PHP, Kotlin, Java, and Swift. [Learn more in docs](https://appwrite.io/docs/products/functions/runtimes)
- Create custom login flows with custom sessions and tokens. [Learn more in docs](https://appwrite.io/docs/products/auth/custom-token)
### Upgrading
- Appwrite Cloud is not yet updated to 1.5.x, expect an announcement in the upcoming weeks. If you lock your Appwrite SDK version, this update is not breaking.
- Follow the [self-hosted docs](https://appwrite.io/docs/advanced/self-hosting/update) to update your self-hosted Appwrite.
- Update your SDKs to the latest versions. The API is backwards compatible, using old SDKs will not break existing apps, but you will not have access to new features.
### Full changes
* Sync 1.5.x by @abnegate in https://github.com/appwrite/appwrite/pull/6030
* Sync master into 1.5.x by @fanatic75 in https://github.com/appwrite/appwrite/pull/6092
* add collections to config file and messaging scopes to config file by @fanatic75 in https://github.com/appwrite/appwrite/pull/5930
* Sync 1.4.x to 1.5.x by @fanatic75 in https://github.com/appwrite/appwrite/pull/6233
* Feat add messaging response models by @fanatic75 in https://github.com/appwrite/appwrite/pull/5951
* Feat messages event config by @fanatic75 in https://github.com/appwrite/appwrite/pull/5986
* Sync main 1.5.x by @fanatic75 in https://github.com/appwrite/appwrite/pull/6514
* Sync 1.5.x with 1.4.x by @fanatic75 in https://github.com/appwrite/appwrite/pull/6875
* Feat provider controllers by @fanatic75 in https://github.com/appwrite/appwrite/pull/6023
* Feat topics controller by @fanatic75 in https://github.com/appwrite/appwrite/pull/6032
* Feat sms push controllers by @fanatic75 in https://github.com/appwrite/appwrite/pull/6950
* Sync 1.4.x to 1.5.x by @fanatic75 in https://github.com/appwrite/appwrite/pull/6965
* Providers from attribute by @fanatic75 in https://github.com/appwrite/appwrite/pull/6991
* made review changes by @fanatic75 in https://github.com/appwrite/appwrite/pull/7010
* removes provider from topics by @fanatic75 in https://github.com/appwrite/appwrite/pull/7014
* review changes by @fanatic75 in https://github.com/appwrite/appwrite/pull/7048
* Event label messaging by @fanatic75 in https://github.com/appwrite/appwrite/pull/7058
* Feat logs messaging api by @fanatic75 in https://github.com/appwrite/appwrite/pull/7069
* Chore sync main 1.5.x by @fanatic75 in https://github.com/appwrite/appwrite/pull/7115
* Chore sync main by @abnegate in https://github.com/appwrite/appwrite/pull/7120
* Add XDebug by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7082
* More review changes by @fanatic75 in https://github.com/appwrite/appwrite/pull/7129
* Feat target provider type by @fanatic75 in https://github.com/appwrite/appwrite/pull/7132
* removes internal provider by @fanatic75 in https://github.com/appwrite/appwrite/pull/7151
* Feat account target by @fanatic75 in https://github.com/appwrite/appwrite/pull/7152
* 1.4.x by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7149
* adds user name in subscriber response model by @fanatic75 in https://github.com/appwrite/appwrite/pull/7177
* feat: add migration stats task by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7187
* migrates enum attribute size to 255 by @fanatic75 in https://github.com/appwrite/appwrite/pull/7183
* changes TextMagic to Textmagic in all places and uses email validator by @fanatic75 in https://github.com/appwrite/appwrite/pull/7188
* chore: upgrade console to 3.2.9 by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7189
* makes provider creation fields optional and adds target info in subsc… by @fanatic75 in https://github.com/appwrite/appwrite/pull/7195
* Chore update sdks by @abnegate in https://github.com/appwrite/appwrite/pull/7180
* adds target when creating user via server endpoint by @fanatic75 in https://github.com/appwrite/appwrite/pull/7217
* Feat add message provider type by @abnegate in https://github.com/appwrite/appwrite/pull/7219
* feat: update console by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7225
* Implement Job based hamster by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7216
* bump: console version by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7238
* chore: update console version by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7240
* misc changes by @fanatic75 in https://github.com/appwrite/appwrite/pull/7227
* Update appwrite base image by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7241
* Revert "Update appwrite base image" by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7256
* feat: upgrade console to 3.2.15 by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7258
* mail support string as attachment by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7261
* fix redis issue by encoding content by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7262
* Fix cookie issue by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7260
* support mail template override by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7251
* feat: project usage custom date range by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7266
* feat: usage breakdown by project by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7270
* provide retention time as queue server resource by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7272
* fix: remove expired cookie by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7275
* PEA-15 Refactor Deletes and maintenance worker by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7273
* Refactor usage execution trigger by @shimonewman in https://github.com/appwrite/appwrite/pull/7274
* Fix max array size 1 by @abnegate in https://github.com/appwrite/appwrite/pull/7287
* chore: update console by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7291
* Fix SMS import by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7293
* rename stats collection by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7301
* fix deletes worker by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7310
* Sync main with 1.4.x by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7312
* combining network inbound by @shimonewman in https://github.com/appwrite/appwrite/pull/7298
* chore: update console by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7313
* Update console by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7314
* chore: update console by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7315
* chore: update console by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7316
* chore: update console by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7319
* chore: update hamster script by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7320
* Refactor usage worker sn by @shimonewman in https://github.com/appwrite/appwrite/pull/7326
* usageHook tweaks by @shimonewman in https://github.com/appwrite/appwrite/pull/7330
* Refactor inf metric calc by @shimonewman in https://github.com/appwrite/appwrite/pull/7339
* Fix user last activity not updating by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7292
* Fix user identity attaching to wrong user by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7280
* fix for file extension not supported by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7349
* Bump console to version 3.3.14 by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7358
* Bump console to version 3.3.15 by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7359
* Update tr.json by @fanksin in https://github.com/appwrite/appwrite/pull/7276
* Improve large file handling by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7351
* PEA-38 compression constants by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7366
* Fix permission issue with chunk upload by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7328
* 1.4.x by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7317
* Sync with main by @shimonewman in https://github.com/appwrite/appwrite/pull/7374
* Feat: Max password length by @Meldiron in https://github.com/appwrite/appwrite/pull/7376
* feat: console hostname env variable by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7360
* Sync main by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7384
* Add `_APP_CONSOLE_HOSTNAMES` env var to allow more hostnames to the console project by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7377
* Fix app console hostnames check by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7385
* Add General E2E tests to CI pipeline by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7386
* Fix app console hostnames check on refactor usage sn by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7387
* executor: pass build timeout to runtimes by @iMacHumphries in https://github.com/appwrite/appwrite/pull/7350
* Create an enum for Message status by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7380
* [Feat]: Zoho OAuth Provider by @UtkarshAhuja2003 in https://github.com/appwrite/appwrite/pull/7365
* Messaging uniform logic fixes by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7397
* Webhook attempts PR suggestions by @Meldiron in https://github.com/appwrite/appwrite/pull/7402
* fix: escape html in email params by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7409
* Add a flag to install and upgrade commands to not start Appwrite by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7271
* Add support for querying topic total by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7388
* Adds uniform error logic for messaging worker and extra params for email by @fanatic75 in https://github.com/appwrite/appwrite/pull/7245
* Update the delete identity endpoints to set the params and payload by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7348
* Fix utopia-php/framework version by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7410
* feat: account delete by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7415
* chore: update collection name in hamster by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7417
* Feat: Magic URL improvements by @Meldiron in https://github.com/appwrite/appwrite/pull/7416
* feat: update assistant by @loks0n in https://github.com/appwrite/appwrite/pull/7421
* Feat: Improve worker logging by @Meldiron in https://github.com/appwrite/appwrite/pull/7192
* Added security phrase to magic URL by @Meldiron in https://github.com/appwrite/appwrite/pull/7424
* fix: hotfix for redirect param in custom templates by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7437
* Make OTP template more contextual by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7434
* Fix: Remove passwordAgain by @Meldiron in https://github.com/appwrite/appwrite/pull/7441
* Rename usage_network_infinity to usage_bandwidth_infinity by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7443
* Added cloud base templates to appwrite/appwrite by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7440
* Remove the endpoint param for APNS providers by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7418
* Default topic description to an empty string instead of null by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7430
* feat: SSR by @loks0n in https://github.com/appwrite/appwrite/pull/5777
* Add support for draft messages by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7429
* Add search param for list subscribers endpoint by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7382
* Delete subscribers and update topic totals when deleting target by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7396
* Fix list messages allowed queries by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7381
* Allow filtering targets by provider type by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7419
* Feat message scheduling by @abnegate in https://github.com/appwrite/appwrite/pull/7431
* Fix create/update push target routes by @abnegate in https://github.com/appwrite/appwrite/pull/7461
* Fix cc + bcc targets fetched by identifier instead of $id by @abnegate in https://github.com/appwrite/appwrite/pull/7468
* Add endpoint to list a message's targets by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7463
* Limit webhook failure attempts to 10 by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7128
* Follow existing style of code by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7470
* Refactored url construction by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7478
* hamster additions by @shimonewman in https://github.com/appwrite/appwrite/pull/7462
* Feat: session renewal by @Meldiron in https://github.com/appwrite/appwrite/pull/7452
* Feat: Email OTP by @Meldiron in https://github.com/appwrite/appwrite/pull/7422
* feat: delete account by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7392
* Fix: Email image endpoints by @Meldiron in https://github.com/appwrite/appwrite/pull/7474
* Feat smtp test endpoint by @eldadfux in https://github.com/appwrite/appwrite/pull/7307
* Update ruby by @abnegate in https://github.com/appwrite/appwrite/pull/7464
* Refactor usage by @shimonewman in https://github.com/appwrite/appwrite/pull/7005
* Fix: Certificate email style by @Meldiron in https://github.com/appwrite/appwrite/pull/7475
* sync: 1.5.x with main by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7486
* 1.5.x <- main by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7485
* Refactor Usage Stats by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7311
* Feat maintenance delete expired targets by @abnegate in https://github.com/appwrite/appwrite/pull/7460
* Trigger deletes worker when target is deleted by @abnegate in https://github.com/appwrite/appwrite/pull/7490
* Throw on enable if provider credentials are missing instead of ignoring by @abnegate in https://github.com/appwrite/appwrite/pull/7484
* Add Queue Retry Command to Appwrite by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7391
* Add failed queues endpoint to health by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7466
* Feat: CNAME validation logs by @Meldiron in https://github.com/appwrite/appwrite/pull/7482
* Add queue management commands by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7497
* Add threshold to queue failed health endpoint by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7499
* fix: use atomic operations for count updates by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7511
* chore: add logs by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7513
* chore: add logs by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7514
* chore: add auth label to phone endpoint by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7515
* JSON + OR query support by @fogelito in https://github.com/appwrite/appwrite/pull/7252
* Labels limit by @fogelito in https://github.com/appwrite/appwrite/pull/7504
* chore: update rate limits by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7523
* Add message delete route by @abnegate in https://github.com/appwrite/appwrite/pull/7510
* Feat remove description by @abnegate in https://github.com/appwrite/appwrite/pull/7518
* Upgrade to PHP 8.2 by @eldadfux in https://github.com/appwrite/appwrite/pull/7067
* Add SMTP provider by @abnegate in https://github.com/appwrite/appwrite/pull/7525
* remove debug leftovers by @shimonewman in https://github.com/appwrite/appwrite/pull/7531
* feat: ssr changes by @loks0n in https://github.com/appwrite/appwrite/pull/7532
* Add webhook max failed attempts as env variable by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7489
* Feat: Rename security phrases by @Meldiron in https://github.com/appwrite/appwrite/pull/7533
* Update to standard namespacing for enums by @abnegate in https://github.com/appwrite/appwrite/pull/7537
* Remove redundant usage labels by @abnegate in https://github.com/appwrite/appwrite/pull/7536
* Update containers by @abnegate in https://github.com/appwrite/appwrite/pull/7526
* feat: mfa by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7346
* Fix: client error reporting by @Meldiron in https://github.com/appwrite/appwrite/pull/7539
* Feat session target delete by @abnegate in https://github.com/appwrite/appwrite/pull/7540
* Fix spec generation by @abnegate in https://github.com/appwrite/appwrite/pull/7538
* Feat block countries from certain regions by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7487
* Fix: Empty values in PATCH of users by @Meldiron in https://github.com/appwrite/appwrite/pull/7471
* Add a 20 second delay to maintenance worker by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7544
* Add health certificate validity endpoint by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7547
* Add count for messages(sms) metric by @shimonewman in https://github.com/appwrite/appwrite/pull/7520
* Fix user API mfa route auth by @abnegate in https://github.com/appwrite/appwrite/pull/7549
* Fix graphql tests by @abnegate in https://github.com/appwrite/appwrite/pull/7553
* Fix message status values and enums by @abnegate in https://github.com/appwrite/appwrite/pull/7552
* Update param name by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7519
* Fix SHA function enum name by @abnegate in https://github.com/appwrite/appwrite/pull/7554
* Sync main with refactor-usage-sn by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7556
* Usage queue poc by @shimonewman in https://github.com/appwrite/appwrite/pull/7503
* Update team counts by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7561
* PEA-233-prevent console user deletion before deleting their team by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7500
* PEA-233-prevent console user deletion before deleting their team by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7563
* Update place holders from [PARAM_NAME] to <PARAM_NAME> by @gewenyu99 in https://github.com/appwrite/appwrite/pull/7560
* PEA-334 - Fix spec use cloud endpoint by default by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7567
* Merge main by @abnegate in https://github.com/appwrite/appwrite/pull/7568
* Hardcode size and orders for Json Key Indexes by @fogelito in https://github.com/appwrite/appwrite/pull/7557
* Feat support label queries by @abnegate in https://github.com/appwrite/appwrite/pull/7570
* Fix missing user activity logs by @Souptik2001 in https://github.com/appwrite/appwrite/pull/7559
* Refactor usage sn by @shimonewman in https://github.com/appwrite/appwrite/pull/7576
* Catch errors parseQueries by @fogelito in https://github.com/appwrite/appwrite/pull/7558
* fix Indexes by @fogelito in https://github.com/appwrite/appwrite/pull/7572
* Fix updating message status by @abnegate in https://github.com/appwrite/appwrite/pull/7571
* Fix telesign params by @abnegate in https://github.com/appwrite/appwrite/pull/7569
* Fix email/sms template type enums getting the same values by @abnegate in https://github.com/appwrite/appwrite/pull/7551
* Replace catching \Exception with \Throwable by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7555
* cache collection attr migration by @shimonewman in https://github.com/appwrite/appwrite/pull/7575
* Remove database methods from deletes worker by @shimonewman in https://github.com/appwrite/appwrite/pull/7135
* Fix smtp provider update by @abnegate in https://github.com/appwrite/appwrite/pull/7578
* usage logs updates by @shimonewman in https://github.com/appwrite/appwrite/pull/7588
* Add unknown error is delivered total is 0 but there were no delivery … by @abnegate in https://github.com/appwrite/appwrite/pull/7579
* Feat subscribe permission by @abnegate in https://github.com/appwrite/appwrite/pull/7580
* Remove redundant hook by @abnegate in https://github.com/appwrite/appwrite/pull/7581
* Update geodb by @abnegate in https://github.com/appwrite/appwrite/pull/7582
* Project id to logger by @shimonewman in https://github.com/appwrite/appwrite/pull/7598
* Refactor cache by @shimonewman in https://github.com/appwrite/appwrite/pull/5616
* Feat topic totals per type by @abnegate in https://github.com/appwrite/appwrite/pull/7589
* Update docker base by @abnegate in https://github.com/appwrite/appwrite/pull/7599
* Update Response and Request filters aswell as Migrations for 1.5.x by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7457
* fix: blocked users web controllers by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7601
* dev: introduce redis insights by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7583
* Update image lib by @abnegate in https://github.com/appwrite/appwrite/pull/7566
* Update exception for github session not found by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7375
* Feat more phone validation by @loks0n in https://github.com/appwrite/appwrite/pull/7165
* fix project network usage by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7608
* usage logs updates by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7615
* usage/usage-dump queue health endpoints by @shimonewman in https://github.com/appwrite/appwrite/pull/7614
* Make self-hosted and cloud specs consistent by @abnegate in https://github.com/appwrite/appwrite/pull/7617
* Remove callback resources from workers by @abnegate in https://github.com/appwrite/appwrite/pull/7618
* Feat email attachments by @abnegate in https://github.com/appwrite/appwrite/pull/7611
* Update GETTING_STARTED.md by @GuptaPratik02 in https://github.com/appwrite/appwrite/pull/6826
* Refactor remove resource collection by @abnegate in https://github.com/appwrite/appwrite/pull/7620
* Fix duplicate subscribers by @abnegate in https://github.com/appwrite/appwrite/pull/7624
* Allow push images by @abnegate in https://github.com/appwrite/appwrite/pull/7594
* fix: msg91 by @loks0n in https://github.com/appwrite/appwrite/pull/7626
* Fix: Router CURL by @Meldiron in https://github.com/appwrite/appwrite/pull/7627
* Add storage health check by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7591
* Revert "usage/usage-dump queue health endpoints" by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7629
* Revert "Fix: Router CURL" by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7630
* Fix: Router CURL by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7632
* Use swoole process mode instead of base by @abnegate in https://github.com/appwrite/appwrite/pull/7635
* feat: ssr dx by @loks0n in https://github.com/appwrite/appwrite/pull/7619
* Remove preloading by @abnegate in https://github.com/appwrite/appwrite/pull/7637
* fix: add mfa path to console by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7639
* Fix 1.5.x Migrations by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7621
* Provider null by @fogelito in https://github.com/appwrite/appwrite/pull/7625
* Add ARM64 to docker publish by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7622
* Fix tests by @abnegate in https://github.com/appwrite/appwrite/pull/7640
* fix: email templates by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7641
* Fix content-type of file by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7643
* Fix update topics permissions by @abnegate in https://github.com/appwrite/appwrite/pull/7638
* Allow setting APNS to sandbox mode by @abnegate in https://github.com/appwrite/appwrite/pull/7645
* Fix: Functions CI/CD tests by @Meldiron in https://github.com/appwrite/appwrite/pull/7647
* Fix missing userId on update challenge by @abnegate in https://github.com/appwrite/appwrite/pull/7650
* Fix mail reset attachment by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7653
* Fix return by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7654
* Fix-function-id-2 by @eldadfux in https://github.com/appwrite/appwrite/pull/7656
* Only return allowed runtimes in runtime list route by @abnegate in https://github.com/appwrite/appwrite/pull/7659
* Add twoWayKey checks and multiple many-to-many restrictions by @fogelito in https://github.com/appwrite/appwrite/pull/7153
* chore: remove array sdk method by @loks0n in https://github.com/appwrite/appwrite/pull/7649
* Use new migration error handling by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7652
* fix: phone verification flaky by @loks0n in https://github.com/appwrite/appwrite/pull/7666
* feat: test perf by @loks0n in https://github.com/appwrite/appwrite/pull/7665
* Fix: False positive MFA by @Meldiron in https://github.com/appwrite/appwrite/pull/7664
* Fix: Spanish template translations by @DH-555 in https://github.com/appwrite/appwrite/pull/7658
* Bug 7597 - Adding default value by @navjotNSK in https://github.com/appwrite/appwrite/pull/7651
* 1.5.x api descriptions by @gewenyu99 in https://github.com/appwrite/appwrite/pull/7667
* Fix: Empty password validation by @Meldiron in https://github.com/appwrite/appwrite/pull/7662
* [Fix]: Remove internal attributes on select query by @UtkarshAhuja2003 in https://github.com/appwrite/appwrite/pull/7648
* Feat remove status param by @abnegate in https://github.com/appwrite/appwrite/pull/7670
* Sync main by @abnegate in https://github.com/appwrite/appwrite/pull/7669
* Rescheduling fixes by @abnegate in https://github.com/appwrite/appwrite/pull/7668
* Disallow creating a session if one already exists by @abnegate in https://github.com/appwrite/appwrite/pull/7616
* Fix duplication by @abnegate in https://github.com/appwrite/appwrite/pull/7679
* Feat fix 1.5.x migrations by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/7671
* Allow ssl along with tls in custom smtp by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7674
* Change status code from 503 to 403 by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7672
* Train Assistant for Appwrite 1.5 by @loks0n in https://github.com/appwrite/appwrite/pull/7686
* adding limit to queue retry by @shimonewman in https://github.com/appwrite/appwrite/pull/7692
* fix: encode secret in oauth workaround by @loks0n in https://github.com/appwrite/appwrite/pull/7695
* Update generator and deploy RC SDKs by @abnegate in https://github.com/appwrite/appwrite/pull/7545
* Update endpoint description by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7704
* Chore sync 1.4.x into main by @stnguyen90 in https://github.com/appwrite/appwrite/pull/7697
* chore: update error types for create account endpoints by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7698
* usage/usage-dump queue health endpoints by @shimonewman in https://github.com/appwrite/appwrite/pull/7661
* Refactor usage sn by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7660
* Fix: MFA flows and docs by @Meldiron in https://github.com/appwrite/appwrite/pull/7709
* update cover image for SDKs by @lohanidamodar in https://github.com/appwrite/appwrite/pull/7715
* Feat: More Recovery code endpoints by @Meldiron in https://github.com/appwrite/appwrite/pull/7713
* feat: mfa collection restructure by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7696
* Fix: SDKs enums by @Meldiron in https://github.com/appwrite/appwrite/pull/7723
* sync: main -> 1.5.x by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7705
* Sync main 1.5.x by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7727
* Updated header by @DylanG-64 in https://github.com/appwrite/appwrite/pull/7728
* chore: update exector, runtimes by @loks0n in https://github.com/appwrite/appwrite/pull/7729
* feat: pint by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7738
* feat: cascading response/request filters by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7745
* Revert 7629 revert 7614 chore usage queue health by @christyjacob4 in https://github.com/appwrite/appwrite/pull/7707
* fix: migration 1.5.x by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7737
* sync: main 1.5.x 3 by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/7747
* Allow users to disable APIs by @vermakhushboo in https://github.com/appwrite/appwrite/pull/7725
* Feat seperate image IDs by @abnegate in https://github.com/appwrite/appwrite/pull/7749
* fix: account endpoint order by @loks0n in https://github.com/appwrite/appwrite/pull/7739
* Feat image jwts by @abnegate in https://github.com/appwrite/appwrite/pull/7751
## New Contributors
* @fanksin made their first contribution in https://github.com/appwrite/appwrite/pull/7276
* @iMacHumphries made their first contribution in https://github.com/appwrite/appwrite/pull/7350
* @UtkarshAhuja2003 made their first contribution in https://github.com/appwrite/appwrite/pull/7365
* @Souptik2001 made their first contribution in https://github.com/appwrite/appwrite/pull/7559
* @GuptaPratik02 made their first contribution in https://github.com/appwrite/appwrite/pull/6826
* @navjotNSK made their first contribution in https://github.com/appwrite/appwrite/pull/7651
* @DylanG-64 made their first contribution in https://github.com/appwrite/appwrite/pull/7728
**Full Changelog**: https://github.com/appwrite/appwrite/compare/1.4.13...1.5.0
# Version 1.4.14
## Changes

View file

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

View file

@ -1,4 +1,4 @@
> Great news! Appwrite Cloud is now in public beta! Sign up at [cloud.appwrite.io](https://cloud.appwrite.io) for a hassle-free, hosted experience. Join us in the Cloud today! ☁️🎉
> Our Appwrite Init event has concluded. You can check out all the new and upcoming features [on our Init website](https://appwrite.io/init) 🚀
<br />
<p align="center">
@ -76,7 +76,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.4.13
appwrite/appwrite:1.5.4
```
### Windows
@ -88,7 +88,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.4.13
appwrite/appwrite:1.5.4
```
#### PowerShell
@ -98,7 +98,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.4.13
appwrite/appwrite:1.5.4
```
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

@ -10,6 +10,7 @@
| 1.2.x | :white_check_mark: |
| 1.3.x | :white_check_mark: |
| 1.4.x | :white_check_mark: |
| 1.5.x | :white_check_mark: |
## Reporting a Vulnerability

View file

@ -23,6 +23,7 @@ use Utopia\Platform\Service;
use Utopia\Pools\Group;
use Utopia\Queue\Connection;
use Utopia\Registry\Registry;
use Utopia\System\System;
global $register;
@ -159,7 +160,7 @@ CLI::setResource('logError', function (Registry $register) {
$logger = $register->get('logger');
if ($logger) {
$version = Http::getEnv('_APP_VERSION', 'UNKNOWN');
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace($namespace);
@ -178,7 +179,8 @@ CLI::setResource('logError', function (Registry $register) {
$log->setAction($action);
$isProduction = Http::getEnv('_APP_ENV', 'development') === 'production';
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);

16
app/config/apis.php Normal file
View file

@ -0,0 +1,16 @@
<?php
return [
'rest' => [
'key' => 'rest',
'name' => 'REST',
],
'graphql' => [
'key' => 'graphql',
'name' => 'GraphQL',
],
'realtime' => [
'key' => 'realtime',
'name' => 'Realtime',
],
];

View file

@ -34,6 +34,28 @@ $commonCollections = [
'array' => false,
'filters' => [],
],
[
'$id' => 'resourceType',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 255,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('mimeType'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'accessedAt',
'type' => Database::VAR_DATETIME,
@ -4300,6 +4322,17 @@ $consoleCollections = array_merge([
'array' => false,
'filters' => ['json'],
],
[
'$id' => ID::custom('apis'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'array' => false,
'filters' => ['json'],
],
[
'$id' => ID::custom('smtp'),
'type' => Database::VAR_STRING,

View file

@ -29,6 +29,11 @@ return [
'description' => 'The request originated from an unknown origin. If you trust this domain, please list it as a trusted platform in the Appwrite console.',
'code' => 403,
],
Exception::GENERAL_API_DISABLED => [
'name' => Exception::GENERAL_API_DISABLED,
'description' => 'The requested API is disabled. You can enable the API from the Appwrite console.',
'code' => 403,
],
Exception::GENERAL_SERVICE_DISABLED => [
'name' => Exception::GENERAL_SERVICE_DISABLED,
'description' => 'The requested service is disabled. You can enable the service from the Appwrite console.',

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Baie dankie",
"emails.verification.signature": "Die {{project}} span",
"emails.magicSession.subject": "Teken aan",
"emails.magicSession.hello": "Goeie dag,",
"emails.magicSession.hello": "Goeie dag",
"emails.magicSession.body": "Volg hierdie skakel om in te teken.",
"emails.magicSession.footer": "Ignoreer gerus hierdie boodskap as u nie die versoek gestuur het om met die' adres in te teken nie.",
"emails.magicSession.thanks": "Baie dankie",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "شكرا",
"emails.verification.signature": "فريق {{project}}",
"emails.magicSession.subject": "تسجيل الدخول",
"emails.magicSession.hello": "أهلا،",
"emails.magicSession.hello": "أهلا",
"emails.magicSession.body": "اتبع هذا الرابط لتسجيل الدخول",
"emails.magicSession.footer": "لو لم تطلب تسجيل الدخول بهذا البريد الاكتروني ، يمكنك تجاهل هذه الرسالة",
"emails.magicSession.thanks": "شكرا",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "ধন্যবাদ",
"emails.verification.signature": "{{project}} দল",
"emails.magicSession.subject": "লগইন",
"emails.magicSession.hello": "নমস্কাৰ,",
"emails.magicSession.hello": "নমস্কাৰ",
"emails.magicSession.body": "লগইন কৰিবলৈ এই লিংকটো অনুসৰণ কৰক।",
"emails.magicSession.footer": "যদি আপুনি এই ইমেইল ব্যৱহাৰ কৰি লগইন কৰিবলৈ কোৱা নাছিল, আপুনি এই বাৰ্তাটো উপেক্ষা কৰিব পাৰে।",
"emails.magicSession.thanks": "ধন্যবাদ",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Təşəkkürlər",
"emails.verification.signature": "{{project}} komandası",
"emails.magicSession.subject": "Daxil Olmaq",
"emails.magicSession.hello": "Salam,",
"emails.magicSession.hello": "Salam",
"emails.magicSession.body": "Daxil olmaq üçün bu linki izləyin.",
"emails.magicSession.footer": "Bu e-poçtdan istifadə edərək giriş istəməmisinizsə, bu mesajı görməməzlikdən gələ bilərsiniz.",
"emails.magicSession.thanks": "Təşəkkürlər",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Дзякуем",
"emails.verification.signature": "каманда {{project}}",
"emails.magicSession.subject": "Лагін",
"emails.magicSession.hello": "Прывітанне,",
"emails.magicSession.hello": "Прывітанне",
"emails.magicSession.body": "Перайдзіце па спасылцы, каб увайсці.",
"emails.magicSession.footer": "Калі вы не прасілі ўвайсці, выкарыстоўваючы гэты адрас электроннай пошты, праігнаруйце гэтае паведамленне.",
"emails.magicSession.thanks": "Дзякуем",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Gràcies",
"emails.verification.signature": "Equip {{project}}",
"emails.magicSession.subject": "Entrar",
"emails.magicSession.hello": "Hola,",
"emails.magicSession.hello": "Hola",
"emails.magicSession.body": "Accedeix a aquest enllaç per a entrar.",
"emails.magicSession.footer": "Si no has sol·licitat entrar amb aquesta adreça electrònica, pots ignorar aquest missatge.",
"emails.magicSession.thanks": "Gràcies",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Tak",
"emails.verification.signature": "{{project}} team",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Hej,",
"emails.magicSession.hello": "Hej",
"emails.magicSession.body": "Følg dette link for at logge ind.",
"emails.magicSession.footer": "Hvis du ikke har bedt om at logge ind med denne email, ignorer venligst denne besked.",
"emails.magicSession.thanks": "Tak",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Danke",
"emails.verification.signature": "{{project}}-Team",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Hey,",
"emails.magicSession.hello": "Hey",
"emails.magicSession.body": "Folge diesem Link, um dich einzuloggen.",
"emails.magicSession.footer": "Solltest du keinen Login für diese E-Mail-Adresse angefordert haben, kannst du diese Nachricht ignorieren.",
"emails.magicSession.thanks": "Danke",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Ευχαριστούμε",
"emails.verification.signature": "Η ομάδα του {{project}}",
"emails.magicSession.subject": "Είσοδος",
"emails.magicSession.hello": "Γεια σου,",
"emails.magicSession.hello": "Γεια σου",
"emails.magicSession.body": "Ακολουθήστε αυτό το link για να συνδεθείτε",
"emails.magicSession.footer": "Εάν δεν ζητήσατε να συνδεθείτε χρησιμοποιώντας αυτό το email, μπορείτε να αγνοήσετε αυτό το μήνυμα.",
"emails.magicSession.thanks": "Ευχαριστούμε",

View file

@ -9,7 +9,7 @@
"emails.verification.thanks": "Dankegon.",
"emails.verification.signature": "Teamo {{project}}",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Saluton,",
"emails.magicSession.hello": "Saluton",
"emails.magicSession.body": "Alklaku ĉi tiun ligon por eniri.",
"emails.magicSession.footer": "Se vi ne petis ĉi tiun konfirmon de ĉi tiu retpoŝto, vi povas ignori ĉi tiun mesaĝon.",
"emails.magicSession.thanks": "Dankegon",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Merci",
"emails.verification.signature": "Équipe {{project}}",
"emails.magicSession.subject": "Connexion",
"emails.magicSession.hello": "Bonjour,",
"emails.magicSession.hello": "Bonjour",
"emails.magicSession.body": "Suivez ce lien pour vous connecter.",
"emails.magicSession.footer": "Si vous n'avez pas demandé à vous connecter en utilisant cet e-mail, vous pouvez ignorer ce message.",
"emails.magicSession.thanks": "Merci",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Go raibh maith agat",
"emails.verification.signature": "{{project}} foireann",
"emails.magicSession.subject": "Logáil isteach",
"emails.magicSession.hello": "Haigh,",
"emails.magicSession.hello": "Haigh",
"emails.magicSession.body": "Lean an nasc seo chun logáil isteach.",
"emails.magicSession.footer": "Mura ndearna tú iarratas logáil isteach leis an ríomhphost seo, déan neamhaird den teachtaireacht seo.",
"emails.magicSession.thanks": "Go raibh maith agat",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "આભાર",
"emails.verification.signature": "{{project}} ટીમ",
"emails.magicSession.subject": "પ્રવેશ કરો",
"emails.magicSession.hello": "નમસ્કાર,",
"emails.magicSession.hello": "નમસ્કાર",
"emails.magicSession.body": "પ્રવેશ કરવા માટે આ લિંકને અનુસરો.",
"emails.magicSession.footer": "જો તમે આ ઇમેઇલનો ઉપયોગ કરીને પ્રવેશ કરવાનું ન કહ્યું હોય, તો તમે આ સંદેશને અવગણી શકો છો.",
"emails.magicSession.thanks": "આભાર",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "תודה",
"emails.verification.signature": "צוות {{project}}",
"emails.magicSession.subject": "כניסה למערכת",
"emails.magicSession.hello": "שלום,",
"emails.magicSession.hello": "שלום",
"emails.magicSession.body": "לחץ על קישור זה כדי להיכנס.",
"emails.magicSession.footer": "אם לא ביקשת להיכנס באמצעות דוא\"ל זה, תוכל להתעלם מהודעה זו.",
"emails.magicSession.thanks": "תודה",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "धन्यवाद",
"emails.verification.signature": "{{project}} टीम",
"emails.magicSession.subject": "लॉग इन",
"emails.magicSession.hello": "नमस्ते,",
"emails.magicSession.hello": "नमस्ते",
"emails.magicSession.body": "इस लिंक के माध्यम से लॉग-इन करें।",
"emails.magicSession.footer": "यदि आप इस ईमेल द्वारा लॉगिन नहीं करना चाहते हैं, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
"emails.magicSession.thanks": "धन्यवाद",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Hvala",
"emails.verification.signature": "{{project}} tim",
"emails.magicSession.subject": "Prijavite se",
"emails.magicSession.hello": "Pozdrav,",
"emails.magicSession.hello": "Pozdrav",
"emails.magicSession.body": "Slijedite ovu poveznicu za prijavu.",
"emails.magicSession.footer": "Ako niste zatražili prijavu putem ove e-pošte, možete zanemariti ovu poruku.",
"emails.magicSession.thanks": "Hvala",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Köszönettel",
"emails.verification.signature": "a {{project}} csapat",
"emails.magicSession.subject": "Bejelentkezés",
"emails.magicSession.hello": "Szia,",
"emails.magicSession.hello": "Szia",
"emails.magicSession.body": "Kattints a linkre a bejelentkezéshez.",
"emails.magicSession.footer": "Ha nem te szerettél volna bejelentkezni ezzel az email címmel, akkor nyugodtan hagyd figyelmen kívül ezt az üzenetet.",
"emails.magicSession.thanks": "Köszönettel",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Terima kasih",
"emails.verification.signature": "Tim {{project}}",
"emails.magicSession.subject": "Masuk",
"emails.magicSession.hello": "Hai,",
"emails.magicSession.hello": "Hai",
"emails.magicSession.body": "Ikuti tautan ini untuk masuk.",
"emails.magicSession.footer": "Jika Anda tidak meminta untuk masuk menggunakan email ini, Anda dapat mengabaikan pesan ini.",
"emails.magicSession.thanks": "Terima kasih",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Grazie",
"emails.verification.signature": "Il team {{project}}",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Ciao,",
"emails.magicSession.hello": "Ciao",
"emails.magicSession.body": "Clicca questo link per accedere.",
"emails.magicSession.footer": "Se non hai richiesto di effettuare laccesso, puoi ignorare questo messaggio.",
"emails.magicSession.thanks": "Grazie",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Matur nuwun",
"emails.verification.signature": "Tim {{project}}",
"emails.magicSession.subject": "Masuk",
"emails.magicSession.hello": "Hai,",
"emails.magicSession.hello": "Hai",
"emails.magicSession.body": "Klik link iki kanggo masuk.",
"emails.magicSession.footer": "Yen sampeyan ora njaluk masuk nggunakake alamat email iki, sampeyan iso nglirwakake pesen iki.",
"emails.magicSession.thanks": "Matur nuwun",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "ಧನ್ಯವಾದಗಳು",
"emails.verification.signature": "{{project}} ತಂಡ",
"emails.magicSession.subject": "ಲಾಗಿನ್",
"emails.magicSession.hello": "ನಮಸ್ಕಾರ,",
"emails.magicSession.hello": "ನಮಸ್ಕಾರ",
"emails.magicSession.body": "ಲಾಗಿನ್ ಮಾಡಲಿಕ್ಕೆ ಈ ಲಿಂಕನ್ನು ಅನುಸರಿಸಿ",
"emails.magicSession.footer": "ನೀವು ಈ ಇಮೇಲನಿಂದ ಲಾಗಿನ್ ಮಾಡಲು ಕೇಳದಿದ್ದರೆ, ಈ ಸಂದೇಶವನ್ನು ನಿರ್ಲಕ್ಷಿಸಿ",
"emails.magicSession.thanks": "ಧನ್ಯವಾದಗಳು",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "감사합니다",
"emails.verification.signature": "{{project}} 팀",
"emails.magicSession.subject": "로그인",
"emails.magicSession.hello": "안녕하세요,",
"emails.magicSession.hello": "안녕하세요",
"emails.magicSession.body": "로그인 하시려면 링크를 클릭하여주세요.",
"emails.magicSession.footer": "이 이메일 계정으로 로그인 신청을 하지 않으셨다면 이 메세지를 무시하여주세요.",
"emails.magicSession.thanks": "감사합니다",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Gratias",
"emails.verification.signature": "{{project}} Team",
"emails.magicSession.subject": "Log in",
"emails.magicSession.hello": "Salve ibi,",
"emails.magicSession.hello": "Salve ibi",
"emails.magicSession.body": "Hanc nexum cum login",
"emails.magicSession.footer": "Si verificationem huius inscriptionis non postulasti, nuntium hunc ignorare potes.",
"emails.magicSession.thanks": "Gratias",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Merci",
"emails.verification.signature": "{{project}} équipe",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Hey,",
"emails.magicSession.hello": "Hey",
"emails.magicSession.body": "Follegt dëse Link fir umellen.",
"emails.magicSession.footer": "Wann Dir net gefrot hutt Iech mat dëser E -Mail anzemelden, kënnt Dir dëse Message ignoréieren.",
"emails.magicSession.thanks": "Merci",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Ačiū",
"emails.verification.signature": "{{project}} komanda",
"emails.magicSession.subject": "Prisijungti",
"emails.magicSession.hello": "Labas,",
"emails.magicSession.hello": "Labas",
"emails.magicSession.body": "Spauskite šią nuorodą, kad prisijungtumėte.",
"emails.magicSession.footer": "Jei neprašėte prisijungti naudojantis šiuo el. paštu, galite ignoruoti šį pranešimą.",
"emails.magicSession.thanks": "Ačiū",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Paldies",
"emails.verification.signature": "{{project}} komanda",
"emails.magicSession.subject": "Ieiet",
"emails.magicSession.hello": "Sveicināti,",
"emails.magicSession.hello": "Sveicināti",
"emails.magicSession.body": "Sekojiet saitei, lai ieietu.",
"emails.magicSession.footer": "Ja Jūs nepieprasījāt ieiet ar šo e-pasta adresi, lūdzu, ignorējiet šo ziņu.",
"emails.magicSession.thanks": "Paldies",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "നന്ദി",
"emails.verification.signature": "{{project}} ടീം",
"emails.magicSession.subject": "ലോഗിൻ",
"emails.magicSession.hello": "നമസ്കാരം,",
"emails.magicSession.hello": "നമസ്കാരം",
"emails.magicSession.body": "ലോഗിൻ ചെയ്യുന്നതിനായി ഈ ലിങ്ക് പിന്തുടരുക.",
"emails.magicSession.footer": "ഈ ഇമെയിൽ ഉപയോഗിച്ച് ലോഗിൻ ചെയ്യാൻ നിങ്ങൾ ആവശ്യപ്പെട്ടില്ലെങ്കിൽ, ഈ സന്ദേശം അവഗണിക്കാവുന്നതാണ്.",
"emails.magicSession.thanks": "നന്ദി",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "धन्यवाद",
"emails.verification.signature": "{{project}} संघ",
"emails.magicSession.subject": "लॉगिन करा",
"emails.magicSession.hello": "नमस्कार ,",
"emails.magicSession.hello": "नमस्कार ",
"emails.magicSession.body": "लॉगिन करण्यासाठी या लिंकचे अनुसरण करा.",
"emails.magicSession.footer": "आपण या ईमेलचा वापर करून लॉगिन करण्यास सांगितले नसल्यास, आपण या संदेशाकडे दुर्लक्ष करू शकता.",
"emails.magicSession.thanks": "धन्यवाद",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Terima kasih",
"emails.verification.signature": "{{project}} team",
"emails.magicSession.subject": "Log masuk",
"emails.magicSession.hello": "Hey,",
"emails.magicSession.hello": "Hey",
"emails.magicSession.body": "Tekan pautan ini untuk log masuk.",
"emails.magicSession.footer": "Sekiranya anda tidak membuat permintaan untuk log masuk menggunakan email ini, sila abaikan mesej ini.",
"emails.magicSession.thanks": "Terima kasih",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Takk",
"emails.verification.signature": "{{project}} team",
"emails.magicSession.subject": "Pålogging",
"emails.magicSession.hello": "Hei,",
"emails.magicSession.hello": "Hei",
"emails.magicSession.body": "Følg denne lenken for å logge på.",
"emails.magicSession.footer": "Dersom du ikke ba om å logge på med denne e-postadressen, kan du se bort fra denne meldingen.",
"emails.magicSession.thanks": "Takk",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "धन्यवाद",
"emails.verification.signature": "{{project}} समूह",
"emails.magicSession.subject": "लगइन",
"emails.magicSession.hello": "नमस्ते,",
"emails.magicSession.hello": "नमस्ते",
"emails.magicSession.body": "लगइन गर्नको लागी यो लिंकमा जानुहोस।",
"emails.magicSession.footer": "यदि तपाइँले यो इमेल प्रयोग गरेर लगइन गर्न सोध्नु भएको छैन भने तपाइँले यो सन्देश लाई बेवास्ता गर्न सक्नुहुन्छ।",
"emails.magicSession.thanks": "धन्यवाद",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Bedankt",
"emails.verification.signature": "{{project}} team",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Hoi,",
"emails.magicSession.hello": "Hoi",
"emails.magicSession.body": "Volg deze link om in te loggen",
"emails.magicSession.footer": "Als u geen aanvraag heeft gemaakt om met deze mail in te loggen, kan u deze mail negeren",
"emails.magicSession.thanks": "Bedankt",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Takk",
"emails.verification.signature": "{{project}} team",
"emails.magicSession.subject": "Pålogging",
"emails.magicSession.hello": "Hei,",
"emails.magicSession.hello": "Hei",
"emails.magicSession.body": "Følg denne lenkja for å logge på.",
"emails.magicSession.footer": "Om du ikkje ba om å logge på med denne e-postadressa, kan du ignorera denne meldinga.",
"emails.magicSession.thanks": "Takk",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "ଧନ୍ୟବାଦ",
"emails.verification.signature": "{{project}} ଦଳ",
"emails.magicSession.subject": "ଲଗଇନ୍ କରନ୍ତୁ",
"emails.magicSession.hello": "ନମସ୍କାର,",
"emails.magicSession.hello": "ନମସ୍କାର",
"emails.magicSession.body": "ଲଗଇନ୍ କରିବାକୁ ଏହି ଲିଙ୍କ୍ ଅନୁସରଣ କରନ୍ତୁ |",
"emails.magicSession.footer": "ଯଦି ଆପଣ ଏହି ଇମେଲ୍ ବ୍ୟବହାର କରି ଲଗଇନ୍ କରିବାକୁ କହି ନାହାଁନ୍ତି, ତେବେ ଆପଣ ଏହି ସନ୍ଦେଶକୁ ଉପେକ୍ଷା କରିପାରିବେ |",
"emails.magicSession.thanks": "ଧନ୍ୟବାଦ",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Dziękujemy",
"emails.verification.signature": "Zespół {{project}}",
"emails.magicSession.subject": "Logowanie",
"emails.magicSession.hello": "Cześć,",
"emails.magicSession.hello": "Cześć",
"emails.magicSession.body": "Kliknij w ten link, aby zalogować się.",
"emails.magicSession.footer": "Jeśli to nie Ty prosiłeś o logowanie przy użyciu tego adresu e-mail, zignoruj tę wiadomość.",
"emails.magicSession.thanks": "Dziękujemy",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Muito obrigado",
"emails.verification.signature": "Time {{project}}",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Olá,",
"emails.magicSession.hello": "Olá",
"emails.magicSession.body": "Clique neste link para entrar.",
"emails.magicSession.footer": "Se você não solicitou conectar-se com este e-mail, ignore essa mensagem.",
"emails.magicSession.thanks": "Muito obrigado",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Obrigado",
"emails.verification.signature": "Equipa {{project}}",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Olá ,",
"emails.magicSession.hello": "Olá ",
"emails.magicSession.body": "Siga esta ligação para iniciar sessão.",
"emails.magicSession.footer": "Se não pediu para entrar usando este e-mail, pode ignorar esta mensagem.",
"emails.magicSession.thanks": "Obrigado",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Mulțumim",
"emails.verification.signature": "Echipa {{project}}",
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Bună ziua,",
"emails.magicSession.hello": "Bună ziua",
"emails.magicSession.body": "Urmează acest link pentru logare.",
"emails.magicSession.footer": "Dacă nu ai incercat să te loghezi folosing această adresa de email, poți ignora acest mesaj.",
"emails.magicSession.thanks": "Mulțumim",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Спасибо",
"emails.verification.signature": "команда {{project}}",
"emails.magicSession.subject": "Логин",
"emails.magicSession.hello": "Здравствуйте,",
"emails.magicSession.hello": "Здравствуйте",
"emails.magicSession.body": "Перейдите по ссылке, чтобы войти.",
"emails.magicSession.footer": "Если вы не просили войти, используя этот адрес электронной почты, проигнорируйте это сообщение.",
"emails.magicSession.thanks": "Спасибо",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "धन्यवादः",
"emails.verification.signature": "{{project}} गणः",
"emails.magicSession.subject": "संप्रवेशः",
"emails.magicSession.hello": "अयि,",
"emails.magicSession.hello": "अयि",
"emails.magicSession.body": "संप्रवेशार्थमिदं संयोगसूत्रमनुसरतु।",
"emails.magicSession.footer": "अनेन ई-पत्रण यदि संप्रवेशो नेष्यते तर्हि वात्र्तामिमामुपेक्षताम्‌।",
"emails.magicSession.thanks": "धन्यवादः",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "ස්තුතියි",
"emails.verification.signature": "{{project}} කණ්ඩායම",
"emails.magicSession.subject": "ප්‍රවේශ වන්න",
"emails.magicSession.hello": "හේයි,",
"emails.magicSession.hello": "හේයි",
"emails.magicSession.body": "ප්‍රවේශ වීමට මෙම සම්බන්ධකය අනුගමනය කරන්න.",
"emails.magicSession.footer": "මෙම විද්‍යුත් තැපෑල භාවිතයෙන් ප්‍රවේශ වීමට ඔබ ඉල්ලුවේ නැත්නම්, ඔබට මෙම පණිවිඩය නොසලකා හැරිය හැක.",
"emails.magicSession.thanks": "ස්තුතියි",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Ďakujeme.",
"emails.verification.signature": "{{project}} tím",
"emails.magicSession.subject": "Prihlásenie",
"emails.magicSession.hello": "Ahoj,",
"emails.magicSession.hello": "Ahoj",
"emails.magicSession.body": "Použi tento link pre prihlásenie.",
"emails.magicSession.footer": "Ak si nepožiadal o prihlásenie cez email, túto správu môžeš ignorovať.",
"emails.magicSession.thanks": "Ďakujeme",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Ndatenda",
"emails.verification.signature": "Chikwata che{{project}}",
"emails.magicSession.subject": "Pinda",
"emails.magicSession.hello": "Hesi,",
"emails.magicSession.hello": "Hesi",
"emails.magicSession.body": "Baya chinongedzo ichi kuti upinde muakaundi yako.",
"emails.magicSession.footer": "Kana usina kukumbira kupinda muakaundi yako uchishandisa email iyi, unogona kufuratira meseji iyi.",
"emails.magicSession.thanks": "Ndatenda",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Tack",
"emails.verification.signature": "{{project}} teamet",
"emails.magicSession.subject": "Logga in",
"emails.magicSession.hello": "Hej,",
"emails.magicSession.hello": "Hej",
"emails.magicSession.body": "Klicka på denna länk för att logga in.",
"emails.magicSession.footer": "Om du inte bad om att logga in med denna e-postadress kan du ignorera detta mail.",
"emails.magicSession.thanks": "Tack",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "நன்றி",
"emails.verification.signature": "{{project}} குழு ",
"emails.magicSession.subject": "உள்நுழைய",
"emails.magicSession.hello": "ஏய்,",
"emails.magicSession.hello": "ஏய்",
"emails.magicSession.body": "இந்த இணைப்பைப் பின்தொடரவும் உள்நுழைய",
"emails.magicSession.footer": "இந்த மின்னஞ்சலைப் பயன்படுத்தி உள்நுழையுமாறு உங்களிடம் கேட்கப்படாவிட்டால், இந்தச் செய்தியைப் புறக்கணிக்கலாம்.",
"emails.magicSession.thanks": "நன்றி",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "ధన్యవాదాలు",
"emails.verification.signature": "{{project}} జట్",
"emails.magicSession.subject": "లాగిన్",
"emails.magicSession.hello": "నమస్కారము,",
"emails.magicSession.hello": "నమస్కారము",
"emails.magicSession.body": "లాగిన్ చేయడానికి ఈ లింక్ ని అనుసరించండి",
"emails.magicSession.footer": "మీరు ఈ ఇమెయిల్ ని ఉపయోగించి లాగిన్ చేయమని అడగకపోతే, మీరు ఈ సందేశాన్ని విస్మరించవచ్చు",
"emails.magicSession.thanks": "ధన్యవాదాలు",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "ขอบคุณ",
"emails.verification.signature": "ทีม {{project}}",
"emails.magicSession.subject": "เข้าสู่ระบบ",
"emails.magicSession.hello": "เรียนผู้ใช้งาน,",
"emails.magicSession.hello": "เรียนผู้ใช้งาน",
"emails.magicSession.body": "กดเข้าไปที่ลิงก์นี้เพื่อเข้าสู่ระบบ",
"emails.magicSession.footer": "หากท่านไม่ได้ต้องการที่จะเข้าสู่ระบบด้วยอีเมลนี้ ท่านสามารถเพิกเฉยข้อความนี้ได้",
"emails.magicSession.thanks": "ขอบคุณ",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Salamat",
"emails.verification.signature": "Pangkat ng {{project}}",
"emails.magicSession.subject": "Mag log in",
"emails.magicSession.hello": "Kamusta, ",
"emails.magicSession.hello": "Kamusta ",
"emails.magicSession.body": "Sundin ang link na ito upang mag-login.",
"emails.magicSession.footer": "Kung hindi mo hiningi na mag-login gamit ang email na ito, maaari mong balewalain ang mensahe na ito.",
"emails.magicSession.thanks": "Salamat",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Teşekkürler",
"emails.verification.signature": "{{project}} takımı",
"emails.magicSession.subject": "Giriş",
"emails.magicSession.hello": "Merhaba,",
"emails.magicSession.hello": "Merhaba",
"emails.magicSession.body": "Giriş yapmak için tıklayın.",
"emails.magicSession.footer": "Eğer bu eposta adresini kullanarak giriş yapmak istemediyseniz devam etmeyin.",
"emails.magicSession.thanks": "Teşekkürler",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Дякуємо",
"emails.verification.signature": "команда {{project}}",
"emails.magicSession.subject": "Логін",
"emails.magicSession.hello": "Вітаємо,",
"emails.magicSession.hello": "Вітаємо",
"emails.magicSession.body": "Перейдіть за цим посиланням, щоб увійти.",
"emails.magicSession.footer": "Якщо ви не просили увійти за допомогою цієї електронної пошти, ви можете ігнорувати це повідомлення.",
"emails.magicSession.thanks": "Дякуємо",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "شکریہ",
"emails.verification.signature": "ٹیم۔ {{project}}",
"emails.magicSession.subject": "اگ ان کریں",
"emails.magicSession.hello": "خوش آمدید,",
"emails.magicSession.hello": "خوش آمدید",
"emails.magicSession.body": "لاگ ان کرنے کے لیے اس لنک پر عمل کریں۔",
"emails.magicSession.footer": "اگر آپ نے اس ای میل کا استعمال کرتے ہوئے لاگ ان کرنے کے لیے نہیں کہا تو آپ اس پیغام کو نظر انداز کر سکتے ہیں۔",
"emails.magicSession.thanks": "شکریہ",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "Cảm ơn",
"emails.verification.signature": "Nhóm {{project}}",
"emails.magicSession.subject": "Đăng nhập",
"emails.magicSession.hello": "Chào,",
"emails.magicSession.hello": "Chào",
"emails.magicSession.body": "Nhấn vào đường dẫn sau để đăng nhập.",
"emails.magicSession.footer": "Nếu bạn không yêu cầu đăng nhập bằng email, bạn có thể bỏ qua email này.",
"emails.magicSession.thanks": "Cảm ơn",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "谢谢",
"emails.verification.signature": "{{project}} 团队",
"emails.magicSession.subject": "登录",
"emails.magicSession.hello": "你好,",
"emails.magicSession.hello": "你好",
"emails.magicSession.body": "点此链接登录。",
"emails.magicSession.footer": "如果您没有要求使用此电子邮件登录,则可忽略此消息。",
"emails.magicSession.thanks": "谢谢",

View file

@ -10,7 +10,7 @@
"emails.verification.thanks": "謝謝",
"emails.verification.signature": "{{project}} 團隊",
"emails.magicSession.subject": "登入",
"emails.magicSession.hello": "嗨",
"emails.magicSession.hello": "嗨",
"emails.magicSession.body": "點此連結登入。",
"emails.magicSession.footer": "如果您沒有要求使用此電子郵件登入,則可以忽略此消息。",
"emails.magicSession.thanks": "謝謝",

View file

@ -15,7 +15,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '14.0.0-rc.7',
'version' => '14.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.0-rc.6',
'version' => '12.0.1',
'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-rc.6',
'version' => '5.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-rc.6',
'version' => '5.0.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@ -185,7 +185,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '0.6.0-rc.17',
'version' => '0.6.0',
'url' => 'https://github.com/appwrite/sdk-for-console',
'package' => '',
'enabled' => true,
@ -195,15 +195,15 @@ return [
'family' => APP_PLATFORM_CONSOLE,
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/console-web'),
'gitUrl' => 'https://github.com/appwrite/sdk-for-console.git',
'gitBranch' => '1.5.x',
'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git',
'gitBranch' => 'main',
'gitRepoName' => 'sdk-for-console',
'gitUserName' => 'appwrite',
],
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '4.2.0-rc.6',
'version' => '5.0.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@ -231,7 +231,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '12.0.0-rc.6',
'version' => '12.0.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@ -249,7 +249,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '10.0.0-rc.6',
'version' => '10.0.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@ -267,7 +267,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '11.0.0-rc.6',
'version' => '11.0.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@ -285,7 +285,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '5.0.0-rc.6',
'version' => '5.0.1',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@ -303,7 +303,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '11.0.0-rc.6',
'version' => '11.0.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@ -321,7 +321,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '4.0.0-rc.6',
'version' => '4.0.0',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => '',
'enabled' => false,
@ -339,7 +339,7 @@ return [
[
'key' => 'java',
'name' => 'Java',
'version' => '4.0.0-rc.6',
'version' => '4.0.0',
'url' => 'https://github.com/appwrite/sdk-for-java',
'package' => '',
'enabled' => false,
@ -357,7 +357,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.8.0-rc.6',
'version' => '0.8.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@ -375,7 +375,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '11.0.0-rc.6',
'version' => '11.0.1',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@ -393,7 +393,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '5.0.0-rc.6',
'version' => '5.0.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@ -415,7 +415,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '5.0.0-rc.6',
'version' => '5.0.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,

View file

@ -5,11 +5,11 @@
*/
use Appwrite\Runtimes\Runtimes;
use Utopia\Http\Http;
use Utopia\System\System;
$runtimes = new Runtimes('v2');
$allowList = empty(Http::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', Http::getEnv('_APP_FUNCTIONS_RUNTIMES'));
$allowList = empty(System::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES'));
$runtimes = $runtimes->getAll(true, $allowList);

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

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,7 @@ use Utopia\Http\Validator\WhiteList;
use Utopia\Image\Image;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Utopia\System\System;
$avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) {
@ -76,7 +77,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
}
if (empty($gitHubSession)) {
throw new Exception(Exception::GENERAL_UNKNOWN, 'GitHub session not found.');
throw new Exception(Exception::USER_SESSION_NOT_FOUND, 'GitHub session not found.');
}
$provider = $gitHubSession->getAttribute('provider', '');
@ -155,7 +156,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
];
} catch (Exception $error) {
if ($logger) {
$version = Http::getEnv('_APP_VERSION', 'UNKNOWN');
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace('console');
@ -174,7 +175,8 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
$log->setAction('avatarsGetGitHub');
$isProduction = Http::getEnv('_APP_ENV', 'development') === 'production';
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
@ -348,8 +350,8 @@ Http::get('/v1/avatars/favicon')
CURLOPT_URL => $url,
CURLOPT_USERAGENT => \sprintf(
APP_USERAGENT,
Http::getEnv('_APP_VERSION', 'UNKNOWN'),
Http::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
System::getEnv('_APP_VERSION', 'UNKNOWN'),
System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
]);

View file

@ -5,6 +5,7 @@ use Appwrite\Utopia\Response;
use Utopia\Database\Document;
use Utopia\Http\Http;
use Utopia\Http\Validator\Text;
use Utopia\System\System;
Http::init()
->groups(['console'])
@ -29,24 +30,24 @@ Http::get('/v1/console/variables')
->label('sdk.response.model', Response::MODEL_CONSOLE_VARIABLES)
->inject('response')
->action(function (Response $response) {
$isDomainEnabled = !empty(Http::getEnv('_APP_DOMAIN', ''))
&& !empty(Http::getEnv('_APP_DOMAIN_TARGET', ''))
&& Http::getEnv('_APP_DOMAIN', '') !== 'localhost'
&& Http::getEnv('_APP_DOMAIN_TARGET', '') !== 'localhost';
$isDomainEnabled = !empty(System::getEnv('_APP_DOMAIN', ''))
&& !empty(System::getEnv('_APP_DOMAIN_TARGET', ''))
&& System::getEnv('_APP_DOMAIN', '') !== 'localhost'
&& System::getEnv('_APP_DOMAIN_TARGET', '') !== 'localhost';
$isVcsEnabled = !empty(Http::getEnv('_APP_VCS_GITHUB_APP_NAME', ''))
&& !empty(Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY', ''))
&& !empty(Http::getEnv('_APP_VCS_GITHUB_APP_ID', ''))
&& !empty(Http::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''))
&& !empty(Http::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''));
$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(Http::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', ''));
$isAssistantEnabled = !empty(System::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', ''));
$variables = new Document([
'_APP_DOMAIN_TARGET' => Http::getEnv('_APP_DOMAIN_TARGET'),
'_APP_STORAGE_LIMIT' => +Http::getEnv('_APP_STORAGE_LIMIT'),
'_APP_FUNCTIONS_SIZE_LIMIT' => +Http::getEnv('_APP_FUNCTIONS_SIZE_LIMIT'),
'_APP_USAGE_STATS' => Http::getEnv('_APP_USAGE_STATS'),
'_APP_DOMAIN_TARGET' => System::getEnv('_APP_DOMAIN_TARGET'),
'_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'),
'_APP_FUNCTIONS_SIZE_LIMIT' => +System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT'),
'_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'),
'_APP_VCS_ENABLED' => $isVcsEnabled,
'_APP_DOMAIN_ENABLED' => $isDomainEnabled,
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled

View file

@ -47,6 +47,7 @@ use Utopia\Storage\Validator\File;
use Utopia\Storage\Validator\FileExt;
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
use Utopia\System\System;
use Utopia\VCS\Adapter\Git\GitHub;
use Utopia\VCS\Exception\RepositoryNotFound;
@ -56,8 +57,8 @@ $redeployVcs = function (Request $request, Document $function, Document $project
$deploymentId = ID::unique();
$entrypoint = $function->getAttribute('entrypoint', '');
$providerInstallationId = $installation->getAttribute('providerInstallationId', '');
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId);
$providerRepositoryId = $function->getAttribute('providerRepositoryId', '');
@ -145,7 +146,7 @@ Http::post('/v1/functions')
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->param('events', [], new ArrayList(new FunctionEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) Http::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
->param('timeout', 15, new Range(1, (int) System::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true)
->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
@ -172,7 +173,7 @@ Http::post('/v1/functions')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $auth) use ($redeployVcs) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
$allowList = \array_filter(\explode(',', Http::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
if (!empty($allowList) && !\in_array($runtime, $allowList)) {
throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $runtime . '" is not supported');
@ -233,7 +234,7 @@ Http::post('/v1/functions')
$schedule = $auth->skip(
fn () => $dbForConsole->createDocument('schedules', new Document([
'region' => Http::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
'resourceType' => 'function',
'resourceId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
@ -282,7 +283,7 @@ Http::post('/v1/functions')
$redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
}
$functionsDomain = Http::getEnv('_APP_DOMAIN_FUNCTIONS', '');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($functionsDomain)) {
$ruleId = ID::unique();
$routeSubdomain = ID::unique();
@ -424,7 +425,7 @@ Http::get('/v1/functions/runtimes')
->action(function (Response $response) {
$runtimes = Config::getParam('runtimes');
$allowList = \array_filter(\explode(',', Http::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
$allowed = [];
foreach ($runtimes as $key => $runtime) {
@ -679,7 +680,7 @@ Http::put('/v1/functions/:functionId')
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->param('events', [], new ArrayList(new FunctionEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) Http::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
->param('timeout', 15, new Range(1, (int) System::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true)
->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
@ -1098,7 +1099,7 @@ Http::post('/v1/functions/:functionId/deployments')
}
$fileExt = new FileExt([FileExt::TYPE_GZIP]);
$fileSizeValidator = new FileSize(Http::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'));
$fileSizeValidator = new FileSize(System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'));
$upload = new Upload();
// Make sure we handle a single file and multiple files the same way
@ -1587,7 +1588,7 @@ Http::post('/v1/functions/:functionId/executions')
}
if (!$current->isEmpty()) {
$jwtObj = new JWT(Http::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
'sessionId' => $current->getId(),
@ -1710,7 +1711,7 @@ Http::post('/v1/functions/:functionId/executions')
]);
/** Execute function */
$executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST'));
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
try {
$version = $function->getAttribute('version', 'v2');
$command = $runtime['startCommand'];

View file

@ -1,6 +1,8 @@
<?php
use Appwrite\Auth\Auth;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\GraphQL\Promises\Adapter;
use Appwrite\GraphQL\Schema;
use Appwrite\Utopia\Request;
@ -13,9 +15,25 @@ use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\Rules\QueryDepth;
use Swoole\Coroutine\WaitGroup;
use Utopia\Database\Document;
use Utopia\Http\Http;
use Utopia\Database\Validator\Authorization;
use Utopia\System\System;
use Utopia\Http\Validator\JSON;
use Utopia\Http\Validator\Text;
use Utopia\Http\Http;
Http::init()
->groups(['graphql'])
->inject('project')
->inject('auth')
->action(function (Document $project, Authorization $auth) {
if (
array_key_exists('graphql', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['graphql']
&& !(Auth::isPrivilegedUser($auth->getRoles()) || Auth::isAppUser($auth->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
});
Http::get('/v1/graphql')
->desc('GraphQL endpoint')
@ -161,9 +179,9 @@ function execute(
Adapter $promiseAdapter,
array $query
): array {
$maxBatchSize = Http::getEnv('_APP_GRAPHQL_MAX_BATCH_SIZE', 10);
$maxComplexity = Http::getEnv('_APP_GRAPHQL_MAX_COMPLEXITY', 250);
$maxDepth = Http::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3);
$maxBatchSize = System::getEnv('_APP_GRAPHQL_MAX_BATCH_SIZE', 10);
$maxComplexity = System::getEnv('_APP_GRAPHQL_MAX_COMPLEXITY', 250);
$maxDepth = System::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3);
if (!empty($query) && !isset($query[0])) {
$query = [$query];
@ -183,7 +201,7 @@ function execute(
$flags = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE;
$validations = GraphQL::getStandardValidationRules();
if (Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') {
if (System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') {
$validations[] = new DisableIntrospection();
$validations[] = new QueryComplexity($maxComplexity);
$validations[] = new QueryDepth($maxDepth);

View file

@ -21,6 +21,7 @@ use Utopia\Registry\Registry;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Utopia\System\System;
Http::get('/v1/health')
->desc('Get HTTP')
@ -672,6 +673,60 @@ Http::get('/v1/health/queue/functions')
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
Http::get('/v1/health/queue/usage')
->desc('Get usage queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueUsage')
->label('sdk.description', '/docs/references/health/get-queue-usage.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_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('queue')
->inject('response')
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$client = new Client(Event::USAGE_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
}
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
Http::get('/v1/health/queue/usage-dump')
->desc('Get usage dump queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueUsageDump')
->label('sdk.description', '/docs/references/health/get-queue-usage-dump.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_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('queue')
->inject('response')
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$client = new Client(Event::USAGE_DUMP_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
}
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
Http::get('/v1/health/storage/local')
->desc('Get local storage')
->groups(['api', 'health'])
@ -775,13 +830,13 @@ Http::get('/v1/health/anti-virus')
'version' => ''
];
if (Http::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
if (System::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
$output['status'] = 'disabled';
$output['version'] = '';
} else {
$antivirus = new Network(
Http::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) Http::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
System::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) System::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
);
try {
@ -809,6 +864,7 @@ Http::get('/v1/health/queue/failed/:name')
Event::MAILS_QUEUE_NAME,
Event::FUNCTIONS_QUEUE_NAME,
Event::USAGE_QUEUE_NAME,
Event::USAGE_DUMP_QUEUE_NAME,
Event::WEBHOOK_CLASS_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,

View file

@ -1,5 +1,6 @@
<?php
use Ahc\Jwt\JWT;
use Appwrite\Auth\Validator\Phone;
use Appwrite\Detector\Detector;
use Appwrite\Event\Delete;
@ -35,7 +36,6 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\Roles;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
use Utopia\Http\Http;
use Utopia\Http\Validator\ArrayList;
use Utopia\Http\Validator\Boolean;
@ -45,6 +45,7 @@ use Utopia\Http\Validator\Range;
use Utopia\Http\Validator\Text;
use Utopia\Http\Validator\WhiteList;
use Utopia\Locale\Locale;
use Utopia\System\System;
use function Swoole\Coroutine\batch;
@ -2131,8 +2132,6 @@ Http::get('/v1/messaging/topics/:topicId')
throw new Exception(Exception::TOPIC_NOT_FOUND);
}
$topic = $dbForProject->getDocument('topics', $topicId);
$response
->dynamic($topic, Response::MODEL_TOPIC);
});
@ -2705,7 +2704,7 @@ Http::post('/v1/messaging/messages/email')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => Http::getEnv('_APP_REGION', 'default'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@ -2821,7 +2820,7 @@ Http::post('/v1/messaging/messages/sms')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => Http::getEnv('_APP_REGION', 'default'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@ -2939,23 +2938,35 @@ Http::post('/v1/messaging/messages/push')
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
if (!\in_array(Permission::read(Role::any()), \array_merge($file->getRead(), $bucket->getRead()))) {
throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC);
}
if (!\in_array($file->getAttribute('mimeType'), ['image/png', 'image/jpeg'])) {
throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
}
$host = Http::getEnv('_APP_DOMAIN', 'localhost');
$domain = new Domain(\parse_url($host, PHP_URL_HOST));
$protocol = Http::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$host = System::getEnv('_APP_DOMAIN', 'localhost');
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
if (!$domain->isKnown()) {
throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC);
$scheduleTime = $currentScheduledAt ?? $scheduledAt;
if (!\is_null($scheduleTime)) {
$expiry = (new \DateTime($scheduleTime))->add(new \DateInterval('P15D'))->format('U');
} else {
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
}
$image = "{$protocol}://{$host}/v1/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/view?project={$project->getId()}";
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
$jwt = $encoder->encode([
'iat' => \time(),
'exp' => $expiry,
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'projectId' => $project->getId(),
]);
$image = [
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'url' => "{$protocol}://{$host}/v1/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/push?project={$project->getId()}&jwt={$jwt}",
];
}
$pushData = [];
@ -2987,7 +2998,7 @@ Http::post('/v1/messaging/messages/push')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => Http::getEnv('_APP_REGION', 'default'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@ -3295,7 +3306,7 @@ Http::patch('/v1/messaging/messages/email/:messageId')
: MessageStatus::SCHEDULED;
}
} else {
$status = null;
$status = $message->getAttribute('status');
}
if (
@ -3332,7 +3343,7 @@ Http::patch('/v1/messaging/messages/email/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => Http::getEnv('_APP_REGION', 'default'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@ -3466,7 +3477,7 @@ Http::patch('/v1/messaging/messages/sms/:messageId')
: MessageStatus::SCHEDULED;
}
} else {
$status = null;
$status = $message->getAttribute('status');
}
if (
@ -3503,7 +3514,7 @@ Http::patch('/v1/messaging/messages/sms/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => Http::getEnv('_APP_REGION', 'default'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@ -3630,7 +3641,7 @@ Http::patch('/v1/messaging/messages/push/:messageId')
: MessageStatus::SCHEDULED;
}
} else {
$status = null;
$status = $message->getAttribute('status');
}
if (
@ -3667,7 +3678,7 @@ Http::patch('/v1/messaging/messages/push/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => Http::getEnv('_APP_REGION', 'default'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@ -3766,23 +3777,35 @@ Http::patch('/v1/messaging/messages/push/:messageId')
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
if (!\in_array(Permission::read(Role::any()), \array_merge($file->getRead(), $bucket->getRead()))) {
throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC);
}
if (!\in_array($file->getAttribute('mimeType'), ['image/png', 'image/jpeg'])) {
throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
}
$host = Http::getEnv('_APP_DOMAIN', 'localhost');
$domain = new Domain(\parse_url($host, PHP_URL_HOST));
$protocol = Http::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$host = System::getEnv('_APP_DOMAIN', 'localhost');
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
if (!$domain->isKnown()) {
throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC);
$scheduleTime = $currentScheduledAt ?? $scheduledAt;
if (!\is_null($scheduleTime)) {
$expiry = (new \DateTime($scheduleTime))->add(new \DateInterval('P15D'))->format('U');
} else {
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
}
$pushData['image'] = "{$protocol}://{$host}/v1/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/view?project={$project->getId()}";
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
$jwt = $encoder->encode([
'iat' => \time(),
'exp' => $expiry,
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'projectId' => $project->getId(),
]);
$pushData['image'] = [
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'url' => "{$protocol}://{$host}/v1/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/push?project={$project->getId()}&jwt={$jwt}"
];
}
$message->setAttribute('data', $pushData);

View file

@ -27,6 +27,7 @@ use Utopia\Migration\Sources\Appwrite;
use Utopia\Migration\Sources\Firebase;
use Utopia\Migration\Sources\NHost;
use Utopia\Migration\Sources\Supabase;
use Utopia\System\System;
include_once __DIR__ . '/../shared/api.php';
@ -109,8 +110,8 @@ Http::post('/v1/migrations/firebase/oauth')
->inject('request')
->action(function (array $resources, string $projectId, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations, Request $request) {
$firebase = new OAuth2Firebase(
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
);
@ -553,8 +554,8 @@ Http::get('/v1/migrations/firebase/report/oauth')
->inject('dbForConsole')
->action(function (array $resources, string $projectId, Response $response, Request $request, Document $user, Database $dbForConsole) {
$firebase = new OAuth2Firebase(
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
);
@ -575,7 +576,7 @@ Http::get('/v1/migrations/firebase/report/oauth')
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
}
if (Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') {
if (System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') {
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
}
@ -655,8 +656,8 @@ Http::get('/v1/migrations/firebase/connect')
$dbForConsole->updateDocument('users', $user->getId(), $user);
$oauth2 = new OAuth2Firebase(
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
);
$url = $oauth2->getLoginURL();
@ -710,8 +711,8 @@ Http::get('/v1/migrations/firebase/redirect')
// OAuth Authroization
if (!empty($code)) {
$oauth2 = new OAuth2Firebase(
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
);
@ -797,8 +798,8 @@ Http::get('/v1/migrations/firebase/projects')
->inject('request')
->action(function (Document $user, Response $response, Document $project, Database $dbForConsole, Request $request) {
$firebase = new OAuth2Firebase(
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
);
@ -819,7 +820,7 @@ Http::get('/v1/migrations/firebase/projects')
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
}
if (Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') {
if (System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') {
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
}

View file

@ -41,6 +41,7 @@ use Utopia\Http\Validator\URL;
use Utopia\Http\Validator\WhiteList;
use Utopia\Locale\Locale;
use Utopia\Pools\Group;
use Utopia\System\System;
Http::init()
->groups(['projects'])
@ -64,7 +65,7 @@ Http::post('/v1/projects')
->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('region', Http::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true)
->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true)
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
->param('logo', '', new Text(1024), 'Project logo.', true)
->param('url', '', new URL(), 'Project URL.', true)
@ -88,7 +89,7 @@ Http::post('/v1/projects')
throw new Exception(Exception::TEAM_NOT_FOUND);
}
$allowList = \array_filter(\explode(',', Http::getEnv('_APP_PROJECT_REGIONS', '')));
$allowList = \array_filter(\explode(',', System::getEnv('_APP_PROJECT_REGIONS', '')));
if (!empty($allowList) && !\in_array($region, $allowList)) {
throw new Exception(Exception::PROJECT_REGION_UNSUPPORTED, 'Region "' . $region . '" is not supported');
@ -131,7 +132,7 @@ Http::post('/v1/projects')
}
}
$databaseOverride = Http::getEnv('_APP_DATABASE_OVERRIDE', null);
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE', null);
$index = array_search($databaseOverride, $databases);
if ($index !== false) {
$database = $databases[$index];
@ -492,6 +493,71 @@ Http::patch('/v1/projects/:projectId/service/all')
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/api')
->desc('Update API status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateApiStatus')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('api', '', new WhiteList(array_keys(Config::getParam('apis')), true), 'API name.')
->param('status', null, new Boolean(), 'API status.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $api, bool $status, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$apis = $project->getAttribute('apis', []);
$apis[$api] = $status;
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('apis', $apis));
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/api/all')
->desc('Update all API status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateApiStatusAll')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('status', null, new Boolean(), 'API status.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$allApis = array_keys(Config::getParam('apis'));
$apis = [];
foreach ($allApis as $api) {
$apis[$api] = $status;
}
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('apis', $apis));
$response->dynamic($project, Response::MODEL_PROJECT);
});
Http::patch('/v1/projects/:projectId/oauth2')
->desc('Update project OAuth2')
->groups(['api', 'projects'])
@ -1555,8 +1621,8 @@ Http::post('/v1/projects/:projectId/smtp/tests')
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('emails', [], new ArrayList(new Email(), 10), 'Array of emails to send test email to. Maximum of 10 emails are allowed.')
->param('senderName', Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender')
->param('senderEmail', Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), new Email(), 'Email of the sender')
->param('senderName', System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender')
->param('senderEmail', System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), new Email(), 'Email of the sender')
->param('replyTo', '', new Email(), 'Reply to email', true)
->param('host', '', new HostName(), 'SMTP server host name')
->param('port', 587, new Integer(), 'SMTP server port', true)

View file

@ -19,6 +19,7 @@ use Utopia\Http\Validator\Domain as ValidatorDomain;
use Utopia\Http\Validator\Text;
use Utopia\Http\Validator\WhiteList;
use Utopia\Logger\Log;
use Utopia\System\System;
Http::post('/v1/proxy/rules')
->groups(['api', 'proxy'])
@ -44,7 +45,7 @@ Http::post('/v1/proxy/rules')
->inject('dbForConsole')
->inject('dbForProject')
->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForConsole, Database $dbForProject) {
$mainDomain = Http::getEnv('_APP_DOMAIN', '');
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($domain === $mainDomain) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
}
@ -108,13 +109,13 @@ Http::post('/v1/proxy/rules')
]);
$status = 'created';
$functionsDomain = Http::getEnv('_APP_DOMAIN_FUNCTIONS');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) {
$status = 'verified';
}
if ($status === 'created') {
$target = new Domain(Http::getEnv('_APP_DOMAIN_TARGET', ''));
$target = new Domain(System::getEnv('_APP_DOMAIN_TARGET', ''));
$validator = new CNAME($target->get()); // Verify Domain with DNS records
if ($validator->isValid($domain->get())) {
@ -296,7 +297,7 @@ Http::patch('/v1/proxy/rules/:ruleId/verification')
throw new Exception(Exception::RULE_NOT_FOUND);
}
$target = new Domain(Http::getEnv('_APP_DOMAIN_TARGET', ''));
$target = new Domain(System::getEnv('_APP_DOMAIN_TARGET', ''));
if (!$target->isKnown() || $target->isTest()) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Domain target must be configured as environment variable.');

View file

@ -1,5 +1,7 @@
<?php
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\ClamAV\Network;
use Appwrite\Event\Delete;
@ -44,6 +46,7 @@ use Utopia\Storage\Validator\File;
use Utopia\Storage\Validator\FileExt;
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
use Utopia\System\System;
Http::post('/v1/storage/buckets')
->desc('Create bucket')
@ -64,7 +67,7 @@ Http::post('/v1/storage/buckets')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', (int) Http::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) Http::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(Http::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true)
->param('maximumFileSize', (int) System::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) System::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
@ -241,7 +244,7 @@ Http::put('/v1/storage/buckets/:bucketId')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', null, new Range(1, (int) Http::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)Http::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true)
->param('maximumFileSize', null, new Range(1, (int) System::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
@ -257,7 +260,7 @@ Http::put('/v1/storage/buckets/:bucketId')
}
$permissions ??= $bucket->getPermissions();
$maximumFileSize ??= $bucket->getAttribute('maximumFileSize', (int) Http::getEnv('_APP_STORAGE_LIMIT', 0));
$maximumFileSize ??= $bucket->getAttribute('maximumFileSize', (int) System::getEnv('_APP_STORAGE_LIMIT', 0));
$allowedFileExtensions ??= $bucket->getAttribute('allowedFileExtensions', []);
$enabled ??= $bucket->getAttribute('enabled', true);
$encryption ??= $bucket->getAttribute('encryption', true);
@ -419,7 +422,7 @@ Http::post('/v1/storage/buckets/:bucketId/files')
}
$maximumFileSize = $bucket->getAttribute('maximumFileSize', 0);
if ($maximumFileSize > (int) Http::getEnv('_APP_STORAGE_LIMIT', 0)) {
if ($maximumFileSize > (int) System::getEnv('_APP_STORAGE_LIMIT', 0)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Maximum bucket file size is larger than _APP_STORAGE_LIMIT');
}
@ -521,10 +524,10 @@ Http::post('/v1/storage/buckets/:bucketId/files')
}
if ($chunksUploaded === $chunks) {
if (Http::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && $deviceForFiles->getType() === Storage::DEVICE_LOCAL) {
if (System::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && $deviceForFiles->getType() === Storage::DEVICE_LOCAL) {
$antivirus = new Network(
Http::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) Http::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
System::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) System::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
);
if (!$antivirus->fileScan($path)) {
@ -561,7 +564,7 @@ Http::post('/v1/storage/buckets/:bucketId/files')
if (empty($data)) {
$data = $deviceForFiles->read($path);
}
$key = Http::getEnv('_APP_OPENSSL_KEY_V1');
$key = System::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
}
@ -917,7 +920,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$algorithm = $file->getAttribute('algorithm', Compression::NONE);
$cipher = $file->getAttribute('openSSLCipher');
$mime = $file->getAttribute('mimeType');
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) Http::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) System::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
if (!\in_array($mime, $inputs)) {
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
} else {
@ -954,7 +957,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
Http::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
System::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@ -1102,7 +1105,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
Http::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
System::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@ -1251,7 +1254,162 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
Http::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
System::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
);
}
switch ($file->getAttribute('algorithm', Compression::NONE)) {
case Compression::ZSTD:
if (empty($source)) {
$source = $deviceForFiles->read($path);
}
$compressor = new Zstd();
$source = $compressor->decompress($source);
break;
case Compression::GZIP:
if (empty($source)) {
$source = $deviceForFiles->read($path);
}
$compressor = new GZIP();
$source = $compressor->decompress($source);
break;
}
if (!empty($source)) {
if (!empty($rangeHeader)) {
$response->send(substr($source, $start, ($end - $start + 1)));
}
$response->send($source);
return;
}
if (!empty($rangeHeader)) {
$response->send($deviceForFiles->read($path, $start, ($end - $start + 1)));
return;
}
$size = $deviceForFiles->getFileSize($path);
if ($size > APP_STORAGE_READ_BUFFER) {
for ($i = 0; $i < ceil($size / MAX_OUTPUT_CHUNK_SIZE); $i++) {
$response->chunk(
$deviceForFiles->read(
$path,
($i * MAX_OUTPUT_CHUNK_SIZE),
min(MAX_OUTPUT_CHUNK_SIZE, $size - ($i * MAX_OUTPUT_CHUNK_SIZE))
),
(($i + 1) * MAX_OUTPUT_CHUNK_SIZE) >= $size
);
}
} else {
$response->send($deviceForFiles->read($path));
}
});
Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->desc('Get file for push notification')
->groups(['api', 'storage'])
->label('scope', 'public')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('jwt', '', new Text(2048, 0), 'JSON Web Token to validate', true)
->inject('response')
->inject('request')
->inject('dbForProject')
->inject('project')
->inject('mode')
->inject('deviceForFiles')
->inject('auth')
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles, Authorization $auth) {
$bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
try {
$decoded = $decoder->decode($jwt);
} catch (JWTException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if (
$decoded['projectId'] !== $project->getId() ||
$decoded['bucketId'] !== $bucketId ||
$decoded['fileId'] !== $fileId ||
$decoded['exp'] < \time()
) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$isAPIKey = Auth::isAppUser($auth->getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
$mimes = Config::getParam('storage-mimes');
$path = $file->getAttribute('path', '');
if (!$deviceForFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
}
$contentType = 'text/plain';
if (\in_array($file->getAttribute('mimeType'), $mimes)) {
$contentType = $file->getAttribute('mimeType');
}
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage());
$size = $file->getAttribute('sizeOriginal', 0);
$rangeHeader = $request->getHeader('range');
if (!empty($rangeHeader)) {
$start = $request->getRangeStart();
$end = $request->getRangeEnd();
$unit = $request->getRangeUnit();
if ($end === null) {
$end = min(($start + 2000000 - 1), ($size - 1));
}
if ($unit != 'bytes' || $start >= $end || $end >= $size) {
throw new Exception(Exception::STORAGE_INVALID_RANGE);
}
$response
->addHeader('Accept-Ranges', 'bytes')
->addHeader('Content-Range', "bytes $start-$end/$size")
->addHeader('Content-Length', $end - $start + 1)
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
}
$source = '';
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = $deviceForFiles->read($path);
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
System::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@ -1480,6 +1638,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId')
if ($deviceDeleted) {
$queueForDeletes
->setType(DELETE_TYPE_CACHE_BY_RESOURCE)
->setResourceType('bucket/' . $bucket->getId())
->setResource('file/' . $fileId)
;

View file

@ -414,7 +414,7 @@ Http::post('/v1/teams/:teamId/memberships')
$isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles());
$isAppUser = Auth::isAppUser($auth->getRoles());
if (!$isPrivilegedUser && !$isAppUser && empty(Http::getEnv('_APP_SMTP_HOST'))) {
if (!$isPrivilegedUser && !$isAppUser && empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
}
@ -577,8 +577,8 @@ Http::post('/v1/teams/:teamId/memberships')
$smtp = $project->getAttribute('smtp', []);
$smtpEnabled = $smtp['enabled'] ?? false;
$senderEmail = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
if ($smtpEnabled) {
@ -639,7 +639,7 @@ Http::post('/v1/teams/:teamId/memberships')
->trigger()
;
} elseif (!empty($phone)) {
if (empty(Http::getEnv('_APP_SMS_PROVIDER'))) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@ -720,7 +720,7 @@ Http::get('/v1/teams/:teamId/memberships')
}
// Set internal queries
$queries[] = Query::equal('teamId', [$teamId]);
$queries[] = Query::equal('teamInternalId', [$team->getInternalId()]);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
@ -951,16 +951,16 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
}
if ($membership->getAttribute('teamId') !== $teamId) {
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
}
$team = $auth->skip(fn () => $dbForProject->getDocument('teams', $teamId));
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
}
if ($membership->getAttribute('teamInternalId') !== $team->getInternalId()) {
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
}
if (Auth::hash($secret) !== $membership->getAttribute('secret')) {
throw new Exception(Exception::TEAM_INVALID_SECRET);
}
@ -973,7 +973,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$user->setAttributes($dbForProject->getDocument('users', $userId)->getArrayCopy()); // Get user
}
if ($membership->getAttribute('userId') !== $user->getId()) {
if ($membership->getAttribute('userInternalId') !== $user->getInternalId()) {
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')');
}
@ -1080,10 +1080,6 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::TEAM_INVITE_NOT_FOUND);
}
if ($membership->getAttribute('teamId') !== $teamId) {
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
}
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
if ($user->isEmpty()) {
@ -1096,6 +1092,10 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::TEAM_NOT_FOUND);
}
if ($membership->getAttribute('teamInternalId') !== $team->getInternalId()) {
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
}
try {
$dbForProject->deleteDocument('memberships', $membership->getId());
} catch (AuthorizationException $exception) {

View file

@ -471,15 +471,7 @@ Http::post('/v1/users/:userId/targets')
->action(function (string $targetId, string $userId, string $providerType, string $identifier, string $providerId, string $name, Event $queueForEvents, Response $response, Database $dbForProject) {
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
$provider = new Document();
if ($providerType === MESSAGE_TYPE_PUSH) {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
}
$provider = $dbForProject->getDocument('providers', $providerId);
switch ($providerType) {
case 'email':
@ -520,7 +512,7 @@ Http::post('/v1/users/:userId/targets')
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'providerId' => $providerId ?? null,
'providerId' => empty($provider->getId()) ? null : $provider->getId(),
'providerInternalId' => $provider->isEmpty() ? null : $provider->getInternalId(),
'providerType' => $providerType,
'userId' => $userId,
@ -814,6 +806,9 @@ Http::get('/v1/users/:userId/logs')
$output[$i] = new Document([
'event' => $log['event'],
'userId' => ID::custom($log['data']['userId']),
'userEmail' => $log['data']['userEmail'] ?? null,
'userName' => $log['data']['userName'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $os['osCode'],
@ -842,7 +837,7 @@ Http::get('/v1/users/:userId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByUser($user->getId()),
'total' => $audit->countLogsByUser($user->getInternalId()),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@ -1212,7 +1207,7 @@ Http::patch('/v1/users/:userId/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::USER_EMAIL_ALREADY_EXISTS);

View file

@ -35,6 +35,7 @@ use Utopia\Http\Http;
use Utopia\Http\Validator\Boolean;
use Utopia\Http\Validator\Host;
use Utopia\Http\Validator\Text;
use Utopia\System\System;
use Utopia\VCS\Adapter\Git\GitHub;
use Utopia\VCS\Exception\RepositoryNotFound;
@ -42,9 +43,13 @@ use function Swoole\Coroutine\batch;
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request, Authorization $auth) {
foreach ($repositories as $resource) {
$resourceType = $resource->getAttribute('resourceType');
try {
$resourceType = $resource->getAttribute('resourceType');
if ($resourceType !== "function") {
continue;
}
if ($resourceType === "function") {
$projectId = $resource->getAttribute('projectId');
$project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId));
$dbForProject = $getProjectDB($project);
@ -238,11 +243,22 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment);
->setDeployment($deployment)
->setProject($project); // set the project because it won't be set for git deployments
$queueForBuilds->trigger(); // must trigger here so that we create a build for each function
//TODO: Add event?
} catch (Throwable $e) {
$errors[] = $e->getMessage();
}
}
$queueForBuilds->reset(); // prevent shutdown hook from triggering again
if (!empty($errors)) {
throw new Exception(Exception::GENERAL_UNKNOWN, \implode("\n", $errors));
}
};
Http::get('/v1/vcs/github/authorize')
@ -270,7 +286,7 @@ Http::get('/v1/vcs/github/authorize')
'failure' => $failure,
]);
$appName = Http::getEnv('_APP_VCS_GITHUB_APP_NAME');
$appName = System::getEnv('_APP_VCS_GITHUB_APP_NAME');
if (empty($appName)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.');
@ -341,7 +357,7 @@ Http::get('/v1/vcs/github/callback')
// OAuth Authroization
if (!empty($code)) {
$oauth2 = new OAuth2Github(Http::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), Http::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), "");
$oauth2 = new OAuth2Github(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), "");
$accessToken = $oauth2->getAccessToken($code) ?? '';
$refreshToken = $oauth2->getRefreshToken($code) ?? '';
$accessTokenExpiry = $oauth2->getAccessTokenExpiry($code) ?? '';
@ -388,8 +404,8 @@ Http::get('/v1/vcs/github/callback')
// Create / Update installation
if (!empty($providerInstallationId)) {
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId) ?? '';
@ -473,8 +489,8 @@ Http::post('/v1/vcs/github/installations/:installationId/providerRepositories/:p
}
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId);
@ -547,8 +563,8 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories')
}
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$page = 1;
@ -639,7 +655,7 @@ Http::post('/v1/vcs/github/installations/:installationId/providerRepositories')
}
if ($installation->getAttribute('personal', false) === true) {
$oauth2 = new OAuth2Github(Http::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), Http::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), "");
$oauth2 = new OAuth2Github(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), "");
$identity = $dbForConsole->findOne('identities', [
Query::equal('provider', ['github']),
@ -681,8 +697,8 @@ Http::post('/v1/vcs/github/installations/:installationId/providerRepositories')
}
} else {
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId);
@ -738,8 +754,8 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
}
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId) ?? '';
@ -787,8 +803,8 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
}
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId) ?? '';
@ -826,7 +842,7 @@ Http::post('/v1/vcs/github/events')
function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) {
$payload = $request->getRawPayload();
$signatureRemote = $request->getHeader('x-hub-signature-256', '');
$signatureLocal = Http::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', '');
$signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', '');
$valid = empty($signatureRemote) ? true : $github->validateWebhookEvent($payload, $signatureRemote, $signatureLocal);
@ -835,8 +851,8 @@ Http::post('/v1/vcs/github/events')
}
$event = $request->getHeader('x-github-event', '');
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$parsedPayload = $github->getEvent($event, $payload);
if ($event == $github::EVENT_PUSH) {
@ -1120,8 +1136,8 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito
$repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository));
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);

View file

@ -8,18 +8,9 @@ use Appwrite\Event\Usage;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Request\Filters\V12 as RequestV12;
use Appwrite\Utopia\Request\Filters\V13 as RequestV13;
use Appwrite\Utopia\Request\Filters\V14 as RequestV14;
use Appwrite\Utopia\Request\Filters\V15 as RequestV15;
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V11 as ResponseV11;
use Appwrite\Utopia\Response\Filters\V12 as ResponseV12;
use Appwrite\Utopia\Response\Filters\V13 as ResponseV13;
use Appwrite\Utopia\Response\Filters\V14 as ResponseV14;
use Appwrite\Utopia\Response\Filters\V15 as ResponseV15;
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
use Appwrite\Utopia\View;
@ -41,6 +32,7 @@ use Utopia\Locale\Locale;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Logger\Logger;
use Utopia\System\System;
Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
@ -60,15 +52,15 @@ function router(Http $utopia, Database $dbForConsole, callable $getProjectDB, Sw
)[0] ?? null;
if ($route === null) {
if ($host === Http::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
}
if (\str_ends_with($host, Http::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) {
if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.');
}
if (Http::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') {
if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') {
if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.');
}
@ -99,7 +91,7 @@ function router(Http $utopia, Database $dbForConsole, callable $getProjectDB, Sw
$type = $route->getAttribute('resourceType');
if ($type === 'function') {
if (Http::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if ($request->getProtocol() !== 'https') {
if ($request->getMethod() !== Request::METHOD_GET) {
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.');
@ -259,7 +251,7 @@ function router(Http $utopia, Database $dbForConsole, callable $getProjectDB, Sw
]);
/** Execute function */
$executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST'));
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
try {
$version = $function->getAttribute('version', 'v2');
$command = $runtime['startCommand'];
@ -388,7 +380,7 @@ Http::init()
* Appwrite Router
*/
$host = $request->getHostname() ?? '';
$mainDomain = Http::getEnv('_APP_DOMAIN', '');
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain) {
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) {
@ -406,32 +398,14 @@ Http::init()
return $response->setStatusCode(404)->send('Not Found');
}
$requestFormat = $request->getHeader('x-appwrite-response-format', Http::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
$requestFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($requestFormat) {
switch ($requestFormat) {
case version_compare($requestFormat, '0.12.0', '<'):
Request::setFilter(new RequestV12());
break;
case version_compare($requestFormat, '0.13.0', '<'):
Request::setFilter(new RequestV13());
break;
case version_compare($requestFormat, '0.14.0', '<'):
Request::setFilter(new RequestV14());
break;
case version_compare($requestFormat, '0.15.3', '<'):
Request::setFilter(new RequestV15());
break;
case version_compare($requestFormat, '1.4.0', '<'):
Request::setFilter(new RequestV16());
break;
case version_compare($requestFormat, '1.5.0', '<'):
Request::setFilter(new RequestV17());
break;
default:
Request::setFilter(null);
if (version_compare($requestFormat, '1.4.0', '<')) {
$request->addFilter(new RequestV16());
}
if (version_compare($requestFormat, '1.5.0', '<')) {
$request->addFilter(new RequestV17());
}
} else {
Request::setFilter(null);
}
$domain = $request->getHostname();
@ -447,7 +421,7 @@ Http::init()
} else {
$auth->disable();
$envDomain = Http::getEnv('_APP_DOMAIN', '');
$envDomain = System::getEnv('_APP_DOMAIN', '');
$mainDomain = null;
if (!empty($envDomain) && $envDomain !== 'localhost') {
$mainDomain = $envDomain;
@ -524,7 +498,7 @@ Http::init()
$isIpAddress = filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false;
$isConsoleProject = $project->getAttribute('$id', '') === 'console';
$isConsoleRootSession = Http::getEnv('_APP_CONSOLE_ROOT_SESSION', 'disabled') === 'enabled';
$isConsoleRootSession = System::getEnv('_APP_CONSOLE_ROOT_SESSION', 'disabled') === 'enabled';
Config::setParam(
'cookieDomain',
@ -540,35 +514,14 @@ Http::init()
/*
* Response format
*/
$responseFormat = $request->getHeader('x-appwrite-response-format', Http::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
$responseFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($responseFormat) {
switch ($responseFormat) {
case version_compare($responseFormat, '0.11.2', '<='):
Response::setFilter(new ResponseV11());
break;
case version_compare($responseFormat, '0.12.4', '<='):
Response::setFilter(new ResponseV12());
break;
case version_compare($responseFormat, '0.13.4', '<='):
Response::setFilter(new ResponseV13());
break;
case version_compare($responseFormat, '0.14.0', '<='):
Response::setFilter(new ResponseV14());
break;
case version_compare($responseFormat, '0.15.3', '<='):
Response::setFilter(new ResponseV15());
break;
case version_compare($responseFormat, '1.4.0', '<'):
Response::setFilter(new ResponseV16());
break;
case version_compare($responseFormat, '1.5.0', '<'):
Response::setFilter(new ResponseV17());
break;
default:
Response::setFilter(null);
if (version_compare($responseFormat, '1.4.0', '<')) {
$response->addFilter(new ResponseV16());
}
if (version_compare($responseFormat, '1.5.0', '<')) {
$response->addFilter(new ResponseV17());
}
} else {
Response::setFilter(null);
}
/*
@ -577,7 +530,7 @@ Http::init()
* As recommended at:
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
if (Http::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if (System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
if ($request->getMethod() !== Request::METHOD_GET) {
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.');
@ -634,7 +587,7 @@ Http::options()
* Appwrite Router
*/
$host = $request->getHostname() ?? '';
$mainDomain = Http::getEnv('_APP_DOMAIN', '');
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain) {
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) {
@ -664,7 +617,7 @@ Http::error()
->inject('log')
->inject('auth')
->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $auth) {
$version = Http::getEnv('_APP_VERSION', 'UNKNOWN');
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
if ($error instanceof AppwriteException) {
@ -709,7 +662,7 @@ Http::error()
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
$log->setAction($action);
$isProduction = Http::getEnv('_APP_ENV', 'development') === 'production';
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);

View file

@ -15,6 +15,7 @@ use Utopia\Http\Http;
use Utopia\Http\Validator\Host;
use Utopia\Http\Validator\Text;
use Utopia\Http\Validator\WhiteList;
use Utopia\System\System;
use Utopia\VCS\Adapter\Git\GitHub;
Http::get('/v1/mock/tests/general/oauth2')
@ -136,7 +137,7 @@ Http::patch('/v1/mock/functions-v2')
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, Response $response, Database $dbForProject) {
$isDevelopment = Http::getEnv('_APP_ENV', 'development') === 'development';
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
if (!$isDevelopment) {
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
@ -165,7 +166,7 @@ Http::get('/v1/mock/github/callback')
->inject('response')
->inject('dbForConsole')
->action(function (string $providerInstallationId, string $projectId, GitHub $github, Document $project, Response $response, Database $dbForConsole) {
$isDevelopment = Http::getEnv('_APP_ENV', 'development') === 'development';
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
if (!$isDevelopment) {
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
@ -179,8 +180,8 @@ Http::get('/v1/mock/github/callback')
}
if (!empty($providerInstallationId)) {
$privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId) ?? '';

View file

@ -11,6 +11,7 @@ use Appwrite\Event\Func;
use Appwrite\Event\Messaging;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Utopia\Queue\Connections;
use Appwrite\Utopia\Request;
@ -26,9 +27,9 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\System\System;
use Utopia\Http\Http;
use Utopia\Http\Validator\WhiteList;
use Utopia\Pools\Group;
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
preg_match_all('/{(.*?)}/', $label, $matches);
@ -320,6 +321,14 @@ Http::init()
$route = $utopia->getRoute();
if (
array_key_exists('rest', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['rest']
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
/*
* Abuse Check
*/
@ -370,7 +379,7 @@ Http::init()
;
}
$enabled = Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
$enabled = System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
if (
$enabled // Abuse is enabled
@ -409,18 +418,17 @@ Http::init()
;
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$timestamp = 60 * 60 * 24 * 30;
$data = $cache->load($key, $timestamp);
if (!empty($data)) {
$data = json_decode($data, true);
$parts = explode('/', $data['resourceType']);
if (!empty($data) && !$cacheLog->isEmpty()) {
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
$type = $parts[0] ?? null;
if ($type === 'bucket') {
@ -441,7 +449,7 @@ Http::init()
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$parts = explode('/', $data['resource']);
$parts = explode('/', $cacheLog->getAttribute('resource'));
$fileId = $parts[1] ?? null;
if ($fileSecurity && !$valid) {
@ -458,8 +466,8 @@ Http::init()
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($data['contentType'])
->send(base64_decode($data['payload']))
->setContentType($cacheLog->getAttribute('mimeType'))
->send($data)
;
} else {
$response->addHeader('X-Appwrite-Cache', 'miss');
@ -659,7 +667,6 @@ Http::shutdown()
if ($useCache) {
$resource = $resourceType = null;
$data = $response->getPayload();
if (!empty($data['payload'])) {
$pattern = $route->getLabel('cache.resource', null);
if (!empty($pattern)) {
@ -671,22 +678,17 @@ Http::shutdown()
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
}
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$data = json_encode([
'resourceType' => $resourceType,
'resource' => $resource,
'contentType' => $response->getContentType(),
'payload' => base64_encode($data['payload']),
]) ;
$signature = md5($data);
$cacheLog = $auth->skip(fn () => $dbForProject->getDocument('cache', $key));
$key = md5($request->getURI() . '*' . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
$signature = md5($data['payload']);
$cacheLog = $auth->skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
$auth->skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'resourceType' => $resourceType,
'mimeType' => $response->getContentType(),
'accessedAt' => $now,
'signature' => $signature,
])));
@ -699,7 +701,7 @@ Http::shutdown()
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$cache->save($key, $data);
$cache->save($key, $data['payload']);
}
}
}
@ -743,7 +745,7 @@ Http::shutdown()
Http::init()
->groups(['usage'])
->action(function () {
if (Http::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
if (System::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
throw new Exception(Exception::GENERAL_USAGE_DISABLED);
}
});

View file

@ -8,6 +8,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Http\Http;
use Utopia\System\System;
Http::init()
->groups(['mfaProtected'])
@ -36,7 +37,7 @@ Http::init()
->inject('geodb')
->inject('auth')
->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, Authorization $auth) {
$denylist = Http::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
if (!empty($denylist && $project->getId() === 'console')) {
$countries = explode(',', $denylist);
$record = $geodb->get($request->getIP()) ?? [];

View file

@ -65,11 +65,6 @@ use Utopia\DSN\DSN;
use Utopia\Http\Http;
use Utopia\Http\Request;
use Utopia\Http\Response;
use Utopia\Http\Validator\Hostname;
use Utopia\Http\Validator\IP;
use Utopia\Http\Validator\Range;
use Utopia\Http\Validator\URL;
use Utopia\Http\Validator\WhiteList;
use Utopia\Locale\Locale;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
@ -86,6 +81,12 @@ use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Http\Validator\Hostname;
use Utopia\Http\Validator\IP;
use Utopia\Http\Validator\Range;
use Utopia\Http\Validator\URL;
use Utopia\Http\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
const APP_NAME = 'Appwrite';
@ -115,8 +116,8 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 331;
const APP_VERSION_STABLE = '1.5.0';
const APP_CACHE_BUSTER = 405;
const APP_VERSION_STABLE = '1.5.4';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@ -248,7 +249,7 @@ const METRIC_NETWORK_OUTBOUND = 'network.outbound';
$register = new Registry();
Http::setMode(Http::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION));
Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION));
if (!Http::isProduction()) {
// Allow specific domains to skip public domain validation in dev environment
@ -261,6 +262,7 @@ if (!Http::isProduction()) {
*/
Config::load('events', __DIR__ . '/config/events.php');
Config::load('auth', __DIR__ . '/config/auth.php');
Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs
Config::load('errors', __DIR__ . '/config/errors.php');
Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php');
Config::load('platforms', __DIR__ . '/config/platforms.php');
@ -520,7 +522,7 @@ Database::addFilter(
Database::addFilter(
'encrypt',
function (mixed $value) {
$key = Http::getEnv('_APP_OPENSSL_KEY_V1');
$key = System::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
@ -537,7 +539,7 @@ Database::addFilter(
return;
}
$value = json_decode($value, true);
$key = Http::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
$key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
@ -723,8 +725,8 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
*/
$register->set('logger', function () {
// Register error logger
$providerName = Http::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = Http::getEnv('_APP_LOGGING_CONFIG', '');
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
if (empty($providerName) || empty($providerConfig)) {
return;
@ -742,61 +744,61 @@ $register->set('pools', function () {
$group = new Group();
$fallbackForDB = 'db_main=' . AppwriteURL::unparse([
'scheme' => Http::getEnv('_APP_DB_ADAPTER', 'mariadb'),
'host' => Http::getEnv('_APP_DB_HOST', 'mariadb'),
'port' => Http::getEnv('_APP_DB_PORT', '3306'),
'user' => Http::getEnv('_APP_DB_USER', ''),
'pass' => Http::getEnv('_APP_DB_PASS', ''),
'path' => Http::getEnv('_APP_DB_SCHEMA', ''),
'scheme' => 'mariadb',
'host' => System::getEnv('_APP_DB_HOST', 'mariadb'),
'port' => System::getEnv('_APP_DB_PORT', '3306'),
'user' => System::getEnv('_APP_DB_USER', ''),
'pass' => System::getEnv('_APP_DB_PASS', ''),
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
]);
$fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([
'scheme' => 'redis',
'host' => Http::getEnv('_APP_REDIS_HOST', 'redis'),
'port' => Http::getEnv('_APP_REDIS_PORT', '6379'),
'user' => Http::getEnv('_APP_REDIS_USER', ''),
'pass' => Http::getEnv('_APP_REDIS_PASS', ''),
'host' => System::getEnv('_APP_REDIS_HOST', 'redis'),
'port' => System::getEnv('_APP_REDIS_PORT', '6379'),
'user' => System::getEnv('_APP_REDIS_USER', ''),
'pass' => System::getEnv('_APP_REDIS_PASS', ''),
]);
$connections = [
'console' => [
'type' => 'database',
'dsns' => Http::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB),
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB),
'multiple' => false,
'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'],
],
'database' => [
'type' => 'database',
'dsns' => Http::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB),
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB),
'multiple' => true,
'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'],
],
'queue' => [
'type' => 'queue',
'dsns' => Http::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis),
'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis),
'multiple' => false,
'schemes' => ['redis'],
],
'pubsub' => [
'type' => 'pubsub',
'dsns' => Http::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis),
'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis),
'multiple' => false,
'schemes' => ['redis'],
],
'cache' => [
'type' => 'cache',
'dsns' => Http::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis),
'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis),
'multiple' => true,
'schemes' => ['redis'],
],
];
$maxConnections = Http::getEnv('_APP_CONNECTIONS_MAX', 151);
$instanceConnections = $maxConnections / Http::getEnv('_APP_POOL_CLIENTS', 14);
$maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151);
$instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14);
$multiprocessing = Http::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled';
$multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled';
if ($multiprocessing) {
$workerCount = swoole_cpu_num() * intval(Http::getEnv('_APP_WORKER_PER_CORE', 6));
$workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
} else {
$workerCount = 1;
}
@ -936,11 +938,11 @@ $register->set('pools', function () {
$register->set('db', function () {
// This is usually for our workers or CLI commands scope
$dbHost = Http::getEnv('_APP_DB_HOST', '');
$dbPort = Http::getEnv('_APP_DB_PORT', '');
$dbUser = Http::getEnv('_APP_DB_USER', '');
$dbPass = Http::getEnv('_APP_DB_PASS', '');
$dbScheme = Http::getEnv('_APP_DB_SCHEMA', '');
$dbHost = System::getEnv('_APP_DB_HOST', '');
$dbPort = System::getEnv('_APP_DB_PORT', '');
$dbUser = System::getEnv('_APP_DB_USER', '');
$dbPass = System::getEnv('_APP_DB_PASS', '');
$dbScheme = System::getEnv('_APP_DB_SCHEMA', '');
return new PDO(
"mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4",
@ -955,21 +957,21 @@ $register->set('smtp', function () {
$mail->isSMTP();
$username = Http::getEnv('_APP_SMTP_USERNAME');
$password = Http::getEnv('_APP_SMTP_PASSWORD');
$username = System::getEnv('_APP_SMTP_USERNAME');
$password = System::getEnv('_APP_SMTP_PASSWORD');
$mail->XMailer = 'Appwrite Mailer';
$mail->Host = Http::getEnv('_APP_SMTP_HOST', 'smtp');
$mail->Port = Http::getEnv('_APP_SMTP_PORT', 25);
$mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp');
$mail->Port = System::getEnv('_APP_SMTP_PORT', 25);
$mail->SMTPAuth = !empty($username) && !empty($password);
$mail->Username = $username;
$mail->Password = $password;
$mail->SMTPSecure = Http::getEnv('_APP_SMTP_SECURE', '');
$mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', '');
$mail->SMTPAutoTLS = false;
$mail->CharSet = 'UTF-8';
$from = \urldecode(Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
$email = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
$email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$mail->setFrom($email, $from);
$mail->addReplyTo($email, $from);
@ -1020,8 +1022,9 @@ foreach ($locales as $locale) {
'method' => 'GET',
'user_agent' => \sprintf(
APP_USERAGENT,
Http::getEnv('_APP_VERSION', 'UNKNOWN'),
Http::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
System::getEnv('_APP_VERSION', 'UNKNOWN'),
System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
'timeout' => 2,
],
@ -1038,7 +1041,7 @@ Http::setResource('hooks', function ($register) {
}, ['register']);
Http::setResource('register', fn () => $register);
Http::setResource('locale', fn () => new Locale(Http::getEnv('_APP_LOCALE', 'en')));
Http::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en')));
Http::setResource('localeCodes', function () {
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
@ -1095,7 +1098,7 @@ Http::setResource('clients', function ($request, $console, $project) {
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
$hostnames = explode(',', Http::getEnv('_APP_CONSOLE_HOSTNAMES', ''));
$hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', ''));
$validator = new Hostname();
foreach ($hostnames as $hostname) {
$hostname = trim($hostname);
@ -1213,7 +1216,7 @@ Http::setResource('user', function (string $mode, Document $project, Document $c
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(Http::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
try {
$payload = $jwt->decode($authJWT);
@ -1300,16 +1303,16 @@ Http::setResource('console', function () {
'legalAddress' => '',
'legalTaxId' => '',
'auths' => [
'invites' => Http::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
'limit' => (Http::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
],
'authWhitelistEmails' => (!empty(Http::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', Http::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(Http::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', Http::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
'oAuthProviders' => [
'githubEnabled' => true,
'githubSecret' => Http::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
'githubAppid' => Http::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
],
]);
}, []);
@ -1426,7 +1429,7 @@ Http::setResource('deviceForBuilds', function ($project) {
function getDevice($root): Device
{
$connection = Http::getEnv('_APP_CONNECTIONS_STORAGE', '');
$connection = System::getEnv('_APP_CONNECTIONS_STORAGE', '');
if (!empty($connection)) {
$acl = 'private';
@ -1463,43 +1466,43 @@ function getDevice($root): Device
return new Local($root);
}
} else {
switch (strtolower(Http::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) {
switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) {
case Storage::DEVICE_LOCAL:
default:
return new Local($root);
case Storage::DEVICE_S3:
$s3AccessKey = Http::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
$s3SecretKey = Http::getEnv('_APP_STORAGE_S3_SECRET', '');
$s3Region = Http::getEnv('_APP_STORAGE_S3_REGION', '');
$s3Bucket = Http::getEnv('_APP_STORAGE_S3_BUCKET', '');
$s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
$s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', '');
$s3Region = System::getEnv('_APP_STORAGE_S3_REGION', '');
$s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', '');
$s3Acl = 'private';
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
case Storage::DEVICE_DO_SPACES:
$doSpacesAccessKey = Http::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
$doSpacesSecretKey = Http::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
$doSpacesRegion = Http::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
$doSpacesBucket = Http::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
$doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
$doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
$doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
$doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
$doSpacesAcl = 'private';
return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
case Storage::DEVICE_BACKBLAZE:
$backblazeAccessKey = Http::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', '');
$backblazeSecretKey = Http::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', '');
$backblazeRegion = Http::getEnv('_APP_STORAGE_BACKBLAZE_REGION', '');
$backblazeBucket = Http::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', '');
$backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', '');
$backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', '');
$backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', '');
$backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', '');
$backblazeAcl = 'private';
return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl);
case Storage::DEVICE_LINODE:
$linodeAccessKey = Http::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', '');
$linodeSecretKey = Http::getEnv('_APP_STORAGE_LINODE_SECRET', '');
$linodeRegion = Http::getEnv('_APP_STORAGE_LINODE_REGION', '');
$linodeBucket = Http::getEnv('_APP_STORAGE_LINODE_BUCKET', '');
$linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', '');
$linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', '');
$linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', '');
$linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', '');
$linodeAcl = 'private';
return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl);
case Storage::DEVICE_WASABI:
$wasabiAccessKey = Http::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', '');
$wasabiSecretKey = Http::getEnv('_APP_STORAGE_WASABI_SECRET', '');
$wasabiRegion = Http::getEnv('_APP_STORAGE_WASABI_REGION', '');
$wasabiBucket = Http::getEnv('_APP_STORAGE_WASABI_BUCKET', '');
$wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', '');
$wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', '');
$wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', '');
$wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', '');
$wasabiAcl = 'private';
return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl);
}

View file

@ -2,6 +2,7 @@
use Appwrite\Auth\Auth;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
@ -27,6 +28,7 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Http\Adapter\FPM\Server as FPMServer;
use Utopia\Http\Http;
use Utopia\Logger\Log;
use Utopia\System\System;
use Utopia\WebSocket\Adapter;
use Utopia\WebSocket\Server;
@ -124,9 +126,9 @@ $stats->create();
$containerId = uniqid();
$statsDocument = null;
$workerNumber = swoole_cpu_num() * intval(Http::getEnv('_APP_WORKER_PER_CORE', 6));
$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
$adapter = new Adapter\Swoole(port: Http::getEnv('PORT', 80));
$adapter = new Adapter\Swoole(port: System::getEnv('PORT', 80));
$adapter
->setPackageMaxLength(64000) // Default maximum Package Size (64kb)
->setWorkerNumber($workerNumber);
@ -137,7 +139,7 @@ $logError = function (Throwable $error, string $action) use ($register) {
$logger = $register->get('logger');
if ($logger && !$error instanceof Exception) {
$version = Http::getEnv('_APP_VERSION', 'UNKNOWN');
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace("realtime");
@ -156,7 +158,7 @@ $logError = function (Throwable $error, string $action) use ($register) {
$log->setAction($action);
$isProduction = Http::getEnv('_APP_ENV', 'development') === 'production';
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
@ -422,10 +424,18 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID');
}
if (
array_key_exists('realtime', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['realtime']
&& !(Auth::isPrivilegedUser($auth->getRoles()) || Auth::isAppUser($auth->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
$dbForProject = getProjectDB($project, $auth);
$console = $http->getResource('console'); /** @var Document $console */
$user = $http->getResource('user'); /** @var Document $user */
$auth = new Authorization();
/*
* Abuse Check
@ -439,7 +449,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$abuse = new Abuse($timeLimit, $auth);
if (Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled' && $abuse->check()) {
if (System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled' && $abuse->check()) {
throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many requests');
}
@ -538,7 +548,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$abuse = new Abuse($timeLimit, $auth);
if ($abuse->check() && Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many messages.');
}

View file

@ -600,13 +600,14 @@ services:
appwrite-worker-usage:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: usage
entrypoint: worker-usage
container_name: appwrite-worker-usage
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
@ -624,6 +625,35 @@ services:
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-worker-usage-dump:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-usage-dump
<<: *x-logging
container_name: appwrite-worker-usage-dump
networks:
- appwrite
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_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-scheduler-functions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>

View file

@ -36,6 +36,7 @@ use Utopia\Queue\Message;
use Utopia\Queue\Server;
use Utopia\Registry\Registry;
use Utopia\Storage\Device\Local;
use Utopia\System\System;
global $register;
@ -119,15 +120,15 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso
}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']);
Server::setResource('abuseRetention', function () {
return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400));
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400));
});
Server::setResource('auditRetention', function () {
return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
});
Server::setResource('executionRetention', function () {
return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
});
Server::setResource('cache', function (Registry $register, Connections $connections) {
@ -252,9 +253,9 @@ if (!empty($workerIndex)) {
}
if (\str_starts_with($workerName, 'databases')) {
$queueName = Http::getEnv('_APP_QUEUE_NAME', 'database_db_main');
$queueName = System::getEnv('_APP_QUEUE_NAME', 'database_db_main');
} else {
$queueName = Http::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName));
$queueName = System::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName));
}
try {
@ -265,7 +266,7 @@ try {
* - _APP_QUEUE_NAME The name of the queue to read for database events
*/
$platform->init(Service::TYPE_WORKER, [
'workersNum' => Http::getEnv('_APP_WORKERS_NUM', 1),
'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1),
'connection' => $pools->get('queue')->pop()->getResource(),
'workerName' => strtolower($workerName) ?? null,
'queueName' => $queueName
@ -300,7 +301,7 @@ $worker
->inject('auth')
->action(function (Throwable $error, ?Logger $logger, Log $log, Connections $connections, Document $project, Authorization $auth) use ($queueName) {
$connections->reclaim();
$version = Http::getEnv('_APP_VERSION', 'UNKNOWN');
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
if ($error instanceof PDOException) {
throw $error;
@ -322,7 +323,7 @@ $worker
$log->addExtra('detailedTrace', $error->getTrace());
$log->addExtra('roles', $auth->getRoles());
$isProduction = Http::getEnv('_APP_ENV', 'development') === 'production';
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);

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