Compare commits

...
Author SHA1 Message Date
ArnabChatterjee20k 6da8c1cb12 updated 2026-05-08 15:21:52 +05:30
ArnabChatterjee20k d303d6f807 Refactor path parameter detection in OpenAPI3 and Swagger2 by utilizing array flipping for improved performance and clarity in matching aliases. 2026-05-08 14:39:26 +05:30
ArnabChatterjee20k 07973dee2d Refactor URL parameter replacement logic in OpenAPI3 and Swagger2 to ensure accurate matching of path parameters by checking for trailing characters. 2026-05-08 14:19:51 +05:30
ArnabChatterjee20k 5cfaa0807d Refactor URL parameter matching in OpenAPI3 and Swagger2 to improve path parameter detection by checking for trailing characters. 2026-05-08 14:08:27 +05:30
ArnabChatterjee20k e181954dd1 removed regex 2026-05-08 14:01:04 +05:30
ArnabChatterjee20k 4b05a6cf8f Refactor URL parameter matching in OpenAPI3 and Swagger2 to use preg_match for improved accuracy with path aliases. 2026-05-08 13:52:25 +05:30
ArnabChatterjee20k 29bbc7299a Enhance URL parameter handling in OpenAPI3 and Swagger2 formats to support aliases for path parameters. 2026-05-08 13:45:46 +05:30
ArnabChatterjee20k 21ba13a076 updated 2026-05-07 18:40:26 +05:30
ArnabChatterjee20k 399f75e8e7 updated 2026-05-07 16:35:56 +05:30
ArnabChatterjee20k 7917506b58 updated oauth 2026-05-07 16:30:23 +05:30
ArnabChatterjee20k 706459e314 updated 2026-05-07 16:01:52 +05:30
ArnabChatterjee20k 7e9d6f33c8 updated methods 2026-05-07 15:56:44 +05:30
Jake BarnbyandGitHub 3b2a01f470 Merge pull request #11910 from appwrite/feat/skip-duplicates
feat(migrations): add onDuplicate option to CSV/JSON/Appwrite imports
2026-05-07 19:21:47 +12:00
premtsd-codeandGitHub e621701cbc Merge branch '1.9.x' into feat/skip-duplicates 2026-05-07 12:00:15 +05:30
Luke B. SilverandGitHub c89bf3084a Merge pull request #12229 from appwrite/chore/builds
Chore/builds
2026-05-06 22:47:57 +01:00
loks0n 9387c480f5 Add Span attributes to build worker for trace observability 2026-05-06 19:56:17 +01:00
premtsd-codeandGitHub 2e9841c9c2 Merge branch '1.9.x' into feat/skip-duplicates 2026-05-06 23:35:25 +05:30
loks0n 8201fea9ef Differentiate executor timeouts for builds, sync, and async executions
Adds Executor\Exception\Timeout (with timeoutSeconds) and translates it at
each call site into BUILD_TIMEOUT, FUNCTION_SYNCHRONOUS_TIMEOUT, or
FUNCTION_ASYNCHRONOUS_TIMEOUT instead of always using the misleading sync
function error. Build timeouts now append to streamed buildLogs rather
than replacing them, and the build worker reports its timeout via Span.
2026-05-06 15:54:11 +01:00
Matej BačoandGitHub bf52b1ba31 Merge pull request #12228 from appwrite/fix-refresh-session
Fix: Refreshing non-oauth sessions
2026-05-06 16:38:01 +02:00
Matej Bačo e834a95213 PR review improvements 2026-05-06 16:21:50 +02:00
Matej Bačo a9dd957a7a Fix test 2026-05-06 15:57:25 +02:00
Matej Bačo d2b551cd12 Fix refreshing nonoauth sessions 2026-05-06 15:50:18 +02:00
Matej BačoandGitHub bc0501aaf2 Merge pull request #12224 from appwrite/fix-oauth-oidc-params
Fix: OAuth OIDC param consistency
2026-05-06 12:25:45 +02:00
Matej Bačo a65c6136f4 Improve code quality 2026-05-06 12:08:02 +02:00
Matej Bačo 78133a3c25 Merge branch '1.9.x' into fix-oauth-oidc-params 2026-05-06 11:46:09 +02:00
Matej Bačo 140cbb633d Fix tests 2026-05-06 11:45:35 +02:00
Matej Bačo 389146c625 oidc backwards compatibiltiy test 2026-05-06 11:32:00 +02:00
Matej BačoandGitHub ec927104fe Merge pull request #12221 from appwrite/fix-variables-api-feature-parity
Feat: Variables API feature parity
2026-05-06 11:27:32 +02:00
Matej Bačo dcef7ef559 OIDC param name improvement 2026-05-06 11:24:53 +02:00
Matej Bačo ff518a055f Merge branch '1.9.x' into fix-oauth-oidc-params 2026-05-06 11:17:42 +02:00
Matej Bačo 00ee9c6b0e Fix benchmark 2026-05-06 11:09:11 +02:00
Prem Palanisamy f9c5f41a1a Merge remote-tracking branch 'origin/1.9.x' into feat/skip-duplicates
# Conflicts:
#	composer.lock
2026-05-06 09:53:13 +01:00
Matej Bačo d27b578888 Fix more tests 2026-05-06 10:34:37 +02:00
Matej Bačo 1ccf058c14 Fix failing tests 2026-05-06 10:19:30 +02:00
Jake BarnbyandGitHub 1273bcd33a Merge pull request #11673 from appwrite/big-int
Big int
2026-05-06 20:17:45 +12:00
Matej BačoandGitHub add2b7b64a Merge pull request #12208 from appwrite/feat-public-proxy-api
Feat: Public Proxy API
2026-05-06 10:15:57 +02:00
ArnabChatterjee20k a0d77ba1b2 Merge remote-tracking branch 'origin/1.9.x' into big-int 2026-05-06 13:22:37 +05:30
Matej BačoandGitHub 63ac3bedca Merge pull request #12212 from appwrite/fix-param-name-consistency
Fix endpoint consistency (oauth get)
2026-05-06 09:42:22 +02:00
Matej Bačo a286b78a0b Fix function tests 2026-05-06 09:41:00 +02:00
Prem Palanisamy e63f9fd6a5 tests: send non-empty parent row data in TwoWayRecreate test
server-ce 1.9.x's tablesdb POST /rows tightened input validation: the
modular Documents/Create.php rejects `data => []` with a 400
"missing data" because TablesDB's Rows/Create.php inherits the strict
default of getSupportForEmptyDocument() = false (only DocumentsDB
overrides it to true). The test was relying on the older permissive
behavior to seed an empty parent row before the relationship cascade
links it.

Add a non-relationship `label` string column on the parents table and
populate it with `data => ['label' => 'p1']` so the POST passes the
empty-data guard. The test's actual assertion target — partner-side
pair-key dedup on DropAndRecreate — is unchanged.

Cascade fixes: testAppwriteMigrationOverwriteAttributeRecreate and
testAppwriteMigrationOverwriteSameSpecRecreate were failing in the
retry pass because TwoWayRecreate's bail at L1616 left source/dest
state uncleaned. Once TwoWayRecreate completes, those tests see a
clean project again.

Caught in CI run 25419479164 / job 74562934987 on the MongoDB
(dedicated) Migrations matrix.
2026-05-06 08:40:17 +01:00
ArnabChatterjee20kandCopilot fc918d8b3c feat: add support for unsigned integer and bigint columns in database tests
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 11:48:47 +05:30
ArnabChatterjee20k 93ce542d31 updated composer 2026-05-06 11:37:05 +05:30
Prem Palanisamy e6a9c68534 Merge remote-tracking branch 'origin/1.9.x' into feat/skip-duplicates
# Conflicts:
#	composer.lock
2026-05-06 07:06:59 +01:00
ArnabChatterjee20kandCopilot 03f7b62ff1 refactor: update database version constraints and simplify bigint handling in collection creation
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 11:24:22 +05:30
Damodar LohaniandGitHub ea4a31c6c6 Merge pull request #12222 from appwrite/CLO-2333-additional-indexes-1.9x
feat(indexes): add 4 missing indexes (supersedes #9629)
2026-05-06 10:50:44 +05:45
Damodar Lohani c9ad685e11 fix(indexes): use empty lengths for mixed-type composite to match existing pattern
greptile flagged missing lengths but the codebase has two conventions:
- all-string composite: [LENGTH_KEY, LENGTH_KEY] (_key_unique, _key_provider_providerUid)
- mixed/non-string composite: [] (e.g. ('enabled', 'type'), ('region', 'accessedAt'),
  ('targetInternalId', 'topicInternalId'), ('period', 'time'), ('metric', 'period', 'time'))

(teamInternalId, confirm) is string+boolean; closest match is
('enabled', 'type') which uses lengths => [] and orders => [].
Aligning to that established pattern instead of inventing
[LENGTH_KEY, 0].
2026-05-06 04:48:53 +00:00
Damodar Lohani 83d56a2f36 fix(indexes): set explicit lengths + orders on new indexes (greptile P1)
All four new indexes left lengths/orders as empty arrays; greptile
flagged the inconsistency vs every existing string-attribute index in
the file (e.g. _key_team uses [LENGTH_KEY], _key_unique uses
[LENGTH_KEY, LENGTH_KEY]).

- memberships._key_team_confirm: [LENGTH_KEY, 0] for (string, boolean)
  + [ORDER_ASC, ORDER_ASC]
- projects._key_teamInternalId: [LENGTH_KEY] + [ORDER_ASC]
- platforms._key_project_id: [LENGTH_KEY] + [ORDER_ASC]
- webhooks._key_project_id: [LENGTH_KEY] + [ORDER_ASC]
2026-05-06 04:44:58 +00:00
Damodar Lohani 94c968e941 feat(indexes): add 4 missing indexes (CLO-2333)
- memberships: _key_team_confirm on (teamInternalId, confirm) for team-membership confirm-state queries
- projects: _key_teamInternalId on teamInternalId for team-scoped project lookups
- platforms: _key_project_id on projectId for user-facing-id lookups
- webhooks: _key_project_id on projectId for user-facing-id lookups

Re-applies the indexes from the stale PR #9629 (1.7.x base, conflicting)
onto a fresh 1.9.x branch. None of these are in 1.9.x today; existing
similar indexes target projectInternalId / teamId rather than the
user-facing projectId / teamInternalId queries this addresses.
2026-05-06 04:35:29 +00:00
Matej BačoandGitHub 8918cc75c3 Merge pull request #12213 from appwrite/feat-list-endpoint-queries-oauth
Feat: queries & total params for list oauth providers
2026-05-05 17:16:09 +02:00
Matej Bačo f90f618bff Fix failing tests 2026-05-05 17:14:05 +02:00
Matej Bačo b382a9c3e5 Fix mistakes after AI 2026-05-05 16:53:24 +02:00
Matej Bačo 3fd79e3045 Merge branch '1.9.x' into fix-param-name-consistency 2026-05-05 16:52:10 +02:00
Matej Bačo 3e58500e13 Upgrade libs 2026-05-05 16:51:57 +02:00
Matej Bačo 1975ab2513 Fix linter 2026-05-05 16:45:15 +02:00
Matej Bačo 15f21daa2b Add tests 2026-05-05 16:45:09 +02:00
Matej Bačo 48fbb59113 pr review fixes 2026-05-05 16:44:47 +02:00
Matej Bačo 9b120e9db7 Remove unwanted changes 2026-05-05 16:29:09 +02:00
Matej Bačo 305e8e1ec7 Add request filter 2026-05-05 16:24:41 +02:00
Matej Bačo 93515fcc1d Finalize variables rework 2026-05-05 16:17:47 +02:00
Matej Bačo fd1a4542e7 Fix variables API feature parity 2026-05-05 16:09:17 +02:00
ArnabChatterjee20k 7ccf2b1a1e updated 2026-05-05 19:28:28 +05:30
ArnabChatterjee20k 57dd7cf952 updated 2026-05-05 19:22:00 +05:30
ArnabChatterjee20k 90e681a906 updated 2026-05-05 19:20:47 +05:30
ArnabChatterjee20k bca876acaf Merge remote-tracking branch 'origin/1.9.x' into big-int 2026-05-05 18:55:11 +05:30
Matej BačoandGitHub 2fc044a7c9 Merge pull request #12218 from appwrite/fix-double-policies-scopes
Chore: Rename policies scope
2026-05-05 13:56:03 +02:00
Matej Bačo fef4cbf3b0 Fix missing new scope 2026-05-05 13:37:51 +02:00
Matej Bačo 32d30dfd9d Fix copy 2026-05-05 13:06:57 +02:00
Matej Bačo b0220292a7 Rename policies to prevent double scope usease 2026-05-05 12:58:06 +02:00
Prem Palanisamy 9ab546d743 composer: pin utopia-php/migration to 1.9.7 (released) 2026-05-05 09:11:18 +01:00
Chirag AggarwalandGitHub 67ab29ffeb Merge pull request #12211 from appwrite/feat/spec-unified-auth-headers 2026-05-05 12:53:26 +05:30
Jake BarnbyandGitHub 1a41a8ea66 Merge pull request #11632 from appwrite/backup-migration-multitype
Add backup policy migration support
2026-05-05 19:20:55 +12:00
Chirag AggarwalandGitHub 538fa94c79 Merge pull request #12216 from appwrite/fix/graphql-php-audit 2026-05-05 11:02:31 +05:30
Chirag Aggarwal 1a410c43d7 fix: update graphql php dependency 2026-05-05 10:54:01 +05:30
Matej Bačo 4d5bb30917 tests for oauth list endpoint ueries and total 2026-05-04 16:05:56 +02:00
Matej Bačo c35d5e348b Support queries in oauth list endpoint 2026-05-04 16:05:46 +02:00
Matej Bačo d2922e7d5d Fix failing tests 2026-05-04 15:49:05 +02:00
Matej Bačo b1c3fc0908 Update tests 2026-05-04 15:40:32 +02:00
Matej Bačo d18f64d526 Fix endpoint consistency, apply request filter alias 2026-05-04 15:22:23 +02:00
Chirag Aggarwal e81ae67745 fix(specs): align console Cookie description with client/server 2026-05-04 16:53:22 +05:30
Chirag Aggarwal 1e9b364f58 feat(specs): include union of auth headers in every platform's securityDefinitions
Each platform spec previously declared only the auth headers referenced by
endpoints in that platform's surface. The unified web SDK now exposes a
single Client class with cross-platform auth factories (fromSession,
fromCookie, fromDevKey, etc.), which means an SDK generator targeting one
platform still needs setter generation for headers that other platforms
expose. The web SDK currently papers over this with a webClientHeaders
augmentation in the SDK generator.

Add the missing securityDefinitions entries directly so every platform
spec carries the union, removing the need for SDK-side augmentation:

- client: + Cookie (for SSR cookie forwarding)
- server: + Cookie, DevKey
- console: + Session, DevKey

Per-endpoint security requirements continue to be filtered per platform
as before; only the securityDefinitions registry expands to declare
schemes that exist API-wide. SDKs that iterate the registry to emit
auth setters now produce the union without per-SDK fallback code.
2026-05-04 16:50:38 +05:30
Matej Bačo d7d0ecb106 Fix failing tests 2026-05-04 12:17:34 +02:00
Matej Bačo 879dc6873e review fixes 2026-05-04 12:07:52 +02:00
Matej Bačo 2263632f7d Fix all proxy create endpoints 2026-05-04 12:06:37 +02:00
Matej Bačo 2231830826 Further proxy API improvements 2026-05-04 11:59:04 +02:00
Matej Bačo cd6f5c64f0 Improve proxy API quality 2026-05-04 11:48:02 +02:00
Matej Bačo d8349544d2 Revert unwanted change 2026-05-04 11:37:26 +02:00
Matej Bačo ec75f101bb SDK quality fix 2026-05-04 11:37:00 +02:00
Matej Bačo 9c9bde2ce6 Introduce project key console tests 2026-05-04 11:36:54 +02:00
ArnabChatterjee20kandGitHub 554fdf5472 Merge pull request #12206 from appwrite/fix-query-syntax-errors
fix: resolve query syntax errors and improve error handling in Reques…
2026-05-04 12:57:39 +05:30
ArnabChatterjee20k 737c85822d multi line comment 2026-05-04 12:46:44 +05:30
ArnabChatterjee20k 1fc3a8803c multiline comment 2026-05-04 12:45:32 +05:30
Damodar LohaniandGitHub 44dd0e8c32 Merge pull request #12207 from appwrite/chore-bump-utopia-audit-2.2.2
chore: bump utopia-php/audit to 2.2.2
2026-05-04 12:58:48 +05:45
ArnabChatterjee20k c32294743a reverted e2e 2026-05-04 12:31:50 +05:30
Damodar Lohani f27172018f chore: bump utopia-php/audit to 2.2.2 2026-05-04 06:56:56 +00:00
ArnabChatterjee20k f5a7cfd2ea fix: resolve query syntax errors and improve error handling in Request class 2026-05-04 12:22:48 +05:30
Luke B. SilverandGitHub 76e6239d32 Merge pull request #12204 from appwrite/chore/bump-image-1.4.1
chore: bump base image to 1.4.1
2026-05-03 20:18:03 +01:00
Matej BačoandGitHub 1da5b549af Merge pull request #12203 from appwrite/fix-missing-scopes-console
Fix: Add deprecated function scopes
2026-05-03 20:21:28 +02:00
loks0nandClaude Opus 4.7 92eceba218 chore: bump base image to 1.4.1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:21:00 +01:00
8f68a59a79 Apply suggestions from code review
Co-authored-by: Matej Bačo <matejbaco2000@gmail.com>
2026-05-03 19:51:56 +02:00
6051b8150c Apply suggestions from code review
Co-authored-by: Matej Bačo <matejbaco2000@gmail.com>
2026-05-03 19:51:10 +02:00
Matej Bačo 4e20e382d2 Add deprecated function scopes 2026-05-03 19:49:13 +02:00
Luke B. SilverandGitHub c96922422f Merge pull request #12201 from appwrite/chore/bump-image-1.3.1
chore: bump base image to 1.3.1
2026-05-03 15:27:45 +01:00
loks0nandClaude Opus 4.7 7f27851dab chore: bump base image to 1.3.1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 15:15:27 +01:00
Chirag AggarwalandGitHub f396064c49 Merge pull request #12198 from appwrite/add-rust-starter-function-template 2026-05-02 16:53:41 +05:30
Chirag Aggarwal 413930a15e Add Rust starter function template runtime 2026-05-02 12:19:07 +05:30
Chirag AggarwalandGitHub f672891c42 Merge pull request #12196 from appwrite/chore/update-runtimes-0.20 2026-05-01 19:33:01 +05:30
Chirag Aggarwal 65f926b4c5 Update appwrite runtimes to 0.20 2026-05-01 18:55:13 +05:30
Prem Palanisamy b79637eef5 Re-add onDuplicate param to modular migration endpoints
1.9.x reorganized app/controllers/api/migrations.php into the
Platform/Modules/Migrations structure but dropped the onDuplicate
param. After the merge, every e2e migration test that passed
'onDuplicate' => 'overwrite' would 400 since the param wasn't in the
allowlist anymore.

Restoring it on the three endpoints that take row-level conflict
behavior: Appwrite/Create, CSV/Imports/Create, JSON/Imports/Create.
Each:
  - Imports OnDuplicate + WhiteList.
  - Adds optional ->param('onDuplicate', OnDuplicate::Fail->value,
    new WhiteList(OnDuplicate::values()), …).
  - Threads $onDuplicate through the action signature.
  - Stores it on the migration document's 'options' attribute so
    Workers/Migrations.php can pick it up via
    OnDuplicate::tryFrom($options['onDuplicate'] ?? '')
    ?? OnDuplicate::Fail.

Worker code already reads options['onDuplicate'] (unchanged) — no
edits needed there.
2026-04-30 17:01:57 +01:00
Prem Palanisamy b1906e5eb9 Merge remote-tracking branch 'origin/1.9.x' into feat/skip-duplicates
# Conflicts:
#	app/controllers/api/migrations.php
#	composer.lock
2026-04-30 16:26:43 +01:00
premtsd-codeandGitHub e14db61e30 Merge branch '1.9.x' into backup-migration-multitype 2026-04-30 16:42:11 +05:30
Matej BačoandGitHub 08ad7d7f71 Merge pull request #12192 from appwrite/fix-oauth-bugs
Fix: OAuth UX with required params
2026-04-30 13:07:52 +02:00
Prem Palanisamy 5ffb7c82e7 Merge 1.9.x into backup-migration-multitype 2026-04-30 11:55:01 +01:00
Matej Bačo 71300383b2 Update tests 2026-04-30 12:48:59 +02:00
Prem Palanisamy d4e32af792 Migrate appwrite to OnDuplicate::Overwrite ('overwrite')
Maintainer review on utopia-php/migration#171 renamed
OnDuplicate::Upsert -> OnDuplicate::Overwrite (value 'upsert' ->
'overwrite') to align with Appwrite terms (skip / overwrite / fail).
Applying the cross-repo ripple here:

- app/controllers/api/migrations.php: 3 endpoint param descriptions
  updated ('upsert' -> 'overwrite' in the help text). The validator
  still uses OnDuplicate::values() so it auto-picks up the new value.
- tests/e2e/Services/Migrations/MigrationsBase.php: all
  'onDuplicate' => 'upsert' -> 'overwrite'; method names
  testAppwriteMigrationUpsert* -> testAppwriteMigrationOverwrite*;
  comments / assertion messages / local var names switched.
- Left untouched: utopia's upsertDocuments operation, transaction
  TransactionState 'upsert' action, Operation validator — those refer
  to the database-level upsert primitive, not the OnDuplicate enum.

composer.lock: utopia-php/migration 7d71505 -> b8ae7bc.
2026-04-30 11:45:33 +01:00
Matej Bačo 8785aa9877 Fix nullable implementation oauth 2026-04-30 12:41:55 +02:00
Matej Bačo 62b7d5558f Mark params nonrequired 2026-04-30 12:36:45 +02:00
Shmuel FogelandGitHub fbbab0f7e1 Merge pull request #12181 from appwrite/set-global-collection
Set global collection
2026-04-30 12:49:00 +03:00
Matej BačoandGitHub 86b9599a57 Merge pull request #12191 from appwrite/oauth-quality-improvements
Fix: oauth order; Fix apple secreting too much
2026-04-30 11:17:51 +02:00
Luke B. SilverandGitHub 526b390c15 Merge pull request #12189 from FerRubioMorales/fix/spanish-session-alert-translations
fix(locale): add Spanish session alert translations
2026-04-30 10:08:02 +01:00
Matej Bačo b73ba68bfb Fix oauth order; Fix apple secreting too much 2026-04-30 10:21:38 +02:00
fogelito d98bd8c972 Remove line 2026-04-30 10:44:21 +03:00
fogelito 81c580bf50 Merge branch '1.9.x' of https://github.com/appwrite/appwrite into set-global-collection 2026-04-30 10:42:09 +03:00
fogelito c0bba74eee set setGlobalCollections logs 2026-04-30 10:36:12 +03:00
frubio 337d47b1d9 fix(locale): add Spanish session alert translations 2026-04-30 09:33:08 +02:00
Chirag AggarwalandGitHub 8ffe48d948 Merge pull request #12179 from appwrite/fix/project-delete-platform-cleanup 2026-04-30 09:43:37 +05:30
Chirag Aggarwal 4050b9ded1 Continue project cleanup after resource failures 2026-04-30 09:28:22 +05:30
fogelito d099167d18 Shared env 2026-04-29 17:09:25 +03:00
fogelito 9d3255f5cd Update lock 2026-04-29 17:08:14 +03:00
fogelito f03cc847f8 Merge branch '1.9.x' of https://github.com/appwrite/appwrite into set-global-collection
# Conflicts:
#	composer.lock
2026-04-29 17:07:32 +03:00
Torsten DittmannandGitHub 6088fd55c8 Merge pull request #12138 from appwrite/feat-out-of-order-chunk-uploads 2026-04-29 18:04:57 +04:00
Matej BačoandGitHub 1aeee8c407 Merge pull request #12178 from appwrite/fix-developer-experience
Fix: Developer experiene with recent chagnes
2026-04-29 15:56:06 +02:00
fogelito 18b9769672 lock file 2026-04-29 16:14:21 +03:00
fogelito 8eed06678b formatting 2026-04-29 15:54:05 +03:00
fogelito 7a9a2899ff setGlobalCollections 2026-04-29 15:41:56 +03:00
Matej Bačo 36486ccc93 Fix tests 2026-04-29 14:41:19 +02:00
Chirag Aggarwal 794d8eac5b Fix project delete platform cleanup ordering 2026-04-29 17:55:06 +05:30
Matej Bačo 32ebfc6cb8 Fix backwards compatibility 2026-04-29 14:14:49 +02:00
Matej Bačo e1b8f5bf98 review improvements 2026-04-29 14:04:54 +02:00
Matej Bačo 4d86e67006 Fix missing scopes for tables 2026-04-29 14:03:44 +02:00
Matej Bačo b3e3b2a330 Fix missing index scopes 2026-04-29 14:00:14 +02:00
Matej Bačo e010bf25d5 Fix formatting 2026-04-29 13:57:16 +02:00
Matej Bačo aaf91f3816 Improve scopes quality 2026-04-29 13:52:13 +02:00
Matej Bačo bae61e8a05 Improve developer experience of keys endpoints 2026-04-29 13:13:13 +02:00
Torsten DittmannandGitHub dfbf45f4cc Merge branch '1.9.x' into feat-out-of-order-chunk-uploads 2026-04-29 15:03:33 +04:00
Chirag AggarwalandGitHub c264db6146 Merge pull request #12177 from appwrite/codex/fix-1-9-trivy-sarif-categories 2026-04-29 16:13:30 +05:30
Chirag Aggarwal cccafeff0c Add nightly SARIF upload guards 2026-04-29 16:02:41 +05:30
Chirag Aggarwal 360d08f087 Preserve CI image for job retries 2026-04-29 16:01:15 +05:30
Chirag AggarwalandGitHub d8e97ae47d Merge pull request #12174 from appwrite/bump-docker-base-1.2.0 2026-04-29 15:59:24 +05:30
Chirag Aggarwal d13e6d75f0 Fix Trivy SARIF categories on nightly scan 2026-04-29 15:58:59 +05:30
Chirag Aggarwal 4447396859 Update base image to 1.2.1 2026-04-29 15:37:59 +05:30
Chirag AggarwalandGitHub 4d1f229ec7 Merge branch '1.9.x' into bump-docker-base-1.2.0 2026-04-29 15:32:32 +05:30
Chirag AggarwalandGitHub 4f733f5984 Merge pull request #12176 from appwrite/ci-ghcr-image-share 2026-04-29 15:32:22 +05:30
Chirag Aggarwal 701f557755 ci: clean up GHCR CI image after pipeline finishes
Every CI run pushes ghcr.io/<repo>/appwrite-dev:<sha> and nothing
removes it. On an active repo with many PRs the GHCR storage grows
without bound. Add a cleanup job that runs after all consumer jobs
complete (always, even if some fail) and deletes the SHA-tagged
package version via the Packages API.

Addresses Greptile feedback on appwrite/appwrite#12176.
2026-04-29 15:23:05 +05:30
Jake BarnbyandGitHub 8ab26aab44 Merge pull request #12171 from appwrite/migration-refractor
Refactor migrations API to module style
2026-04-29 21:44:19 +12:00
Chirag Aggarwal ec3aa2b54f ci: share docker image via GHCR instead of upload-artifact
The build job uploads the appwrite-dev image as an actions artifact
(~hundreds of MB), and 30+ E2E test jobs all pull it concurrently with
actions/download-artifact. GitHub Actions' artifact storage struggles
with that many parallel downloads and intermittently fails with
BlobNotFound or 'Artifact download failed after 5 retries'.

Push the built image to ghcr.io/<repo>/appwrite-dev:<sha> in the build
job and pull from GHCR in each test job. GHCR handles parallel image
fetches without throttling.

Mirrors appwrite-labs/cloud#3906.
2026-04-29 15:09:39 +05:30
Chirag AggarwalandGitHub 2d636ff7ec Merge branch '1.9.x' into bump-docker-base-1.2.0 2026-04-29 15:06:03 +05:30
Matej BačoandGitHub fd42b8fa64 Merge pull request #12175 from appwrite/feat-console-key-scopes-endpoint
Feat: Console key scopes endpoint
2026-04-29 11:17:49 +02:00
Chirag Aggarwal 9d7df34590 fix: clean up php 8.5 runtime deprecations 2026-04-29 14:47:05 +05:30
Matej Bačo e75fc5b859 Add list scopes endpoint for Console 2026-04-29 10:08:31 +02:00
Jake BarnbyandGitHub 57b8305144 Merge pull request #12134 from appwrite/fix-realtime-span-exporter
added a guard to skip double import
2026-04-29 20:02:04 +12:00
Chirag AggarwalandGitHub a429fb5860 Merge branch '1.9.x' into bump-docker-base-1.2.0 2026-04-29 13:30:50 +05:30
Matej BačoandGitHub aca11ed073 Merge pull request #12170 from appwrite/feat-create-dynamic-keys
Feat: create dynamic keys
2026-04-29 09:58:22 +02:00
Chirag Aggarwal 86123c9e93 fix: update PHP extension path for xdebug cleanup in production 2026-04-29 13:27:04 +05:30
Chirag Aggarwal a58ea1123b chore: bump docker-base to 1.2.0 2026-04-29 13:21:17 +05:30
Matej Bačo 05f2d2b9cf Fix tests 2026-04-28 19:29:37 +02:00
Matej Bačo c1f61b22aa Merge branch '1.9.x' into feat-create-dynamic-keys 2026-04-28 17:18:36 +02:00
Matej Bačo 980762fc3e Rename from dynamic key to ephemeral key (api keys) 2026-04-28 17:18:06 +02:00
Matej Bačo c96836b1c0 Improve code quality of folder decoding project ID 2026-04-28 17:10:58 +02:00
Matej Bačo 15917ac7ba Fix failing tests 2026-04-28 17:05:30 +02:00
premtsd-codeandGitHub cd851bff24 Merge branch '1.9.x' into migration-refractor 2026-04-28 20:32:54 +05:30
Prem Palanisamy 3f5dcc81fd Refactor migrations API to module style 2026-04-28 15:57:41 +01:00
Matej Bačo f5a732d231 Add dynami key integration test 2026-04-28 16:47:39 +02:00
Matej Bačo 72dfd8a7bc Add E2E tests for dynamic keys 2026-04-28 16:45:00 +02:00
Matej Bačo 11f80fc2ed Solve key projectId backwards compatibility 2026-04-28 16:35:40 +02:00
Matej Bačo ccb0ddd578 Bug&test fixing 2026-04-28 16:18:36 +02:00
Matej Bačo b2ce95a0cd Dynamic key backwards compatibility 2026-04-28 16:14:10 +02:00
Matej Bačo ed9b47f6ce Migrate project jwt to dynamic api key 2026-04-28 15:57:37 +02:00
Matej Bačo 8f176166c9 Re-introduce project JWT endpoint 2026-04-28 15:31:10 +02:00
Torsten Dittmann a0ef145b92 Merge branch '1.9.x' of https://github.com/appwrite/appwrite into feat-out-of-order-chunk-uploads 2026-04-28 17:10:56 +04:00
Torsten Dittmann 9e1f8af103 fix: persist sourceChunksTotal/Uploaded in finalization createDocument paths
Greptile review: Functions and Sites finalization branches reached via
single-chunk uploads or out-of-order last-chunk assembly omitted
sourceChunksTotal and sourceChunksUploaded in createDocument. This caused
the retry guard to evaluate 0 === 1 on retry, missing and queuing duplicate
builds.
2026-04-28 13:44:41 +04:00
Torsten DittmannandGitHub b055ff1066 Merge branch '1.9.x' into feat-out-of-order-chunk-uploads 2026-04-28 13:19:08 +04:00
Prem Palanisamy 8f32d01686 composer: bump utopia-php/migration to 7d71505 (createIndex pre-check before count/validator) 2026-04-28 08:10:39 +01:00
Prem Palanisamy e1209614c5 composer: bump utopia-php/migration to d37efed (track partner key for orphan cleanup) 2026-04-28 07:56:14 +01:00
Prem Palanisamy 40aa8cb1bd composer: bump utopia-php/migration to 4c3965b (tighten dropAttributeForRecreate) 2026-04-28 07:11:23 +01:00
Prem Palanisamy 4ae4531355 composer: bump utopia-php/migration to aa1e7c7 (drop+recreate fixes) 2026-04-28 07:06:25 +01:00
ArnabChatterjee20k f71a2dfddc changed the condition to app edition for the loading of the span 2026-04-28 11:07:16 +05:30
Prem Palanisamy 443f5cfb0e tests: switch attribute-recreate test to non-SDK change
After dropping createdAt from resolveSchemaAction, source-side recreate
no longer routes through DropAndRecreate via the outer decision. The
inner fallthrough still drops+recreates when the spec diff is a non-SDK
change, so this test now toggles 'array' (a non-SDK field) on recreate
to actually exercise the drop+recreate path it pins.

Also clarifies the two-way recreate test's docblock — with createdAt
gone and identical spec on recreate, it exercises spec-match + pair-key
dedup (both tolerate paths) rather than parent-side drop. End-state
assertions unchanged.
2026-04-28 05:55:38 +01:00
Prem Palanisamy 5a928f2c0f composer: bump utopia-php/migration to 6ec2c45 (drop createdAt) 2026-04-28 05:43:43 +01:00
Prem Palanisamy f5730e8eed composer: bump utopia-php/migration to 36a1acf (spec-match order fix) 2026-04-28 05:16:46 +01:00
Prem Palanisamy b6afef6efc tests: trim verbose multi-paragraph docblocks to one-liners 2026-04-28 04:44:19 +01:00
Prem Palanisamy 3fba7afd2e tests: pin spec-match guard + bump migration to c8d1789
testAppwriteMigrationUpsertSameSpecRecreateTolerates exercises the new
spec-match guard added in utopia-php/migration c8d1789. Source drops +
recreates a column with the EXACT same spec as before; createdAt
advances but specs match → action is forced to Tolerate. Asserts dest
column's $createdAt stays at first-migration value (proving Tolerate,
not DropAndRecreate). Row pass under Upsert still propagates source's
new row value.

Companion to testAppwriteMigrationUpsertAttributeRecreateDropsAndRecreates
which exercises the spec-DIFFERS path: same precondition (drop + recreate),
different outcome (DropAndRecreate vs Tolerate) gated on spec equality.

composer.lock: utopia-php/migration 24fd23b -> c8d1789 (spec-match guard).
2026-04-28 04:16:51 +01:00
Prem Palanisamy ac46ff802a composer: bump utopia-php/migration to 24fd23b (SDK-boundary lock-in test) 2026-04-28 03:45:25 +01:00
Prem Palanisamy 8cccc79935 composer: bump utopia-php/migration to 47933c1 (inline TwoWayPartner) 2026-04-28 03:31:25 +01:00
Prem Palanisamy b03c901fa7 tests: cover one-way DropAndRecreate + attribute-recreate scenarios
Two coverage gaps closed:

- testAppwriteMigrationUpsertOneWayRelationshipDropAndRecreate exercises
  the path that updateRelationshipInPlace gates off: one-way + onDelete
  change → returns false → falls through to DropAndRecreate via
  deleteRelationship. Coverage was lost when
  testAppwriteMigrationUpsertUpdatesRelationshipOnDeleteInPlace was
  converted to two-way to actually hit the in-place path.

- testAppwriteMigrationUpsertAttributeRecreateDropsAndRecreates pins
  the createdAt-different leaf path: source drops + recreates the
  attribute (createdAt advances), re-migration must DropAndRecreate
  on dest and re-flow the row data through the row pass. Companion to
  testAppwriteMigrationUpsertUpdatesAttributeInPlace which covers the
  same-createdAt + newer-updatedAt path.

Migration package already at 09c1b21 (the maintainability commit) from
the previous lock bump — no further composer.lock change needed.
2026-04-27 17:40:23 +01:00
Prem Palanisamy 7b32fd0196 composer: bump utopia-php/migration to 09c1b21 (maintainability pass) 2026-04-27 17:32:14 +01:00
Torsten Dittmann 2f2da98cca fix: adjust out-of-order test expectations and chunk sizes
- Functions/Sites: lower minimum chunk requirement from 3 to 2
- Sites: use random_bytes instead of str_repeat for non-compressible test data
- Remove assertions on sourceChunksTotal/Uploaded from response body (not in response model)
2026-04-27 17:46:20 +04:00
Prem Palanisamy c80c747e48 tests: pin two-way recreate partner-side dedup + bump migration
testAppwriteMigrationUpsertTwoWayRecreateSkipsPartnerSide exercises
the DropAndRecreate path on a two-way relationship that the partner-
side pair-key dedup guards. Source recreates the relationship between
runs, forcing parent-side createdAt diff. Test asserts the migration
completes cleanly and partner-table rows survive — without dedup, the
partner pass re-fires DropAndRecreate and destroys those rows.

composer.lock: utopia-php/migration c76de9a -> c13e77d (partner-side
pair-key dedup restored).
2026-04-27 14:45:39 +01:00
Torsten Dittmann 54997638e8 fix: persist sourceChunksUploaded on finalization and avoid variable shadowing
- Functions/Sites: include sourceChunksUploaded in updateDocument when finalizing existing deployments, fixing the retry guard
- Functions test: rename loop variable to avoid shadowing setup result
2026-04-27 17:24:51 +04:00
Torsten Dittmann 49d2db65e6 feat: support out-of-order chunked uploads
- Add APP_LIMIT_UPLOAD_CHUNK_SIZE constant (5MB) matching official SDKs
- Replace dynamic chunk calculation with fixed 5MB chunk math in all upload endpoints
- Remove -1 last-chunk sentinel that broke when last chunk arrived first
- Fix duplicate-retry guards: return existing resource instead of erroring for chunked uploads
- Add out-of-order e2e tests for Storage, Functions, and Sites
- Upgrade utopia-php/storage to 2.0.0 for device-level out-of-order assembly support
2026-04-27 17:15:00 +04:00
ArnabChatterjee20k 1fdcca9592 added a guard to skip double import 2026-04-27 18:33:47 +05:30
Prem Palanisamy dfde1be035 tests: cover two-way relationship onDelete in-place update + bump migration
The previous version of this test created a one-way relationship,
which falls through to DropAndRecreate (one-way + onDelete change is
gated off in updateRelationshipInPlace because utopia's
updateRelationship partner-cascade throws on one-way). It never
exercised the in-place path it was named for.

Converted to two-way (parents.kids ↔ children.parent), and asserted
both parent- and partner-side onDelete on dest. Partner-side
assertion is the regression guard for the partner-meta refresh that
was missing from updateRelationshipInPlace.

composer.lock: utopia-php/migration a36d95f -> c76de9a (partner-side
onDelete sync fix).
2026-04-27 14:03:02 +01:00
Prem Palanisamy 9be0ed8af0 Merge remote-tracking branch 'origin/feat/skip-duplicates' into feat/skip-duplicates 2026-04-27 12:35:35 +01:00
Prem Palanisamy 5f8a32798d tests: SDK-aligned UpdateInPlace coverage + bump migration to a36d95f
Three new e2e tests in MigrationsBase covering the schema reconciliation
paths added in utopia-php/migration:

- testAppwriteMigrationUpsertUpdatesAttributeInPlace: PATCH source
  required/default (SDK-reachable), assert dest reflects change and the
  pre-existing row's column data is preserved (drop+recreate would have
  wiped it).
- testAppwriteMigrationSkipPreservesAttributeDrift: leaf-level analog
  of the existing container-drift Skip test — guards Skip from ever
  consulting timestamps.
- testAppwriteMigrationUpsertUpdatesRelationshipOnDeleteInPlace: PATCH
  source onDelete cascade->restrict (SDK-reachable), assert dest
  reflects change and structural fields (relationType, twoWay) untouched.

composer.lock: utopia-php/migration 6e6f825 -> a36d95f (mechanical
helpers replacement, parseTimestamp dedup, match dispatch, comment trim).
2026-04-27 12:32:37 +01:00
premtsd-codeandGitHub 557fef2c7b Merge branch '1.9.x' into feat/skip-duplicates 2026-04-27 15:12:17 +05:30
Prem Palanisamy ece468d1f7 Merge remote-tracking branch 'origin/1.9.x' into feat/skip-duplicates 2026-04-27 08:11:56 +01:00
Prem Palanisamy dc9f48c948 composer: bump utopia-php/migration to 6e6f825 (relationships UpdateInPlace) 2026-04-24 15:09:42 +01:00
Prem Palanisamy 478a2a6e86 tests: Upsert drops orphan columns / Skip preserves them
testAppwriteMigrationUpsertDropsOrphanColumn: adds a column directly on
destination (simulating post-rename orphan or dest-only drift), runs
Upsert, asserts the orphan is dropped and source-declared column
survives. Covers the per-table orphan cleanup fired inside
createRecord before rows land.

testAppwriteMigrationSkipKeepsOrphanColumn: same setup, Skip mode.
Asserts the orphan survives, proving the cleanup is correctly gated
to Upsert only.
2026-04-24 07:30:29 +01:00
Prem Palanisamy 8292f7493d composer: bump utopia-php/migration to c2b8715 (rename orphan tracker to 'table' vocabulary) 2026-04-24 07:25:37 +01:00
Prem Palanisamy e728265d21 composer: bump utopia-php/migration to 36fdf26 (Upsert orphan cleanup + nullable timestamps) 2026-04-24 05:57:32 +01:00
Prem Palanisamy 84b6dfa9d5 composer: bump utopia-php/migration to bb21912 (SKIPPED status on tolerate) 2026-04-23 16:39:30 +01:00
Prem Palanisamy 0a26608155 Merge branch 'feat/skip-duplicates' of https://github.com/appwrite/appwrite into feat/skip-duplicates 2026-04-23 07:15:36 +01:00
Prem Palanisamy 18694e0a28 TEMP: run Migrations across all adapters/modes on this branch 2026-04-23 07:10:23 +01:00
premtsd-codeandGitHub 07307c6131 Merge branch '1.9.x' into feat/skip-duplicates 2026-04-23 11:22:05 +05:30
Prem Palanisamy 0134832fb6 tests: cover Upsert UpdateInPlace for database/table + Skip preserves dest drift
Two new E2E tests exercising the schema-tolerance UpdateInPlace path
added in utopia-php/migration's DestinationAppwrite.

testAppwriteMigrationUpsertUpdatesContainerMetadata (positive):
- Fresh migration copies source database + table + column + row to dest.
- Mutates source database name (PUT /databases/:id) and table
  name/permissions/rowSecurity/enabled (PUT /tablesdb/:db/tables/:id).
- One-second sleep before mutation ensures source's $updatedAt is
  strictly greater than dest's at second granularity (strtotime
  comparison).
- Upsert re-migration asserts:
  - 'completed' status.
  - dest database name matches source's new name.
  - dest table name / enabled / rowSecurity match source's new values.
  - child row's 'name' attribute is untouched — UpdateInPlace only
    rewrites container metadata, not rows.

testAppwriteMigrationSkipPreservesContainerDrift (negative):
- Fresh migration, then mutate BOTH dest (simulating ops tightening
  permissions post-migration) and source (divergence).
- Skip re-migration asserts dest kept its tightened values — Skip's
  strict "don't touch" contract protects dev→prod cutover workflows
  from accidentally wiping ops-side drift on schema re-sync.

Both tests use performMigrationSync for strict 'completed' assertions.
Runtime ~18s combined. Existing testAppwriteMigrationRowsOnDuplicate
and testAppwriteMigrationReRunIsIdempotent regression-tested locally.
2026-04-23 06:22:26 +01:00
Prem Palanisamy 44b7b26adf composer: bump utopia-php/migration to e08d531
Picks up the UpdateInPlace branch — database/table metadata drift is
now reconciled on Upsert-newer (renames, enable toggles, table
permissions / documentSecurity) via updateDocument, without touching
child rows.
2026-04-23 06:06:30 +01:00
Prem Palanisamy c1506643e9 composer: bump utopia-php/migration to cfba224
Picks up the PR #171 refactor + unit tests:
- resolveSchemaAction decision point consolidation
- deleteAttributeCompletely primitive (two-way cleanup in one place)
- Hardened sourceIsNewer against MySQL zero-date sentinel
- 14 unit tests locking the decision matrix
2026-04-23 00:12:09 +01:00
Prem Palanisamy 2ad95e5113 ci: restrict to Migrations service for fast feedback loop (TEMP)
Branch is iterating on utopia-php/migration's re-migration tolerance.
Other test matrices (unit, general, abuse, screenshots, benchmark, and
every other e2e service) add ~30+ minutes to CI without exercising code
this PR touches. Restrict the matrix to the Migrations service and skip
the unrelated test jobs until the migration work is ready to merge.

All jobs marked with 'TEMP:' comments + 'if: false' — revert to the
full matrix before merging to main.

Static analysis (lint, phpstan, composer audit, specs, locale, security)
still runs on every PR push.
2026-04-22 16:46:33 +01:00
Prem Palanisamy 715bd40b4a Tighten A→A re-migration tests against utopia-php/migration schema tolerance
utopia-php/migration's DestinationAppwrite now handles schema tolerance on
re-migration (PR #171 on feat/skip-duplicates): it pre-checks destination
`_metadata` for each database / table / column / index and tolerates in
Skip/Upsert mode. Re-runs no longer produce schema-level errors, so the
E2E tests can drop the status-tolerant workaround and assert strict
'completed' outcomes.

Changes:

- composer.json: pin utopia-php/migration to dev-feat/skip-duplicates (aliased
  to 1.9.99 for stability resolution). Will be replaced with a fixed 1.10.0
  tag once the migration PR lands.

- testAppwriteMigrationRowsOnDuplicate: replace the tolerant
  runMigrationAssertingRowSuccess helper with performMigrationSync on the
  Skip and Upsert re-runs. Asserts 'completed' status on every run,
  destination row content matches the expected value per mode (Mutated
  preserved on Skip, Original restored on Upsert). Helper method removed.

- testAppwriteMigrationReRunIsIdempotent (new): seeds two rows on source,
  runs the migration three times back-to-back (fresh, Skip re-run, Upsert
  re-run) against unchanged source data, asserts strict 'completed' on every
  run and row content is stable across all three. Exercises the
  schema-tolerance path end-to-end: every database/table/column on
  destination already exists with a matching spec, so DestinationAppwrite's
  pre-check returns Tolerate for every resource.
2026-04-22 16:38:32 +01:00
Prem Palanisamy 19f02a5129 composer: sync lock content-hash after merge from 1.9.x 2026-04-20 16:44:36 +01:00
Prem Palanisamy d0603c4d28 tests: add Appwrite->Appwrite row onDuplicate test with tolerant poller 2026-04-20 16:38:59 +01:00
premtsd-codeandGitHub 187d0c0cf4 Merge branch '1.9.x' into feat/skip-duplicates 2026-04-20 19:41:01 +05:30
Prem Palanisamy fc6bd7232e tests: add Appwrite->Appwrite row migration onDuplicate test 2026-04-20 14:50:57 +01:00
Prem Palanisamy e878b0b403 tests: cover onDuplicate on JSON import endpoint + column readiness wait 2026-04-20 14:43:45 +01:00
Prem Palanisamy f3c2502a8c composer: widen database/migration pins to minor-version wildcards 2026-04-20 13:57:44 +01:00
Prem Palanisamy 18f9dfb64e Bump utopia-php/migration to 1.9.2, use OnDuplicate enum 2026-04-20 13:16:45 +01:00
Prem Palanisamy 8fa28257be Migrations: replace overwrite/skip with onDuplicate enum string, bump database to 5.3.22 2026-04-20 10:28:05 +01:00
ArnabChatterjee20k e7072574ad updated tests 2026-04-16 14:13:01 +05:30
ArnabChatterjee20k d84f71e728 updated 2026-04-16 14:10:20 +05:30
ArnabChatterjee20k 5f41f7fb86 Merge remote-tracking branch 'origin/1.9.x' into big-int 2026-04-16 14:07:49 +05:30
Prem Palanisamy c5fe71684a Add E2E tests for CSV import overwrite/skip flags
Three new test methods in MigrationsBase, following the existing
testCreateCSVImport setup pattern:

- testCreateCSVImportSkipDuplicates
  Seeds documents.csv, mutates one row, re-imports with skip=true.
  Asserts the mutated row keeps its mutated value (not overwritten
  by the CSV's original value) and the row count stays at 100.

- testCreateCSVImportOverwrite
  Seeds documents.csv, mutates one row, re-imports with overwrite=true.
  Asserts the mutated row is restored to the CSV's original value
  (proving upsertDocuments actually replaced the row) and the row
  count stays at 100.

- testCreateCSVImportDefaultFailsOnDuplicate
  Regression guard: re-imports documents.csv with no flags. Asserts
  the migration goes to status=failed with errors populated, proving
  the default duplicate-throws behavior is preserved.

All three share a prepareCsvImportFixture() helper that sets up
database + table (name, age columns) + bucket + documents.csv
upload. Returns the known first-row id + original name/age so tests
can mutate and assert on a predictable row.

Reuses the existing documents.csv fixture (100 rows with \$id as the
first column). No new fixture files needed.
2026-04-15 14:23:02 +01:00
Prem Palanisamy d44820e00c Add overwrite and skip options to migration imports
Exposes two new optional boolean params on the three migration
creation endpoints so CSV / JSON / appwrite-to-appwrite imports can
choose how to handle rows whose IDs already exist at the destination.

Endpoints updated (app/controllers/api/migrations.php):
- POST /v1/migrations/appwrite
- POST /v1/migrations/csv/imports
- POST /v1/migrations/json/imports

Parameter semantics:
- overwrite=true  -> destination uses upsertDocuments instead of
                     createDocuments; existing rows are replaced
                     with imported values
- skip=true       -> destination wraps createDocuments in
                     skipDuplicates; existing rows are preserved
                     unchanged, duplicate-id rows silently no-op
- both false      -> default; fails fast on DuplicateException
                     (original behavior, unchanged)
- both true       -> overwrite wins (upsert subsumes skip)

Both params are stored in the migration Document's options array
(matches the existing pattern for destination behavior config like
path, size, delimiter, bucketId, etc.) and read back in the worker's
processDestination() to instantiate DestinationAppwrite with the
new constructor params.

Feature-branch note: depends on utopia-php/migration#feat/skip-duplicates
(DestinationAppwrite constructor params) which in turn depends on
utopia-php/database#852 (skipDuplicates scope guard). composer.json is
temporarily pinned to dev-feat/skip-duplicates and
dev-csv-import-upsert-v2 respectively; both must be reset to proper
release versions once the upstream PRs merge.
2026-04-15 14:15:08 +01:00
ArnabChatterjee20k b3c6e0e67f removed size from the conditions 2026-04-09 17:10:47 +05:30
ArnabChatterjee20k 5c0c0b44c0 Merge remote-tracking branch 'origin/big-int' into big-int 2026-04-09 17:08:09 +05:30
ArnabChatterjee20k 4f599fc75c updated 2026-04-09 17:07:47 +05:30
ArnabChatterjee20kandGitHub 928aa9c909 Merge branch '1.9.x' into big-int 2026-04-09 14:43:51 +05:30
ArnabChatterjee20k 0ea8379065 added the changes 2026-04-09 14:11:21 +05:30
ArnabChatterjee20k 0c734f8f12 updated 2026-04-09 12:02:13 +05:30
ArnabChatterjee20k 229ab28427 Merge remote-tracking branch 'origin/1.9.x' into big-int 2026-04-09 11:15:59 +05:30
ArnabChatterjee20k 45dc259df3 updated validator 2026-04-09 11:14:28 +05:30
ArnabChatterjee20k 71b28eae5a updated db 2026-04-01 15:55:26 +05:30
ArnabChatterjee20k 9cb2ea1135 Merge remote-tracking branch 'origin/bump-database-version-bkp' into big-int 2026-04-01 15:54:56 +05:30
premtsd-codeandGitHub d38c921781 Merge branch '1.9.x' into bump-database-version 2026-04-01 05:23:37 +01:00
ArnabChatterjee20k 3c20ea47f5 Merge remote-tracking branch 'origin/1.9.x' into big-int 2026-04-01 09:51:07 +05:30
Prem Palanisamy 32728ae4a0 Merge remote-tracking branch 'origin/1.9.x' into bump-database-version 2026-04-01 02:16:20 +02:00
ArnabChatterjee20k ad7f83db11 updated actions 2026-03-30 20:32:03 +05:30
ArnabChatterjee20k c4b3956bef emtpy 2026-03-30 20:26:23 +05:30
ArnabChatterjee20k 5fa4551400 Refactor bigint handling in validation and action classes to unify range validation logic 2026-03-30 20:10:13 +05:30
ArnabChatterjee20k 2fb54e0846 Add BigInt support with dedicated bigintRange format and update related logic 2026-03-30 18:59:24 +05:30
ArnabChatterjee20k dfbf22cf3c updated format 2026-03-30 18:36:19 +05:30
ArnabChatterjee20k a5b2021d1e empty 2026-03-30 18:17:14 +05:30
ArnabChatterjee20kandGitHub fb28b31906 Merge branch '1.9.x' into big-int 2026-03-30 18:02:29 +05:30
ArnabChatterjee20k 737bfc599a fixed analyze 2026-03-30 18:00:43 +05:30
ArnabChatterjee20k 819863559e Enhance BigInt support across database attributes and validation, updating types and tests for consistency. 2026-03-30 17:45:25 +05:30
ArnabChatterjee20k f6cdcfb823 Merge remote-tracking branch 'origin/big-int' into big-int 2026-03-30 17:39:25 +05:30
ArnabChatterjee20k e5d818ba44 updated 2026-03-30 17:38:24 +05:30
ArnabChatterjee20kGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
19487cf56b Update src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/BigInt/Create.php
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-30 17:34:36 +05:30
ArnabChatterjee20k 5741041f33 updated tests 2026-03-30 17:32:49 +05:30
ArnabChatterjee20k 89eb5c2254 Refactor response model attribute and column types to prioritize BigInt over Integer for more accurate type matching in AttributeList, Collection, ColumnList, and Table classes. 2026-03-30 17:14:38 +05:30
ArnabChatterjee20k 77d195f0c3 updated response models 2026-03-30 16:17:44 +05:30
ArnabChatterjee20k a9ed74c6a8 added tests to migration 2026-03-30 16:11:42 +05:30
ArnabChatterjee20k 01db1efbc4 added tests 2026-03-30 16:08:21 +05:30
ArnabChatterjee20k ffa3f741ed added bigint columns 2026-03-30 15:53:06 +05:30
ArnabChatterjee20k 03c73ac0aa added big int attributes 2026-03-30 15:50:59 +05:30
premtsd-codeandGitHub 28882a4414 Merge branch '1.9.x' into backup-migration-multitype 2026-03-25 06:57:37 +00:00
Prem Palanisamy d993214810 Revert "Add policies.read/write scopes to test API key"
This reverts commit 2838642bae.
2026-03-24 17:22:31 +00:00
Prem Palanisamy 2838642bae Add policies.read/write scopes to test API key 2026-03-24 17:09:27 +00:00
Prem Palanisamy bd03ec7a50 Update composer.lock for migration feature branch 2026-03-24 13:36:42 +00:00
Prem Palanisamy 6128a049f2 Point migration dep to feature branch 2026-03-24 13:33:36 +00:00
premtsd-codeandGitHub 9ba6bfba62 Merge branch '1.9.x' into backup-migration-multitype 2026-03-24 13:13:23 +00:00
premtsd-codeandGitHub 6f73602dd7 Merge branch '1.9.x' into backup-migration-multitype 2026-03-24 13:01:29 +00:00
Prem Palanisamy b1e97b4539 Merge 1.9.x and add new API key scopes 2026-03-24 12:14:50 +00:00
Prem Palanisamy 065a0fddae Add backup policy migration support 2026-03-12 13:41:00 +00:00
235 changed files with 10410 additions and 3430 deletions
@@ -0,0 +1,174 @@
# Parallel Chunk Upload Support for utopia-php/storage
## Context
The Appwrite API now supports out-of-order chunked uploads (chunks can arrive in any sequence). The next step is **parallel uploads** — multiple chunks uploaded simultaneously via separate HTTP requests. The SDK guarantees the first chunk is sent before any parallel chunks, so the document creation race is handled at the API layer. However, the storage device layer has a race condition that must be fixed.
## Problem: `Local::joinChunks()` Race
When two requests upload the final missing chunks in parallel, both can observe `countChunks() == $chunks` and call `joinChunks()` simultaneously.
### Current behavior (loser throws)
```php
// Local::joinChunks()
$dest = \fopen($tmpAssemble, 'wb');
// ... stream all parts into $tmpAssemble ...
if (! \rename($tmpAssemble, $path)) {
\unlink($tmpAssemble);
throw new Exception('Failed to finalize assembled file '.$path);
}
```
The winner succeeds with `rename()`. The loser gets `false` from `rename()` (file already exists at `$path`) and throws a 500-error exception. The client that lost the race receives an error even though the file is fully assembled.
### Required behavior
If `$path` already exists, another request already assembled the file. The loser should **silently succeed** — the file is complete, nothing more to do.
## Proposed Changes
### 1. `Local::joinChunks()` — Handle assembly race
Before opening `$tmpAssemble`, check if the final file already exists. If it does, skip assembly entirely.
```php
private function joinChunks(string $path, int $chunks): void
{
// Race winner already assembled the file
if (\file_exists($path)) {
return;
}
$tmp = \dirname($path).DIRECTORY_SEPARATOR.'tmp_'.asename($path);
$tmpAssemble = \dirname($path).DIRECTORY_SEPARATOR.'tmp_assemble_'.asename($path);
// ... rest of assembly logic ...
if (! \rename($tmpAssemble, $path)) {
// Another request may have won the race between fclose and rename
if (\file_exists($path)) {
\unlink($tmpAssemble);
return;
}
\unlink($tmpAssemble);
throw new Exception('Failed to finalize assembled file '.$path);
}
// ... cleanup ...
}
```
### 2. `Local::countChunks()` — Reliability under concurrent writes
`countChunks()` uses `glob()` on the temp directory. Under heavy parallel load, `glob()` might miss files or return inconsistent counts. The current implementation is already fairly robust (it validates `.part.\d+` suffix), but we should document that the return value is a best-effort snapshot.
No code change needed here unless tests reveal issues.
### 3. Tests — Concurrent chunk uploads
Add a test that simulates two parallel requests completing a multi-chunk upload:
```php
public function testParallelChunkUpload(): void
{
$storage = $this->makeJoinTestStorage();
$dest = $storage->getRoot().DIRECTORY_SEPARATOR.'parallel.dat';
// Upload chunk 1 (creates temp directory)
$storage->uploadData('AAAA', $dest, 'application/octet-stream', 1, 2);
// Simulate two parallel requests uploading the last chunk
// In a real test, use pcntl_fork() or pthreads for true concurrency
// For the test suite, sequential calls are sufficient if we verify
// the second call doesn't throw after the first completed assembly
$storage->uploadData('BBBB', $dest, 'application/octet-stream', 2, 2);
// Verify file exists and is correct
$this->assertTrue(\file_exists($dest));
$this->assertSame('AAAABBBB', \file_get_contents($dest));
// Verify second assembly attempt doesn't throw
// (This simulates the race where another request already assembled)
try {
$storage->uploadData('BBBB', $dest, 'application/octet-stream', 2, 2);
} catch (\Exception $e) {
$this->fail('Duplicate assembly should not throw: '.$e->getMessage());
}
$storage->delete($storage->getRoot(), true);
}
```
A more realistic concurrent test using `pcntl_fork()`:
```php
public function testParallelChunkUploadWithFork(): void
{
if (!\function_exists('pcntl_fork')) {
$this->markTestSkipped('pcntl extension required for fork-based concurrency test');
}
$storage = $this->makeJoinTestStorage();
$dest = $storage->getRoot().DIRECTORY_SEPARATOR.'parallel-fork.dat';
// Pre-upload chunk 1
$storage->uploadData('AAAA', $dest, 'application/octet-stream', 1, 2);
$pid = pcntl_fork();
if ($pid === -1) {
$this->fail('Failed to fork');
} elseif ($pid === 0) {
// Child process: upload chunk 2
try {
$storage->uploadData('BBBB', $dest, 'application/octet-stream', 2, 2);
exit(0);
} catch (\Exception $e) {
exit(1);
}
}
// Parent process: also upload chunk 2 (race condition)
$parentSuccess = true;
try {
$storage->uploadData('BBBB', $dest, 'application/octet-stream', 2, 2);
} catch (\Exception $e) {
$parentSuccess = false;
}
pcntl_waitpid($pid, $status);
$childSuccess = pcntl_wexitstatus($status) === 0;
// At least one should succeed
$this->assertTrue($parentSuccess || $childSuccess, 'At least one parallel upload should succeed');
// File should be correctly assembled
$this->assertTrue(\file_exists($dest));
$this->assertSame('AAAABBBB', \file_get_contents($dest));
$storage->delete($storage->getRoot(), true);
}
```
## S3 Device
S3 already handles out-of-order multipart uploads natively. The `completeMultipartUpload` call with `ksort()` sorts parts by number regardless of upload order. However, parallel `completeMultipartUpload` calls for the same `uploadId` would still be problematic.
This is an **API-layer concern** — the Appwrite API should ensure only one request calls `completeMultipartUpload` per upload. The S3 device itself does not need changes.
## Files to Change
| File | Change |
|------|--------|
| `src/Storage/Device/Local.php` | Add `file_exists($path)` guard at start of `joinChunks()` and in `rename()` failure handler |
| `tests/Storage/Device/LocalTest.php` | Add `testParallelChunkUpload` and `testParallelChunkUploadWithFork` |
## Backwards Compatibility
Fully backwards compatible. The change only affects the error path when `rename()` fails due to an existing file. Previously it threw; now it returns silently. No public API signatures change.
## Related PRs
- Appwrite server PR: https://github.com/appwrite/appwrite/pull/12138 (out-of-order upload support)
- This storage PR is a prerequisite for the follow-up Appwrite PR that enables parallel chunk uploads at the API level.
+2
View File
@@ -47,6 +47,8 @@ _APP_DB_SCHEMA=appwrite
_APP_DB_USER=user
_APP_DB_PASS=password
_APP_DB_ROOT_PASS=rootsecretpassword
_APP_DATABASE_SHARED_TABLES=
_APP_DATABASE_SHARED_NAMESPACE=
_APP_DB_ADAPTER_DOCUMENTSDB=mongodb
_APP_DB_HOST_DOCUMENTSDB=mongodb
_APP_DB_PORT_DOCUMENTSDB=27017
+93 -57
View File
@@ -7,6 +7,7 @@ concurrency:
env:
COMPOSE_FILE: docker-compose.yml
IMAGE: appwrite-dev
REGISTRY_IMAGE: ghcr.io/${{ github.repository }}/appwrite-dev
K6_VERSION: '0.53.0'
on:
@@ -19,6 +20,10 @@ on:
type: string
default: ''
permissions:
contents: read
packages: write
jobs:
dependencies:
name: Checks / Dependencies
@@ -258,32 +263,30 @@ jobs:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build Appwrite
- name: Build and push Appwrite
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: ${{ env.IMAGE }}
load: true
push: true
tags: ${{ env.REGISTRY_IMAGE }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar
target: development
build-args: |
DEBUG=false
TESTING=true
VERSION=dev
- name: Upload Docker Image
uses: actions/upload-artifact@v7
with:
name: ${{ env.IMAGE }}
path: /tmp/${{ env.IMAGE }}.tar
retention-days: 1
unit:
name: Tests / Unit
runs-on: ubuntu-latest
@@ -291,26 +294,32 @@ jobs:
permissions:
contents: read
pull-requests: write
packages: read
steps:
- name: checkout
uses: actions/checkout@v6
- name: Download Docker Image
uses: actions/download-artifact@v7
with:
name: ${{ env.IMAGE }}
path: /tmp
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Docker Image
run: |
docker pull ${{ env.REGISTRY_IMAGE }}:${{ github.sha }}
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}
- name: Load and Start Appwrite
timeout-minutes: 5
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose pull --quiet --ignore-buildable
docker compose up -d --quiet-pull --wait
@@ -338,26 +347,32 @@ jobs:
permissions:
contents: read
pull-requests: write
packages: read
steps:
- name: checkout
uses: actions/checkout@v6
- name: Download Docker Image
uses: actions/download-artifact@v7
with:
name: ${{ env.IMAGE }}
path: /tmp
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Docker Image
run: |
docker pull ${{ env.REGISTRY_IMAGE }}:${{ github.sha }}
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}
- name: Load and Start Appwrite
timeout-minutes: 5
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose pull --quiet --ignore-buildable
docker compose up -d --quiet-pull --wait
@@ -396,6 +411,7 @@ jobs:
permissions:
contents: read
pull-requests: write
packages: read
strategy:
fail-fast: false
matrix:
@@ -450,16 +466,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
- name: Download Docker Image
uses: actions/download-artifact@v7
with:
name: ${{ env.IMAGE }}
path: /tmp
- name: Set environment
run: |
echo "_APP_OPTIONS_ROUTER_PROTECTION=enabled" >> $GITHUB_ENV
if [ "${{ matrix.database }}" = "MariaDB" ]; then
echo "COMPOSE_PROFILES=mariadb" >> $GITHUB_ENV
echo "_APP_DB_ADAPTER=mariadb" >> $GITHUB_ENV
@@ -483,6 +493,18 @@ jobs:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Docker Image
run: |
docker pull ${{ env.REGISTRY_IMAGE }}:${{ github.sha }}
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}
- name: Load and Start Appwrite
timeout-minutes: 5
env:
@@ -491,7 +513,6 @@ jobs:
_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES: ${{ matrix.mode != 'dedicated' && 'documentsdb_db_main' || '' }}
_APP_DATABASE_VECTORSDB_SHARED_TABLES: ${{ matrix.mode != 'dedicated' && 'vectorsdb_db_main' || '' }}
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose pull --quiet --ignore-buildable
docker compose up -d --quiet-pull --wait
@@ -545,6 +566,7 @@ jobs:
permissions:
contents: read
pull-requests: write
packages: read
strategy:
fail-fast: false
matrix:
@@ -555,18 +577,24 @@ jobs:
with:
fetch-depth: 1
- name: Download Docker Image
uses: actions/download-artifact@v7
with:
name: ${{ env.IMAGE }}
path: /tmp
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Docker Image
run: |
docker pull ${{ env.REGISTRY_IMAGE }}:${{ github.sha }}
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}
- name: Load and Start Appwrite
timeout-minutes: 5
env:
@@ -575,7 +603,6 @@ jobs:
_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES: ${{ matrix.mode != 'dedicated' && 'documentsdb_db_main' || '' }}
_APP_DATABASE_VECTORSDB_SHARED_TABLES: ${{ matrix.mode != 'dedicated' && 'vectorsdb_db_main' || '' }}
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose pull --quiet --ignore-buildable
docker compose up -d --quiet-pull --wait
@@ -606,6 +633,7 @@ jobs:
permissions:
contents: read
pull-requests: write
packages: read
strategy:
fail-fast: false
matrix:
@@ -614,18 +642,24 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
- name: Download Docker Image
uses: actions/download-artifact@v7
with:
name: ${{ env.IMAGE }}
path: /tmp
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Docker Image
run: |
docker pull ${{ env.REGISTRY_IMAGE }}:${{ github.sha }}
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}
- name: Load and Start Appwrite
timeout-minutes: 5
env:
@@ -633,7 +667,6 @@ jobs:
_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES: ${{ matrix.mode != 'dedicated' && 'documentsdb_db_main' || '' }}
_APP_DATABASE_VECTORSDB_SHARED_TABLES: ${{ matrix.mode != 'dedicated' && 'vectorsdb_db_main' || '' }}
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose pull --quiet --ignore-buildable
docker compose up -d --quiet-pull --wait
@@ -675,28 +708,31 @@ jobs:
contents: read
issues: write
pull-requests: write
packages: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Download Docker Image
uses: actions/download-artifact@v7
with:
name: ${{ env.IMAGE }}
path: /tmp
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Load Appwrite image
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Appwrite image
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker tag ${{ env.IMAGE }} ${{ env.IMAGE }}:after
docker pull ${{ env.REGISTRY_IMAGE }}:${{ github.sha }}
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}:after
- name: Setup k6
uses: grafana/setup-k6-action@ffe7d7290dfa715e48c2ccc924d068444c94bde2
+31 -1
View File
@@ -5,6 +5,11 @@ on:
types:
- closed
permissions:
actions: write
contents: read
packages: write
jobs:
cleanup:
runs-on: ubuntu-latest
@@ -36,4 +41,29 @@ jobs:
done
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Cleanup GHCR image
continue-on-error: true
run: |
package_path="${GITHUB_REPOSITORY#*/}/appwrite-dev"
encoded_path="$(printf '%s' "$package_path" | jq -Rr @uri)"
gh api --paginate "/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }}/commits" --jq '.[].sha' | while read -r sha; do
version_ids=$(gh api --paginate -H "Accept: application/vnd.github+json" \
"/orgs/${GITHUB_REPOSITORY_OWNER}/packages/container/${encoded_path}/versions" \
--jq ".[] | select(.metadata.container.tags | index(\"${sha}\")) | .id")
if [ -z "$version_ids" ]; then
echo "No GHCR version found for SHA ${sha}"
continue
fi
echo "$version_ids" | while read -r version_id; do
gh api --method DELETE -H "Accept: application/vnd.github+json" \
"/orgs/${GITHUB_REPOSITORY_OWNER}/packages/container/${encoded_path}/versions/${version_id}"
echo "Deleted ${package_path}:${sha} (version ${version_id})"
done
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+6 -2
View File
@@ -24,9 +24,11 @@ jobs:
ignore-unfixed: 'false'
severity: 'CRITICAL,HIGH'
- name: Upload Docker Image Scan Results
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v4
if: always() && hashFiles('trivy-image-results.sarif') != ''
with:
sarif_file: 'trivy-image-results.sarif'
category: 'trivy-image'
scan-code:
name: Scan Code
@@ -42,6 +44,8 @@ jobs:
output: 'trivy-fs-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Code Scan Results
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v4
if: always() && hashFiles('trivy-fs-results.sarif') != ''
with:
sarif_file: 'trivy-fs-results.sarif'
category: 'trivy-source'
+7 -2
View File
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM appwrite/base:1.0.1 AS base
FROM appwrite/base:1.4.1 AS base
LABEL maintainer="team@appwrite.io"
@@ -24,6 +24,10 @@ ENV _APP_VERSION=$VERSION \
_APP_HOME=https://appwrite.io
RUN \
if [ "$DEBUG" != "true" ]; then \
rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
rm -f /usr/local/lib/php/extensions/no-debug-non-zts-*/xdebug.so; \
fi && \
if [ "$DEBUG" == "true" ]; then \
apk add boost boost-dev; \
fi
@@ -100,7 +104,8 @@ RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
FROM base AS production
RUN rm -rf /usr/src/code/app/config/specs && \
rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20240924/xdebug.so && \
rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini && \
rm -f /usr/local/lib/php/extensions/no-debug-non-zts-*/xdebug.so && \
find /usr -name '*.a' -delete 2>/dev/null || true && \
find /usr -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true && \
find /usr -name '*.pyc' -delete 2>/dev/null || true
+20
View File
@@ -157,12 +157,19 @@ $container->set('getProjectDB', function (Group $pools, Database $dbForPlatform,
}
if (isset($databases[$dsn->getHost()])) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database = $databases[$dsn->getHost()];
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
@@ -182,9 +189,16 @@ $container->set('getProjectDB', function (Group $pools, Database $dbForPlatform,
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setSharedTables(true)
->setTenant($project->getSequence())
->setGlobalCollections($projectsGlobalCollections)
->setNamespace($dsn->getParam('namespace'));
} else {
$database
@@ -212,6 +226,11 @@ $container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization
return $database;
}
/** @var array $collections */
$collections = Config::getParam('collections', []);
$logsCollections = $collections['logs'] ?? [];
$logsCollections = array_keys($logsCollections);
$adapter = new DatabasePool($pools->get('logs'));
$database = new Database($adapter, $cache);
@@ -220,6 +239,7 @@ $container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setGlobalCollections($logsCollections)
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
+7
View File
@@ -1523,6 +1523,13 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_team_confirm'),
'type' => Database::INDEX_KEY,
'attributes' => ['teamInternalId', 'confirm'],
'lengths' => [],
'orders' => [],
],
],
],
+22 -1
View File
@@ -404,6 +404,13 @@ $platformCollections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_teamInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['teamInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -635,6 +642,13 @@ $platformCollections = [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_project_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -1007,7 +1021,14 @@ $platformCollections = [
'attributes' => ['projectInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
]
],
[
'$id' => ID::custom('_key_project_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
+11 -1
View File
@@ -384,7 +384,7 @@ return [
],
Exception::API_KEY_EXPIRED => [
'name' => Exception::API_KEY_EXPIRED,
'description' => 'The dynamic API key has expired. Please don\'t use dynamic API keys for more than duration of the execution.',
'description' => 'The ephemeral API key has expired. Please don\'t use ephemeral API keys for more than duration of the execution.',
'code' => 401,
],
@@ -623,6 +623,11 @@ return [
'description' => 'Synchronous function execution timed out. Use asynchronous execution instead, or ensure the execution duration doesn\'t exceed 30 seconds.',
'code' => 408,
],
Exception::FUNCTION_ASYNCHRONOUS_TIMEOUT => [
'name' => Exception::FUNCTION_ASYNCHRONOUS_TIMEOUT,
'description' => 'Asynchronous function execution timed out. Ensure the execution duration doesn\'t exceed the configured function timeout.',
'code' => 408,
],
Exception::FUNCTION_TEMPLATE_NOT_FOUND => [
'name' => Exception::FUNCTION_TEMPLATE_NOT_FOUND,
'description' => 'Function Template with the requested ID could not be found.',
@@ -687,6 +692,11 @@ return [
'description' => 'Build with the requested ID failed. Please check the logs for more information.',
'code' => 400,
],
Exception::BUILD_TIMEOUT => [
'name' => Exception::BUILD_TIMEOUT,
'description' => 'Build timed out. Increase the build timeout via the `_APP_COMPUTE_BUILD_TIMEOUT` environment variable, or simplify the build to complete within the limit.',
'code' => 408,
],
/** Deployments */
Exception::DEPLOYMENT_NOT_FOUND => [
+10
View File
@@ -28,6 +28,16 @@
"emails.invitation.thanks": "Gracias.,",
"emails.invitation.buttonText": "Aceptar invitación a {{team}}",
"emails.invitation.signature": "El equipo de {{project}}",
"emails.sessionAlert.subject": "Alerta de seguridad: nueva sesión en tu cuenta de {{project}}",
"emails.sessionAlert.preview": "Nuevo inicio de sesión detectado en {{project}} a las {{time}} UTC.",
"emails.sessionAlert.hello": "Hola {{user}},",
"emails.sessionAlert.body": "Se ha creado una nueva sesión en tu cuenta de {{b}}{{project}}{{/b}}, {{b}}el {{date}} de {{year}} a las {{time}} UTC{{/b}}.\nEstos son los detalles de la nueva sesión:",
"emails.sessionAlert.listDevice": "Dispositivo: {{b}}{{device}}{{/b}}",
"emails.sessionAlert.listIpAddress": "Dirección IP: {{b}}{{ipAddress}}{{/b}}",
"emails.sessionAlert.listCountry": "País: {{b}}{{country}}{{/b}}",
"emails.sessionAlert.footer": "Si has sido tú, no tienes que hacer nada más.\nSi no has iniciado esta sesión o sospechas actividad no autorizada, protege tu cuenta.",
"emails.sessionAlert.thanks": "Gracias,",
"emails.sessionAlert.signature": "El equipo de {{project}}",
"locale.country.unknown": "Desconocido",
"countries.af": "Afganistán",
"countries.ao": "Angola",
+7 -7
View File
@@ -21,8 +21,8 @@ $member = [
'projects.read',
'locale.read',
'avatars.read',
'execution.read',
'execution.write',
'executions.read',
'executions.write',
'targets.read',
'targets.write',
'subscribers.write',
@@ -59,8 +59,8 @@ $admins = [
'oauth2.write',
'mocks.read',
'mocks.write',
'policies.read',
'policies.write',
'project.policies.read',
'project.policies.write',
'templates.read',
'templates.write',
'projects.write',
@@ -81,8 +81,8 @@ $admins = [
'sites.write',
'log.read',
'log.write',
'execution.read',
'execution.write',
'executions.read',
'executions.write',
'rules.read',
'rules.write',
'migrations.read',
@@ -123,7 +123,7 @@ return [
'files.write',
'locale.read',
'avatars.read',
'execution.write',
'executions.write',
],
],
User::ROLE_USERS => [
+306 -181
View File
@@ -1,239 +1,364 @@
<?php
return [ // List of publicly visible scopes
'sessions.write' => [
'description' => 'Access to create, update, and delete user sessions',
],
'users.read' => [
'description' => 'Access to read your project\'s users',
],
'users.write' => [
'description' => 'Access to create, update, and delete your project\'s users',
],
'teams.read' => [
'description' => 'Access to read your project\'s teams',
],
'teams.write' => [
'description' => 'Access to create, update, and delete your project\'s teams',
],
'databases.read' => [
'description' => 'Access to read your project\'s databases',
],
'databases.write' => [
'description' => 'Access to create, update, and delete your project\'s databases',
],
'collections.read' => [
'description' => 'Access to read your project\'s database collections',
],
'collections.write' => [
'description' => 'Access to create, update, and delete your project\'s database collections',
],
'tables.read' => [
'description' => 'Access to read your project\'s database tables',
],
'tables.write' => [
'description' => 'Access to create, update, and delete your project\'s database tables',
],
'attributes.read' => [
'description' => 'Access to read your project\'s database collection\'s attributes',
],
'attributes.write' => [
'description' => 'Access to create, update, and delete your project\'s database collection\'s attributes',
],
'columns.read' => [
'description' => 'Access to read your project\'s database table\'s columns',
],
'columns.write' => [
'description' => 'Access to create, update, and delete your project\'s database table\'s columns',
],
'indexes.read' => [
'description' => 'Access to read your project\'s database table\'s indexes',
],
'indexes.write' => [
'description' => 'Access to create, update, and delete your project\'s database table\'s indexes',
],
'documents.read' => [
'description' => 'Access to read your project\'s database documents',
],
'documents.write' => [
'description' => 'Access to create, update, and delete your project\'s database documents',
],
'rows.read' => [
'description' => 'Access to read your project\'s database rows',
],
'rows.write' => [
'description' => 'Access to create, update, and delete your project\'s database rows',
],
'files.read' => [
'description' => 'Access to read your project\'s storage files and preview images',
],
'files.write' => [
'description' => 'Access to create, update, and delete your project\'s storage files',
],
'buckets.read' => [
'description' => 'Access to read your project\'s storage buckets',
],
'buckets.write' => [
'description' => 'Access to create, update, and delete your project\'s storage buckets',
],
'functions.read' => [
'description' => 'Access to read your project\'s functions and code deployments',
],
'functions.write' => [
'description' => 'Access to create, update, and delete your project\'s functions and code deployments',
],
'sites.read' => [
'description' => 'Access to read your project\'s sites and deployments',
],
'sites.write' => [
'description' => 'Access to create, update, and delete your project\'s sites and deployments',
],
'log.read' => [
'description' => 'Access to read your site\'s logs',
],
'log.write' => [
'description' => 'Access to update, and delete your site\'s logs',
],
'execution.read' => [
'description' => 'Access to read your project\'s execution logs',
],
'execution.write' => [
'description' => 'Access to execute your project\'s functions',
],
'locale.read' => [
'description' => 'Access to access your project\'s Locale service',
],
'avatars.read' => [
'description' => 'Access to access your project\'s Avatars service',
],
'health.read' => [
'description' => 'Access to read your project\'s health status',
],
'providers.read' => [
'description' => 'Access to read your project\'s providers',
],
'providers.write' => [
'description' => 'Access to create, update, and delete your project\'s providers',
],
'messages.read' => [
'description' => 'Access to read your project\'s messages',
],
'messages.write' => [
'description' => 'Access to create, update, and delete your project\'s messages',
],
'topics.read' => [
'description' => 'Access to read your project\'s topics',
],
'topics.write' => [
'description' => 'Access to create, update, and delete your project\'s topics',
],
'subscribers.read' => [
'description' => 'Access to read your project\'s subscribers',
],
'subscribers.write' => [
'description' => 'Access to create, update, and delete your project\'s subscribers',
],
'targets.read' => [
'description' => 'Access to read your project\'s targets',
],
'targets.write' => [
'description' => 'Access to create, update, and delete your project\'s targets',
],
'rules.read' => [
'description' => 'Access to read your project\'s proxy rules',
],
'rules.write' => [
'description' => 'Access to create, update, and delete your project\'s proxy rules',
],
'schedules.read' => [
'description' => 'Access to read your project\'s schedules',
],
'schedules.write' => [
'description' => 'Access to create, update, and delete your project\'s schedules',
],
'migrations.read' => [
'description' => 'Access to read your project\'s migrations',
],
'migrations.write' => [
'description' => 'Access to create, update, and delete your project\'s migrations.',
],
'vcs.read' => [
'description' => 'Access to read your project\'s VCS repositories',
],
'vcs.write' => [
'description' => 'Access to create, update, and delete your project\'s VCS repositories',
],
'assistant.read' => [
'description' => 'Access to read the Assistant service',
],
'tokens.read' => [
'description' => 'Access to read your project\'s tokens',
],
'tokens.write' => [
'description' => 'Access to create, update, and delete your project\'s tokens',
],
"webhooks.read" => [
"description" =>
"Access to read project\'s webhooks",
],
"webhooks.write" => [
"description" =>
"Access to create, update, and delete project\'s webhooks",
],
// List of publicly visible scopes
return [
// Project
"project.read" => [
"description" =>
"Access to read project\'s information",
"category" => "Project",
],
"project.write" => [
"description" =>
"Access to update project\'s information",
"category" => "Project",
],
"keys.read" => [
"description" =>
"Access to read project\'s keys",
"category" => "Project",
],
"keys.write" => [
"description" =>
"Access to create, update, and delete project\'s keys",
"category" => "Project",
],
"platforms.read" => [
"description" =>
"Access to read project\'s platforms",
"category" => "Project",
],
"platforms.write" => [
"description" =>
"Access to create, update, and delete project\'s platforms",
"category" => "Project",
],
"mocks.read" => [
"description" =>
"Access to read project\'s mocks",
"category" => "Project",
],
"mocks.write" => [
"description" =>
"Access to create, update, and delete project\'s mocks",
"category" => "Project",
],
"policies.read" => [
"description" =>
"Access to read project\'s policies",
"Access to read project\'s policies. Replaced by \'project.policies.read\' for more granular control",
"category" => "Project",
'deprecated' => true,
],
"policies.write" => [
"description" =>
"Access to update project\'s policies. Replaces by \'project.policies.write\' for more granular control",
"category" => "Project",
'deprecated' => true,
],
"project.policies.read" => [
"description" =>
"Access to read project\'s policies",
"category" => "Project",
],
"project.policies.write" => [
"description" =>
"Access to update project\'s policies",
"category" => "Project",
],
"templates.read" => [
"description" =>
"Access to read project\'s templates",
"category" => "Project",
],
"templates.write" => [
"description" =>
"Access to create, update, and delete project\'s templates",
"category" => "Project",
],
"oauth2.read" => [
"description" =>
"Access to read project\'s OAuth2 configuration",
"category" => "Project",
],
"oauth2.write" => [
"description" =>
"Access to update project\'s OAuth2 configuration",
"category" => "Project",
],
// Auth
'users.read' => [
'description' => 'Access to read users',
'category' => 'Auth',
],
'users.write' => [
'description' => 'Access to create, update, and delete users',
'category' => 'Auth',
],
'sessions.read' => [
'description' => 'Access to read user sessions',
'category' => 'Auth',
],
'sessions.write' => [
'description' => 'Access to create, update, and delete user sessions',
'category' => 'Auth',
],
'teams.read' => [
'description' => 'Access to read teams',
'category' => 'Auth',
],
'teams.write' => [
'description' => 'Access to create, update, and delete teams',
'category' => 'Auth',
],
// Databases
'databases.read' => [
'description' => 'Access to read databases',
'category' => 'Databases',
],
'databases.write' => [
'description' => 'Access to create, update, and delete databases',
'category' => 'Databases',
],
'tables.read' => [
'description' => 'Access to read database tables',
'category' => 'Databases',
],
'tables.write' => [
'description' => 'Access to create, update, and delete database tables',
'category' => 'Databases',
],
'columns.read' => [
'description' => 'Access to read database table columns',
'category' => 'Databases',
],
'columns.write' => [
'description' => 'Access to create, update, and delete database table columns',
'category' => 'Databases',
],
'indexes.read' => [
'description' => 'Access to read database table indexes',
'category' => 'Databases',
],
'indexes.write' => [
'description' => 'Access to create, update, and delete database table indexes',
'category' => 'Databases',
],
'rows.read' => [
'description' => 'Access to read database table rows',
'category' => 'Databases',
],
'rows.write' => [
'description' => 'Access to create, update, and delete database table rows',
'category' => 'Databases',
],
'collections.read' => [
'description' => 'Access to read database collections',
'category' => 'Databases',
'deprecated' => true,
],
'collections.write' => [
'description' => 'Access to create, update, and delete database collections',
'category' => 'Databases',
'deprecated' => true,
],
'attributes.read' => [
'description' => 'Access to read database collection attributes',
'category' => 'Databases',
'deprecated' => true,
],
'attributes.write' => [
'description' => 'Access to create, update, and delete database collection attributes',
'category' => 'Databases',
'deprecated' => true,
],
'documents.read' => [
'description' => 'Access to read database collection documents',
'category' => 'Databases',
'deprecated' => true,
],
'documents.write' => [
'description' => 'Access to create, update, and delete database collection documents',
'category' => 'Databases',
'deprecated' => true,
],
// Storage
'buckets.read' => [
'description' => 'Access to read storage buckets',
'category' => 'Storage',
],
'buckets.write' => [
'description' => 'Access to create, update, and delete storage buckets',
'category' => 'Storage',
],
'files.read' => [
'description' => 'Access to read storage files and preview images',
'category' => 'Storage',
],
'files.write' => [
'description' => 'Access to create, update, and delete storage files',
'category' => 'Storage',
],
'tokens.read' => [
'description' => 'Access to read storage file tokens',
'category' => 'Storage',
],
'tokens.write' => [
'description' => 'Access to create, update, and delete storage file tokens',
'category' => 'Storage',
],
// Functions
'functions.read' => [
'description' => 'Access to read functions and deployments',
'category' => 'Functions',
],
'functions.write' => [
'description' => 'Access to create, update, and delete functions and deployments',
'category' => 'Functions',
],
'executions.read' => [
'description' => 'Access to read function executions',
'category' => 'Functions',
],
'executions.write' => [
'description' => 'Access to create function executions',
'category' => 'Functions',
],
'execution.read' => [
'description' => 'Access to read function executions. This scope is deprecated for consistency purposes, and replaced by `executions.read`.',
'category' => 'Functions',
'deprecated' => true,
],
'execution.write' => [
'description' => 'Access to create function executions. This scope is deprecated for consistency purposes, and replaced by `executions.write`.',
'category' => 'Functions',
'deprecated' => true,
],
// Sites
'sites.read' => [
'description' => 'Access to read sites and deployments',
'category' => 'Sites',
],
'sites.write' => [
'description' => 'Access to create, update, and delete sites and deployments',
'category' => 'Sites',
],
'log.read' => [
'description' => 'Access to read site logs',
'category' => 'Sites',
],
'log.write' => [
'description' => 'Access to update, and delete site logs',
'category' => 'Sites',
],
// Messaging
'providers.read' => [
'description' => 'Access to read messaging providers',
'category' => 'Messaging',
],
'providers.write' => [
'description' => 'Access to create, update, and delete messaging providers',
'category' => 'Messaging',
],
'topics.read' => [
'description' => 'Access to read messaging topics',
'category' => 'Messaging',
],
'topics.write' => [
'description' => 'Access to create, update, and delete messaging topics',
'category' => 'Messaging',
],
'subscribers.read' => [
'description' => 'Access to read messaging subscribers',
'category' => 'Messaging',
],
'subscribers.write' => [
'description' => 'Access to create, update, and delete messaging subscribers',
'category' => 'Messaging',
],
'targets.read' => [
'description' => 'Access to read messaging targets',
'category' => 'Messaging',
],
'targets.write' => [
'description' => 'Access to create, update, and delete messaging targets',
'category' => 'Messaging',
],
'messages.read' => [
'description' => 'Access to read messaging messages',
'category' => 'Messaging',
],
'messages.write' => [
'description' => 'Access to create, update, and delete messaging messages',
'category' => 'Messaging',
],
// Proxy
'rules.read' => [
'description' => 'Access to read proxy rules.',
'category' => 'Proxy',
],
'rules.write' => [
'description' => 'Access to create, update, and delete proxy rules.',
'category' => 'Proxy',
],
// Other
"webhooks.read" => [
"description" =>
"Access to read webhooks",
'category' => 'Other',
],
"webhooks.write" => [
"description" =>
"Access to create, update, and delete webhooks",
'category' => 'Other',
],
'locale.read' => [
'description' => 'Access to use Locale service',
'category' => 'Other',
],
'avatars.read' => [
'description' => 'Access to use Avatars service',
'category' => 'Other',
],
'health.read' => [
'description' => 'Access to use Health service',
'category' => 'Other',
],
'assistant.read' => [
'description' => 'Access to use Assistant service',
'category' => 'Other',
],
'migrations.read' => [
'description' => 'Access to read migrations',
'category' => 'Other',
],
'migrations.write' => [
'description' => 'Access to create, update, and delete migrations.',
'category' => 'Other',
],
// TODO: Figure out where to move those
'schedules.read' => [
'description' => 'Access to read schedules.',
'category' => 'Other',
],
'schedules.write' => [
'description' => 'Access to create, update, and delete schedules.',
'category' => 'Other',
],
'vcs.read' => [
'description' => 'Access to read resources under VCS service.',
'category' => 'Other',
],
'vcs.write' => [
'description' => 'Access to create, update, and delete resources under VCS service.',
'category' => 'Other',
],
];
+1 -1
View File
@@ -286,7 +286,7 @@ return [
'name' => 'Migrations',
'subtitle' => 'The Migrations service allows you to migrate third-party data to your Appwrite project.',
'description' => '/docs/services/migrations.md',
'controller' => 'api/migrations.php',
'controller' => '', // Uses modules
'sdk' => true,
'docs' => true,
'docsUrl' => 'https://appwrite.io/docs/migrations',
+3 -2
View File
@@ -79,12 +79,13 @@ return [
...getRuntimes($templateRuntimes['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter', $allowList),
...getRuntimes($templateRuntimes['BUN'], 'bun install', 'src/main.ts', 'bun/starter', $allowList),
...getRuntimes($templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter', $allowList),
...getRuntimes($templateRuntimes['RUST'], '', 'main.rs', 'rust/starter', $allowList),
],
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/starter">file</a>.',
'instructions' => 'For documentation and instructions check out the <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates">templates repository</a>.',
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates',
'providerOwner' => 'appwrite',
'providerVersion' => '0.2.*',
'providerVersion' => '0.3.*',
'variables' => [],
'scopes' => ['users.read']
],
+2 -2
View File
@@ -830,11 +830,11 @@ Http::patch('/v1/account/sessions/:sessionId')
$refreshToken = $session->getAttribute('providerRefreshToken', '');
$oAuthProviders = Config::getParam('oAuthProviders') ?? [];
$className = $oAuthProviders[$provider]['class'] ?? null;
if (!empty($provider) && ($className === null || !\class_exists($className))) {
if (!empty($refreshToken) && ($className === null || !\class_exists($className))) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
if (!empty($provider) && \class_exists($className)) {
if ($className !== null && \class_exists($className)) {
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -856,7 +856,7 @@ Http::get('/v1/users/:userId/targets/:targetId')
Http::get('/v1/users/:userId/sessions')
->desc('List user sessions')
->groups(['api', 'users'])
->label('scope', 'users.read')
->label('scope', ['users.read', 'sessions.read'])
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
@@ -2314,7 +2314,7 @@ Http::post('/v1/users/:userId/sessions')
->desc('Create session')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'users.write')
->label('scope', ['users.write', 'sessions.write'])
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{request.userId}')
->label('usage.metric', 'sessions.{scope}.requests.create')
@@ -2470,7 +2470,7 @@ Http::delete('/v1/users/:userId/sessions/:sessionId')
->desc('Delete user session')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.[sessionId].delete')
->label('scope', 'users.write')
->label('scope', ['users.write', 'sessions.write'])
->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
@@ -2521,7 +2521,7 @@ Http::delete('/v1/users/:userId/sessions')
->desc('Delete user sessions')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.delete')
->label('scope', 'users.write')
->label('scope', ['users.write', 'sessions.write'])
->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
+42 -21
View File
@@ -27,6 +27,8 @@ use Appwrite\Utopia\Request\Filters\V20 as RequestV20;
use Appwrite\Utopia\Request\Filters\V21 as RequestV21;
use Appwrite\Utopia\Request\Filters\V22 as RequestV22;
use Appwrite\Utopia\Request\Filters\V23 as RequestV23;
use Appwrite\Utopia\Request\Filters\V24 as RequestV24;
use Appwrite\Utopia\Request\Filters\V25 as RequestV25;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
@@ -36,7 +38,10 @@ use Appwrite\Utopia\Response\Filters\V20 as ResponseV20;
use Appwrite\Utopia\Response\Filters\V21 as ResponseV21;
use Appwrite\Utopia\Response\Filters\V22 as ResponseV22;
use Appwrite\Utopia\Response\Filters\V23 as ResponseV23;
use Appwrite\Utopia\Response\Filters\V24 as ResponseV24;
use Appwrite\Utopia\Response\Filters\V25 as ResponseV25;
use Appwrite\Utopia\View;
use Executor\Exception\Timeout as ExecutorTimeout;
use Executor\Executor;
use MaxMind\Db\Reader;
use Swoole\Http\Request as SwooleRequest;
@@ -397,7 +402,7 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
'projectId' => $project->getId(),
'scopes' => $resource->getAttribute('scopes', [])
]);
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $jwtKey;
$headers['x-appwrite-key'] = API_KEY_EPHEMERAL . '_' . $jwtKey;
$headers['x-appwrite-trigger'] = 'http';
$headers['x-appwrite-user-jwt'] = '';
@@ -577,26 +582,30 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
'site' => '',
};
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
body: \strlen($body) > 0 ? $body : null,
variables: $vars,
timeout: $resource->getAttribute('timeout', 30),
image: $runtime['image'],
source: $source,
entrypoint: $entrypoint,
version: $version,
path: $path,
method: $method,
headers: $headers,
runtimeEntrypoint: $runtimeEntrypoint,
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
logging: $resource->getAttribute('logging', true),
requestTimeout: 30,
responseFormat: Executor::RESPONSE_FORMAT_ARRAY_HEADERS
);
try {
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
body: \strlen($body) > 0 ? $body : null,
variables: $vars,
timeout: $resource->getAttribute('timeout', 30),
image: $runtime['image'],
source: $source,
entrypoint: $entrypoint,
version: $version,
path: $path,
method: $method,
headers: $headers,
runtimeEntrypoint: $runtimeEntrypoint,
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
logging: $resource->getAttribute('logging', true),
requestTimeout: 30,
responseFormat: Executor::RESPONSE_FORMAT_ARRAY_HEADERS
);
} catch (ExecutorTimeout $th) {
throw new AppwriteException(AppwriteException::FUNCTION_SYNCHRONOUS_TIMEOUT, previous: $th);
}
$headerOverrides = [];
@@ -899,6 +908,12 @@ Http::init()
if (version_compare($requestFormat, '1.9.2', '<')) {
$request->addFilter(new RequestV23());
}
if (version_compare($requestFormat, '1.9.3', '<')) {
$request->addFilter(new RequestV24());
}
if (version_compare($requestFormat, '1.9.4', '<')) {
$request->addFilter(new RequestV25());
}
}
$localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
@@ -923,6 +938,12 @@ Http::init()
*/
$responseFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($responseFormat) {
if (version_compare($responseFormat, '1.9.4', '<')) {
$response->addFilter(new ResponseV25());
}
if (version_compare($responseFormat, '1.9.3', '<')) {
$response->addFilter(new ResponseV24());
}
if (version_compare($responseFormat, '1.9.2', '<')) {
$response->addFilter(new ResponseV23());
}
+2 -1
View File
@@ -183,7 +183,8 @@ Http::init()
// Handle special app role case
if ($apiKey->getRole() === User::ROLE_APPS) {
// Disable authorization checks for project API keys
if (($apiKey->getType() === API_KEY_STANDARD || $apiKey->getType() === API_KEY_DYNAMIC) && $apiKey->getProjectId() === $project->getId()) {
// Dynamic supported for backwards compatibility
if (($apiKey->getType() === API_KEY_STANDARD || $apiKey->getType() === API_KEY_EPHEMERAL || $apiKey->getType() === 'dynamic') && $apiKey->getProjectId() === $project->getId()) {
$authorization->setDefaultStatus(false);
}
+5 -3
View File
@@ -44,14 +44,15 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4323;
const APP_VERSION_STABLE = '1.9.2';
const APP_CACHE_BUSTER = 4325;
const APP_VERSION_STABLE = '1.9.4';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
const APP_DATABASE_ATTRIBUTE_URL = 'url';
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
const APP_DATABASE_ATTRIBUTE_BIGINT_RANGE = 'bigintRange';
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
const APP_DATABASE_ATTRIBUTE_POINT = 'point';
const APP_DATABASE_ATTRIBUTE_LINE = 'line';
@@ -244,6 +245,7 @@ const APP_AUTH_TYPE_KEY = 'Key';
const APP_AUTH_TYPE_ADMIN = 'Admin';
// Response related
const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB
const APP_LIMIT_UPLOAD_CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
const APP_FUNCTION_LOG_LENGTH_LIMIT = 1000000;
const APP_FUNCTION_ERROR_LENGTH_LIMIT = 1000000;
// Function headers
@@ -255,7 +257,7 @@ const MESSAGE_TYPE_SMS = 'sms';
const MESSAGE_TYPE_PUSH = 'push';
// API key types
const API_KEY_STANDARD = 'standard';
const API_KEY_DYNAMIC = 'dynamic';
const API_KEY_EPHEMERAL = 'ephemeral';
const API_KEY_ORGANIZATION = 'organization';
const API_KEY_ACCOUNT = 'account';
// Usage metrics
+7
View File
@@ -36,6 +36,13 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_INTEGER);
// BigInt uses a dedicated bigintRange format name to avoid clobbering `intRange`.
Structure::addFormat(APP_DATABASE_ATTRIBUTE_BIGINT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_BIGINT);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
+10
View File
@@ -11,6 +11,7 @@ use Appwrite\Utopia\Response\Model\AlgoScryptModified;
use Appwrite\Utopia\Response\Model\AlgoSha;
use Appwrite\Utopia\Response\Model\Any;
use Appwrite\Utopia\Response\Model\Attribute;
use Appwrite\Utopia\Response\Model\AttributeBigInt;
use Appwrite\Utopia\Response\Model\AttributeBoolean;
use Appwrite\Utopia\Response\Model\AttributeDatetime;
use Appwrite\Utopia\Response\Model\AttributeEmail;
@@ -37,6 +38,7 @@ use Appwrite\Utopia\Response\Model\Branch;
use Appwrite\Utopia\Response\Model\Bucket;
use Appwrite\Utopia\Response\Model\Collection;
use Appwrite\Utopia\Response\Model\Column;
use Appwrite\Utopia\Response\Model\ColumnBigInt;
use Appwrite\Utopia\Response\Model\ColumnBoolean;
use Appwrite\Utopia\Response\Model\ColumnDatetime;
use Appwrite\Utopia\Response\Model\ColumnEmail;
@@ -56,6 +58,8 @@ use Appwrite\Utopia\Response\Model\ColumnString;
use Appwrite\Utopia\Response\Model\ColumnText;
use Appwrite\Utopia\Response\Model\ColumnURL;
use Appwrite\Utopia\Response\Model\ColumnVarchar;
use Appwrite\Utopia\Response\Model\ConsoleKeyScope;
use Appwrite\Utopia\Response\Model\ConsoleKeyScopeList;
use Appwrite\Utopia\Response\Model\ConsoleOAuth2Provider;
use Appwrite\Utopia\Response\Model\ConsoleOAuth2ProviderList;
use Appwrite\Utopia\Response\Model\ConsoleOAuth2ProviderParameter;
@@ -71,6 +75,7 @@ use Appwrite\Utopia\Response\Model\DetectionVariable;
use Appwrite\Utopia\Response\Model\DevKey;
use Appwrite\Utopia\Response\Model\Document as ModelDocument;
use Appwrite\Utopia\Response\Model\Embedding;
use Appwrite\Utopia\Response\Model\EphemeralKey;
use Appwrite\Utopia\Response\Model\Error;
use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution;
@@ -294,6 +299,7 @@ Response::setModel(new Attribute());
Response::setModel(new AttributeList());
Response::setModel(new AttributeString());
Response::setModel(new AttributeInteger());
Response::setModel(new AttributeBigInt());
Response::setModel(new AttributeFloat());
Response::setModel(new AttributeBoolean());
Response::setModel(new AttributeEmail());
@@ -327,6 +333,7 @@ Response::setModel(new Column());
Response::setModel(new ColumnList());
Response::setModel(new ColumnString());
Response::setModel(new ColumnInteger());
Response::setModel(new ColumnBigInt());
Response::setModel(new ColumnFloat());
Response::setModel(new ColumnBoolean());
Response::setModel(new ColumnEmail());
@@ -392,6 +399,7 @@ Response::setModel(new Execution());
Response::setModel(new Project());
Response::setModel(new Webhook());
Response::setModel(new Key());
Response::setModel(new EphemeralKey());
Response::setModel(new DevKey());
Response::setModel(new MockNumber());
Response::setModel(new OAuth2GitHub());
@@ -486,6 +494,8 @@ Response::setModel(new ConsoleVariables());
Response::setModel(new ConsoleOAuth2ProviderParameter());
Response::setModel(new ConsoleOAuth2Provider());
Response::setModel(new ConsoleOAuth2ProviderList());
Response::setModel(new ConsoleKeyScope());
Response::setModel(new ConsoleKeyScopeList());
Response::setModel(new MFAChallenge());
Response::setModel(new MFARecoveryCodes());
Response::setModel(new MFAType());
+6
View File
@@ -159,10 +159,16 @@ $container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization
$adapter = new DatabasePool($pools->get('logs'));
$database = new Database($adapter, $cache);
/** @var array $collections */
$collections = Config::getParam('collections', []);
$logsCollections = $collections['logs'] ?? [];
$logsCollections = array_keys($logsCollections);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setSharedTables(true)
->setGlobalCollections($logsCollections)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
+28
View File
@@ -204,9 +204,16 @@ return function (Container $container): void {
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setSharedTables(true)
->setTenant($project->getSequence())
->setGlobalCollections($projectsGlobalCollections)
->setNamespace($dsn->getParam('namespace'));
} else {
$database
@@ -223,6 +230,11 @@ return function (Container $container): void {
$adapter = null;
return function (?Document $project = null) use ($pools, $cache, $authorization, &$adapter) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$logsCollections = $collections['logs'] ?? [];
$logsCollections = array_keys($logsCollections);
$adapter ??= new DatabasePool($pools->get('logs'));
$database = new Database($adapter, $cache);
@@ -230,6 +242,7 @@ return function (Container $container): void {
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setSharedTables(true)
->setGlobalCollections($logsCollections)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
@@ -690,8 +703,15 @@ return function (Container $container): void {
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
@@ -1292,6 +1312,12 @@ return function (Container $container): void {
$database = new Database($adapter, $cache);
$sharedTables = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')));
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
@@ -1314,6 +1340,7 @@ return function (Container $container): void {
if (\in_array($databaseHost, $dbTypeSharedTables)) {
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($databaseDSN->getParam('namespace'));
} else {
@@ -1325,6 +1352,7 @@ return function (Container $container): void {
} elseif (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
+38
View File
@@ -14,6 +14,7 @@ use Appwrite\Utopia\Database\Documents\User;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Audit as UtopiaAudit;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Utopia\Console;
use Utopia\Database\Adapter\Pool as DatabasePool;
use Utopia\Database\Database;
@@ -90,8 +91,15 @@ return function (Container $container): void {
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
@@ -130,8 +138,15 @@ return function (Container $container): void {
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
@@ -152,8 +167,15 @@ return function (Container $container): void {
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
@@ -210,6 +232,14 @@ return function (Container $container): void {
$sharedTables = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')));
/** @var array $collections */
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database->setGlobalCollections($projectsGlobalCollections);
// For separate pools (documentsdb/vectorsdb), check their own shared tables config.
// If not configured, use dedicated mode to avoid cross-engine tenant type mismatches.
if ($databaseHost !== $dsn->getHost()) {
@@ -222,6 +252,7 @@ return function (Container $container): void {
if (\in_array($databaseHost, $dbTypeSharedTables)) {
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($projectDocument->getSequence())
->setNamespace($databaseDSN->getParam('namespace'));
} else {
@@ -233,6 +264,7 @@ return function (Container $container): void {
} elseif (\in_array($dsn->getHost(), $sharedTables, true)) {
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($projectDocument->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
@@ -257,6 +289,11 @@ return function (Container $container): void {
return $database;
}
/** @var array $collections */
$collections = Config::getParam('collections', []);
$logsCollections = $collections['logs'] ?? [];
$logsCollections = array_keys($logsCollections);
$adapter = new DatabasePool($pools->get('logs'));
$database = new Database($adapter, $cache);
@@ -264,6 +301,7 @@ return function (Container $container): void {
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setSharedTables(true)
->setGlobalCollections($logsCollections)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES_WORKER);
+10 -1
View File
@@ -45,7 +45,10 @@ use Utopia\WebSocket\Adapter;
use Utopia\WebSocket\Server;
require_once __DIR__ . '/init.php';
require_once __DIR__ . '/init/span.php';
if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') {
require_once __DIR__ . '/init/span.php';
}
/** @var Registry $register */
$register = $GLOBALS['register'] ?? throw new \RuntimeException('Registry not initialized');
@@ -127,8 +130,14 @@ if (!function_exists('getProjectDB')) {
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
$collections = Config::getParam('collections', []);
$projectCollections = $collections['projects'] ?? [];
$projectsGlobalCollections = array_keys($projectCollections);
$projectsGlobalCollections[] = 'audit';
$database
->setSharedTables(true)
->setGlobalCollections($projectsGlobalCollections)
->setTenant($project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
+6 -6
View File
@@ -49,7 +49,7 @@
"ext-openssl": "*",
"ext-zlib": "*",
"ext-sockets": "*",
"appwrite/php-runtimes": "0.19.*",
"appwrite/php-runtimes": "0.20.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "1.2.*",
"utopia-php/agents": "1.2.*",
@@ -74,15 +74,15 @@
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.22.*",
"utopia-php/migration": "1.9.*",
"utopia-php/migration": "1.*",
"utopia-php/platform": "0.13.*",
"utopia-php/pools": "1.*",
"utopia-php/span": "1.1.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.17.*",
"utopia-php/servers": "0.3.*",
"utopia-php/queue": "0.18.*",
"utopia-php/servers": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "1.0.*",
"utopia-php/storage": "2.*",
"utopia-php/system": "0.10.*",
"utopia-php/telemetry": "0.2.*",
"utopia-php/vcs": "3.*",
@@ -93,7 +93,7 @@
"chillerlan/php-qrcode": "4.3.*",
"adhocore/jwt": "1.1.*",
"spomky-labs/otphp": "11.*",
"webonyx/graphql-php": "15.31.*",
"webonyx/graphql-php": "15.32.*",
"league/csv": "9.14.*",
"enshrined/svg-sanitize": "0.22.*"
},
Generated
+130 -130
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "805802552f7482eaeae4bdaa505ae982",
"content-hash": "ec2ad489c60f0102f0dfab223b6d1fe4",
"packages": [
{
"name": "adhocore/jwt",
@@ -161,16 +161,16 @@
},
{
"name": "appwrite/php-runtimes",
"version": "0.19.5",
"version": "0.20.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "aa2f7760cd0493c0880209b92df812c9386b3546"
"reference": "7d9b7f4eef5c0a142a60907b06de2219d025c5c3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/aa2f7760cd0493c0880209b92df812c9386b3546",
"reference": "aa2f7760cd0493c0880209b92df812c9386b3546",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/7d9b7f4eef5c0a142a60907b06de2219d025c5c3",
"reference": "7d9b7f4eef5c0a142a60907b06de2219d025c5c3",
"shasum": ""
},
"require": {
@@ -210,9 +210,9 @@
],
"support": {
"issues": "https://github.com/appwrite/runtimes/issues",
"source": "https://github.com/appwrite/runtimes/tree/0.19.5"
"source": "https://github.com/appwrite/runtimes/tree/0.20.0"
},
"time": "2026-04-01T01:39:23+00:00"
"time": "2026-05-01T07:47:07+00:00"
},
{
"name": "brick/math",
@@ -2708,16 +2708,16 @@
},
{
"name": "symfony/http-client",
"version": "v7.4.8",
"version": "v7.4.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "01933e626c3de76bea1e22641e205e78f6a34342"
"reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/01933e626c3de76bea1e22641e205e78f6a34342",
"reference": "01933e626c3de76bea1e22641e205e78f6a34342",
"url": "https://api.github.com/repos/symfony/http-client/zipball/7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6",
"reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6",
"shasum": ""
},
"require": {
@@ -2785,7 +2785,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.4.8"
"source": "https://github.com/symfony/http-client/tree/v7.4.9"
},
"funding": [
{
@@ -2805,7 +2805,7 @@
"type": "tidelift"
}
],
"time": "2026-03-30T12:55:43+00:00"
"time": "2026-04-29T13:25:15+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -3351,16 +3351,16 @@
},
{
"name": "utopia-php/abuse",
"version": "1.2.2",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "20bee84fd14dbe81d50ecabf1ffd81cceca06152"
"reference": "53f4274939353522ba331f55bcff6e6011ffc56c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/20bee84fd14dbe81d50ecabf1ffd81cceca06152",
"reference": "20bee84fd14dbe81d50ecabf1ffd81cceca06152",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/53f4274939353522ba331f55bcff6e6011ffc56c",
"reference": "53f4274939353522ba331f55bcff6e6011ffc56c",
"shasum": ""
},
"require": {
@@ -3397,9 +3397,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/1.2.2"
"source": "https://github.com/utopia-php/abuse/tree/1.2.3"
},
"time": "2026-02-02T10:43:10+00:00"
"time": "2026-04-29T11:19:08+00:00"
},
{
"name": "utopia-php/agents",
@@ -3502,16 +3502,16 @@
},
{
"name": "utopia-php/audit",
"version": "2.2.1",
"version": "2.2.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "e3e2d6ad5c7f6377d9237df296a12eb7943892fd"
"reference": "90886c202e7983999e6b6a8201004d5ab61d4b57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/e3e2d6ad5c7f6377d9237df296a12eb7943892fd",
"reference": "e3e2d6ad5c7f6377d9237df296a12eb7943892fd",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/90886c202e7983999e6b6a8201004d5ab61d4b57",
"reference": "90886c202e7983999e6b6a8201004d5ab61d4b57",
"shasum": ""
},
"require": {
@@ -3545,9 +3545,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/2.2.1"
"source": "https://github.com/utopia-php/audit/tree/2.2.2"
},
"time": "2026-02-02T10:39:25+00:00"
"time": "2026-05-04T06:48:58+00:00"
},
{
"name": "utopia-php/auth",
@@ -3658,21 +3658,21 @@
},
{
"name": "utopia-php/cli",
"version": "0.23.2",
"version": "0.23.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cli.git",
"reference": "145b91fef827853bcceaa3ab8ca2b1d6faaca2ab"
"reference": "3c45ae5bcdcd3c7916e1909d74c60b8e771610db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/145b91fef827853bcceaa3ab8ca2b1d6faaca2ab",
"reference": "145b91fef827853bcceaa3ab8ca2b1d6faaca2ab",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/3c45ae5bcdcd3c7916e1909d74c60b8e771610db",
"reference": "3c45ae5bcdcd3c7916e1909d74c60b8e771610db",
"shasum": ""
},
"require": {
"php": ">=7.4",
"utopia-php/servers": "0.3.*"
"utopia-php/servers": "0.4.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -3703,9 +3703,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cli/issues",
"source": "https://github.com/utopia-php/cli/tree/0.23.2"
"source": "https://github.com/utopia-php/cli/tree/0.23.3"
},
"time": "2026-04-27T09:19:04+00:00"
"time": "2026-05-05T04:38:59+00:00"
},
{
"name": "utopia-php/compression",
@@ -3850,22 +3850,23 @@
},
{
"name": "utopia-php/database",
"version": "5.3.22",
"version": "5.7.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "d765945da6b3141852014b2f96ecf1fe7e3d6ba7"
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/d765945da6b3141852014b2f96ecf1fe7e3d6ba7",
"reference": "d765945da6b3141852014b2f96ecf1fe7e3d6ba7",
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-mongodb": "*",
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.4",
"utopia-php/cache": "1.*",
"utopia-php/console": "0.1.*",
@@ -3903,9 +3904,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/5.3.22"
"source": "https://github.com/utopia-php/database/tree/5.7.0"
},
"time": "2026-04-20T07:12:46+00:00"
"time": "2026-05-06T01:04:08+00:00"
},
{
"name": "utopia-php/detector",
@@ -4062,16 +4063,16 @@
},
{
"name": "utopia-php/domains",
"version": "1.0.5",
"version": "1.0.6",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/domains.git",
"reference": "0edf6bb2b07f30db849a267027077bf5abb994c6"
"reference": "c87ba0a1da4cbf75d2cff9d3ea0262b78f1d86f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/domains/zipball/0edf6bb2b07f30db849a267027077bf5abb994c6",
"reference": "0edf6bb2b07f30db849a267027077bf5abb994c6",
"url": "https://api.github.com/repos/utopia-php/domains/zipball/c87ba0a1da4cbf75d2cff9d3ea0262b78f1d86f6",
"reference": "c87ba0a1da4cbf75d2cff9d3ea0262b78f1d86f6",
"shasum": ""
},
"require": {
@@ -4118,9 +4119,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/domains/issues",
"source": "https://github.com/utopia-php/domains/tree/1.0.5"
"source": "https://github.com/utopia-php/domains/tree/1.0.6"
},
"time": "2026-03-03T09:20:50+00:00"
"time": "2026-04-29T11:08:10+00:00"
},
{
"name": "utopia-php/dsn",
@@ -4271,23 +4272,23 @@
},
{
"name": "utopia-php/http",
"version": "0.34.24",
"version": "0.34.25",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "d1eced0627c5a9fceddf53992ed97d664b810d33"
"reference": "76be330d4197bae680eb4ccc29c573456fe91904"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/d1eced0627c5a9fceddf53992ed97d664b810d33",
"reference": "d1eced0627c5a9fceddf53992ed97d664b810d33",
"url": "https://api.github.com/repos/utopia-php/http/zipball/76be330d4197bae680eb4ccc29c573456fe91904",
"reference": "76be330d4197bae680eb4ccc29c573456fe91904",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/compression": "0.1.*",
"utopia-php/di": "0.3.*",
"utopia-php/servers": "0.3.*",
"utopia-php/servers": "0.4.0",
"utopia-php/telemetry": "0.2.*",
"utopia-php/validators": "0.2.*"
},
@@ -4321,9 +4322,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.34.24"
"source": "https://github.com/utopia-php/http/tree/0.34.25"
},
"time": "2026-04-24T12:16:53+00:00"
"time": "2026-05-05T04:39:15+00:00"
},
{
"name": "utopia-php/image",
@@ -4530,16 +4531,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.9.3",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "111f6221d04578a6f721c23ac872002375f176ae"
"reference": "55f4863d690e775f44fec3cae4bd1f4491fed5ea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/111f6221d04578a6f721c23ac872002375f176ae",
"reference": "111f6221d04578a6f721c23ac872002375f176ae",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/55f4863d690e775f44fec3cae4bd1f4491fed5ea",
"reference": "55f4863d690e775f44fec3cae4bd1f4491fed5ea",
"shasum": ""
},
"require": {
@@ -4550,7 +4551,7 @@
"php": ">=8.1",
"utopia-php/database": "5.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/storage": "1.0.*"
"utopia-php/storage": "2.*"
},
"require-dev": {
"ext-pdo": "*",
@@ -4579,9 +4580,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.9.3"
"source": "https://github.com/utopia-php/migration/tree/1.10.0"
},
"time": "2026-04-22T07:13:26+00:00"
"time": "2026-05-06T04:35:32+00:00"
},
{
"name": "utopia-php/mongo",
@@ -4646,26 +4647,26 @@
},
{
"name": "utopia-php/platform",
"version": "0.13.0",
"version": "0.13.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/platform.git",
"reference": "d23af5349a7ea9ee11f9920a13626226f985522e"
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/d23af5349a7ea9ee11f9920a13626226f985522e",
"reference": "d23af5349a7ea9ee11f9920a13626226f985522e",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-redis": "*",
"php": ">=8.1",
"utopia-php/cli": "0.23.*",
"utopia-php/http": "0.34.*",
"utopia-php/queue": "0.17.*",
"utopia-php/servers": "0.3.*"
"php": ">=8.3",
"utopia-php/cli": "0.23.3",
"utopia-php/http": "0.34.25",
"utopia-php/queue": "0.18.2",
"utopia-php/servers": "0.4.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -4691,9 +4692,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/platform/issues",
"source": "https://github.com/utopia-php/platform/tree/0.13.0"
"source": "https://github.com/utopia-php/platform/tree/0.13.2"
},
"time": "2026-04-17T09:57:18+00:00"
"time": "2026-05-05T06:00:26+00:00"
},
{
"name": "utopia-php/pools",
@@ -4803,25 +4804,24 @@
},
{
"name": "utopia-php/queue",
"version": "0.17.0",
"version": "0.18.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f"
"reference": "f85ca003c99ff475708c05466643d067403c0c22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/0fbc7d7312f5cf76ec112513fb93317000901f5f",
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/f85ca003c99ff475708c05466643d067403c0c22",
"reference": "f85ca003c99ff475708c05466643d067403c0c22",
"shasum": ""
},
"require": {
"php": ">=8.3",
"php-amqplib/php-amqplib": "^3.7",
"utopia-php/di": "0.3.*",
"utopia-php/fetch": "0.5.*",
"utopia-php/pools": "1.*",
"utopia-php/servers": "0.3.*",
"utopia-php/servers": "0.4.0",
"utopia-php/telemetry": "0.2.*",
"utopia-php/validators": "0.2.*"
},
@@ -4864,9 +4864,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.17.0"
"source": "https://github.com/utopia-php/queue/tree/0.18.2"
},
"time": "2026-03-23T16:21:31+00:00"
"time": "2026-05-05T04:38:59+00:00"
},
{
"name": "utopia-php/registry",
@@ -4922,16 +4922,16 @@
},
{
"name": "utopia-php/servers",
"version": "0.3.0",
"version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/servers.git",
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52"
"reference": "7db346ef377503efe0acafe0791085270cd9ed70"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/servers/zipball/235be31200df9437fc96a1c270ffef4c64fafe52",
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52",
"url": "https://api.github.com/repos/utopia-php/servers/zipball/7db346ef377503efe0acafe0791085270cd9ed70",
"reference": "7db346ef377503efe0acafe0791085270cd9ed70",
"shasum": ""
},
"require": {
@@ -4970,9 +4970,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/servers/issues",
"source": "https://github.com/utopia-php/servers/tree/0.3.0"
"source": "https://github.com/utopia-php/servers/tree/0.4.0"
},
"time": "2026-03-13T11:31:42+00:00"
"time": "2026-05-05T04:08:30+00:00"
},
{
"name": "utopia-php/span",
@@ -5020,16 +5020,16 @@
},
{
"name": "utopia-php/storage",
"version": "1.0.1",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "f014be445f0baa635d0764e1673196f412511618"
"reference": "64e132a3768e22243eda36fe4262da22fd204f3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/f014be445f0baa635d0764e1673196f412511618",
"reference": "f014be445f0baa635d0764e1673196f412511618",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/64e132a3768e22243eda36fe4262da22fd204f3c",
"reference": "64e132a3768e22243eda36fe4262da22fd204f3c",
"shasum": ""
},
"require": {
@@ -5043,9 +5043,8 @@
"utopia-php/validators": "0.2.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.0.1"
"laravel/pint": "^1.21",
"phpunit/phpunit": "^9.3"
},
"type": "library",
"autoload": {
@@ -5067,22 +5066,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/1.0.1"
"source": "https://github.com/utopia-php/storage/tree/2.0.2"
},
"time": "2026-02-23T05:59:32+00:00"
"time": "2026-05-01T15:06:16+00:00"
},
{
"name": "utopia-php/system",
"version": "0.10.1",
"version": "0.10.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/system.git",
"reference": "7c1669533bb9c285de19191270c8c1439161a78a"
"reference": "04229a822b147c1abaf1a92fb42c2d7aad4625df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/system/zipball/7c1669533bb9c285de19191270c8c1439161a78a",
"reference": "7c1669533bb9c285de19191270c8c1439161a78a",
"url": "https://api.github.com/repos/utopia-php/system/zipball/04229a822b147c1abaf1a92fb42c2d7aad4625df",
"reference": "04229a822b147c1abaf1a92fb42c2d7aad4625df",
"shasum": ""
},
"require": {
@@ -5123,9 +5122,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/system/issues",
"source": "https://github.com/utopia-php/system/tree/0.10.1"
"source": "https://github.com/utopia-php/system/tree/0.10.2"
},
"time": "2026-03-15T21:07:41+00:00"
"time": "2026-05-05T14:33:41+00:00"
},
{
"name": "utopia-php/telemetry",
@@ -5385,16 +5384,16 @@
},
{
"name": "webonyx/graphql-php",
"version": "v15.31.5",
"version": "v15.32.3",
"source": {
"type": "git",
"url": "https://github.com/webonyx/graphql-php.git",
"reference": "089c4ef7e112df85788cfe06596278a8f99f4aa9"
"reference": "993bf0bea17f870412ad8a90f60c41cb8d5f1145"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webonyx/graphql-php/zipball/089c4ef7e112df85788cfe06596278a8f99f4aa9",
"reference": "089c4ef7e112df85788cfe06596278a8f99f4aa9",
"url": "https://api.github.com/repos/webonyx/graphql-php/zipball/993bf0bea17f870412ad8a90f60c41cb8d5f1145",
"reference": "993bf0bea17f870412ad8a90f60c41cb8d5f1145",
"shasum": ""
},
"require": {
@@ -5403,16 +5402,16 @@
"php": "^7.4 || ^8"
},
"require-dev": {
"amphp/amp": "^2.6",
"amphp/http-server": "^2.1",
"amphp/amp": "^2.6 || ^3",
"amphp/http-server": "^2.1 || ^3",
"dms/phpunit-arraysubset-asserts": "dev-master",
"ergebnis/composer-normalize": "^2.28",
"friendsofphp/php-cs-fixer": "3.94.2",
"friendsofphp/php-cs-fixer": "3.95.1",
"mll-lab/php-cs-fixer-config": "5.13.0",
"nyholm/psr7": "^1.5",
"phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "2.1.46",
"phpstan/phpstan": "2.1.51",
"phpstan/phpstan-phpunit": "2.0.16",
"phpstan/phpstan-strict-rules": "2.0.10",
"phpunit/phpunit": "^9.5 || ^10.5.21 || ^11",
@@ -5426,6 +5425,7 @@
"ticketswap/phpstan-error-formatter": "1.3.0"
},
"suggest": {
"amphp/amp": "To leverage async resolving on AMPHP platform (v3 with AmpFutureAdapter, v2 with AmpPromiseAdapter)",
"amphp/http-server": "To leverage async resolving with webserver on AMPHP platform",
"psr/http-message": "To use standard GraphQL server",
"react/promise": "To leverage async resolving on React PHP platform"
@@ -5448,7 +5448,7 @@
],
"support": {
"issues": "https://github.com/webonyx/graphql-php/issues",
"source": "https://github.com/webonyx/graphql-php/tree/v15.31.5"
"source": "https://github.com/webonyx/graphql-php/tree/v15.32.3"
},
"funding": [
{
@@ -5460,22 +5460,22 @@
"type": "open_collective"
}
],
"time": "2026-04-11T18:06:15+00:00"
"time": "2026-04-24T13:49:35+00:00"
}
],
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.24.0",
"version": "1.27.5",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "6d4d26659bc7a1c347c1d4d8dae3b77b5562e0cb"
"reference": "9faa38b48d422f3da764a719712905c83b3922cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6d4d26659bc7a1c347c1d4d8dae3b77b5562e0cb",
"reference": "6d4d26659bc7a1c347c1d4d8dae3b77b5562e0cb",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9faa38b48d422f3da764a719712905c83b3922cb",
"reference": "9faa38b48d422f3da764a719712905c83b3922cb",
"shasum": ""
},
"require": {
@@ -5511,9 +5511,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/1.24.0"
"source": "https://github.com/appwrite/sdk-generator/tree/1.27.5"
},
"time": "2026-04-24T12:50:05+00:00"
"time": "2026-05-05T12:09:40+00:00"
},
{
"name": "brianium/paratest",
@@ -6222,11 +6222,11 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.51",
"version": "2.1.54",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc3b523c45e714c70de2ac5113b958223b55dc59",
"reference": "dc3b523c45e714c70de2ac5113b958223b55dc59",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
"reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
"shasum": ""
},
"require": {
@@ -6271,7 +6271,7 @@
"type": "github"
}
],
"time": "2026-04-21T18:22:01+00:00"
"time": "2026-04-29T13:31:09+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -6620,16 +6620,16 @@
},
{
"name": "phpunit/phpunit",
"version": "12.5.23",
"version": "12.5.24",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969"
"reference": "d75dd30597caa80e72fad2ef7904601a30ef1046"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969",
"reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d75dd30597caa80e72fad2ef7904601a30ef1046",
"reference": "d75dd30597caa80e72fad2ef7904601a30ef1046",
"shasum": ""
},
"require": {
@@ -6698,7 +6698,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.23"
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.24"
},
"funding": [
{
@@ -6706,7 +6706,7 @@
"type": "other"
}
],
"time": "2026-04-18T06:12:49+00:00"
"time": "2026-05-01T04:21:04+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -7691,16 +7691,16 @@
},
{
"name": "symfony/console",
"version": "v8.0.8",
"version": "v8.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7"
"reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7",
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7",
"url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d",
"reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d",
"shasum": ""
},
"require": {
@@ -7757,7 +7757,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v8.0.8"
"source": "https://github.com/symfony/console/tree/v8.0.9"
},
"funding": [
{
@@ -7777,7 +7777,7 @@
"type": "tidelift"
}
],
"time": "2026-03-30T15:14:47+00:00"
"time": "2026-04-29T15:02:55+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -1 +0,0 @@
List all OAuth2 providers supported by the Appwrite server, along with the parameters required to configure each provider. The response excludes mock providers but includes sandbox providers.
-1
View File
@@ -1 +0,0 @@
Get all Environment Variables that are relevant for the console.
@@ -0,0 +1 @@
Create a bigint attribute. Optionally, minimum and maximum values can be provided.
@@ -0,0 +1 @@
Update a bigint attribute. Changing the `default` value will not update already existing documents.
@@ -0,0 +1 @@
Create a bigint column. Optionally, minimum and maximum values can be provided.
@@ -0,0 +1 @@
Update a bigint column. Changing the `default` value will not update already existing rows.
+5 -3
View File
@@ -105,7 +105,7 @@ class Key
/**
* Decode the given secret key into a Key object, containing the project ID, type, role, scopes, and name.
* Can be a stored API key or a dynamic key (JWT).
* Can be a stored API key or an ephemeral key (JWT).
*
* @throws Exception
*/
@@ -138,7 +138,9 @@ class Key
);
switch ($type) {
case API_KEY_DYNAMIC:
// Dynamic supported for backwards compatibility
case API_KEY_EPHEMERAL:
case 'dynamic':
$jwtObj = new JWT(
key: System::getEnv('_APP_OPENSSL_KEY_V1'),
algo: 'HS256',
@@ -153,7 +155,7 @@ class Key
$expired = true;
}
$name = $payload['name'] ?? 'Dynamic Key';
$name = $payload['name'] ?? 'Ephemeral Key';
$projectId = $payload['projectId'] ?? '';
$disabledMetrics = $payload['disabledMetrics'] ?? [];
$hostnameOverride = $payload['hostnameOverride'] ?? false;
-2
View File
@@ -206,8 +206,6 @@ abstract class OAuth2
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
\curl_close($ch);
if ($code >= 400) {
throw new Exception($response, $code);
}
+7
View File
@@ -37,6 +37,13 @@ class Authentik extends OAuth2
return 'authentik';
}
public function verifyCredentials(): void
{
if (empty($this->getAuthentikDomain())) {
throw new \Exception('Authentik endpoint is required.');
}
}
/**
* @return string
*/
+7
View File
@@ -37,6 +37,13 @@ class FusionAuth extends OAuth2
return 'fusionauth';
}
public function verifyCredentials(): void
{
if (empty($this->getFusionAuthDomain())) {
throw new \Exception('FusionAuth endpoint is required.');
}
}
/**
* @return string
*/
+11
View File
@@ -37,6 +37,17 @@ class Keycloak extends OAuth2
return 'keycloak';
}
public function verifyCredentials(): void
{
if (empty($this->getKeycloakDomain())) {
throw new \Exception('Keycloak endpoint is required.');
}
if (empty($this->getKeycloakRealm())) {
throw new \Exception('Keycloak realm name is required.');
}
}
/**
* @return string
*/
+9 -2
View File
@@ -36,6 +36,13 @@ class Microsoft extends OAuth2
return 'microsoft';
}
public function verifyCredentials(): void
{
if (empty($this->getTenantID())) {
throw new \Exception('Microsoft tenant is required.');
}
}
/**
* @return string
*/
@@ -201,7 +208,7 @@ class Microsoft extends OAuth2
}
/**
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
* Extracts the Tenant Id from the JSON stored in appSecret.
*
* @return string
*/
@@ -209,6 +216,6 @@ class Microsoft extends OAuth2
{
$secret = $this->getAppSecret();
return $secret['tenantID'] ?? 'common';
return $secret['tenantID'] ?? '';
}
}
+2
View File
@@ -178,6 +178,7 @@ class Exception extends \Exception
public const string FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
public const string FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing';
public const string FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout';
public const string FUNCTION_ASYNCHRONOUS_TIMEOUT = 'function_asynchronous_timeout';
public const string FUNCTION_TEMPLATE_NOT_FOUND = 'function_template_not_found';
public const string FUNCTION_RUNTIME_NOT_DETECTED = 'function_runtime_not_detected';
public const string FUNCTION_EXECUTE_PERMISSION_MISSING = 'function_execute_permission_missing';
@@ -192,6 +193,7 @@ class Exception extends \Exception
public const string BUILD_ALREADY_COMPLETED = 'build_already_completed';
public const string BUILD_CANCELED = 'build_canceled';
public const string BUILD_FAILED = 'build_failed';
public const string BUILD_TIMEOUT = 'build_timeout';
/** Execution */
public const string EXECUTION_NOT_FOUND = 'execution_not_found';
+2
View File
@@ -95,6 +95,8 @@ abstract class Migration
'1.9.0' => 'V24',
'1.9.1' => 'V24',
'1.9.2' => 'V24',
'1.9.3' => 'V24',
'1.9.4' => 'V24',
];
/**
+2
View File
@@ -9,6 +9,7 @@ use Appwrite\Platform\Modules\Core;
use Appwrite\Platform\Modules\Databases;
use Appwrite\Platform\Modules\Functions;
use Appwrite\Platform\Modules\Health;
use Appwrite\Platform\Modules\Migrations;
use Appwrite\Platform\Modules\Project;
use Appwrite\Platform\Modules\Projects;
use Appwrite\Platform\Modules\Proxy;
@@ -39,6 +40,7 @@ class Appwrite extends Platform
$this->addModule(new Storage\Module());
$this->addModule(new VCS\Module());
$this->addModule(new Webhooks\Module());
$this->addModule(new Migrations\Module());
$this->addModule(new Project\Module());
}
}
@@ -62,7 +62,6 @@ class Get extends Action
curl_setopt_array($ch, $options);
curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
return $errno === 0;
}
@@ -85,8 +85,6 @@ class Create extends Action
curl_exec($ch);
curl_close($ch);
$response->chunk('', true);
}
}
@@ -34,7 +34,7 @@ class XList extends Action
namespace: 'console',
group: 'console',
name: 'listOAuth2Providers',
description: '/docs/references/console/list-oauth2-providers.md',
description: 'List all OAuth2 providers supported by the Appwrite server, along with the parameters required to configure each provider. The response excludes mock providers but includes sandbox providers.',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
@@ -54,9 +54,9 @@ class XList extends Action
$actions = OAuth2Base::getProviderActions();
$providers = [];
foreach ($actions as $providerId => $updateClass) {
$config = $providersConfig[$providerId] ?? null;
if ($config === null) {
foreach ($providersConfig as $providerId => $config) {
$updateClass = $actions[$providerId] ?? null;
if ($updateClass === null) {
continue;
}
if (!($config['enabled'] ?? false)) {
@@ -0,0 +1,69 @@
<?php
namespace Appwrite\Platform\Modules\Console\Http\Scopes\Key;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
class XList extends Action
{
use HTTP;
public static function getName(): string
{
return 'listConsoleProjectScopes';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
->setHttpPath('/v1/console/scopes/project')
->desc('List project scopes')
->groups(['api'])
->label('scope', 'public')
->label('sdk', new Method(
namespace: 'console',
group: 'console',
name: 'listProjectScopes',
description: 'List all scopes available for project API keys, along with a description for each scope.',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_CONSOLE_KEY_SCOPE_LIST,
)
],
contentType: ContentType::JSON
))
->inject('response')
->callback($this->action(...));
}
public function action(Response $response): void
{
$scopesConfig = Config::getParam('projectScopes', []);
$scopes = [];
foreach ($scopesConfig as $scopeId => $scope) {
$scopes[] = new Document([
'$id' => $scopeId,
'description' => $scope['description'] ?? '',
'category' => $scope['category'] ?? '',
'deprecated' => $scope['deprecated'] ?? false,
]);
}
$response->dynamic(new Document([
'total' => \count($scopes),
'scopes' => $scopes,
]), Response::MODEL_CONSOLE_KEY_SCOPE_LIST);
}
}
@@ -36,7 +36,7 @@ class Get extends Action
namespace: 'console',
group: 'console',
name: 'variables',
description: '/docs/references/console/variables.md',
description: 'Get all Environment Variables that are relevant for the console.',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
@@ -15,6 +15,7 @@ use Appwrite\Platform\Modules\Console\Http\Redirects\Recover\Get as RedirectReco
use Appwrite\Platform\Modules\Console\Http\Redirects\Register\Get as RedirectRegister;
use Appwrite\Platform\Modules\Console\Http\Redirects\Root\Get as RedirectRoot;
use Appwrite\Platform\Modules\Console\Http\Resources\Get as GetResourceAvailability;
use Appwrite\Platform\Modules\Console\Http\Scopes\Key\XList as ListKeyScopes;
use Appwrite\Platform\Modules\Console\Http\Variables\Get as GetVariables;
use Utopia\Platform\Service;
@@ -30,6 +31,7 @@ class Http extends Service
$this->addAction(GetVariables::getName(), new GetVariables());
$this->addAction(ListOAuth2Providers::getName(), new ListOAuth2Providers());
$this->addAction(ListKeyScopes::getName(), new ListKeyScopes());
$this->addAction(CreateAssistantQuery::getName(), new CreateAssistantQuery());
$this->addAction(GetResourceAvailability::getName(), new GetResourceAvailability());
@@ -241,6 +241,10 @@ abstract class Action extends UtopiaAction
? UtopiaResponse::MODEL_ATTRIBUTE_INTEGER
: UtopiaResponse::MODEL_COLUMN_INTEGER,
Database::VAR_BIGINT => $isCollections
? UtopiaResponse::MODEL_ATTRIBUTE_BIGINT
: UtopiaResponse::MODEL_COLUMN_BIGINT,
Database::VAR_FLOAT => $isCollections
? UtopiaResponse::MODEL_ATTRIBUTE_FLOAT
: UtopiaResponse::MODEL_COLUMN_FLOAT,
@@ -540,6 +544,7 @@ abstract class Action extends UtopiaAction
switch ($attribute->getAttribute('format')) {
case APP_DATABASE_ATTRIBUTE_INT_RANGE:
case APP_DATABASE_ATTRIBUTE_BIGINT_RANGE:
case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE:
$min ??= $attribute->getAttribute('formatOptions')['min'];
$max ??= $attribute->getAttribute('formatOptions')['max'];
@@ -548,14 +553,15 @@ abstract class Action extends UtopiaAction
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
}
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) {
$validator = new Range($min, $max, Database::VAR_INTEGER);
} else {
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_FLOAT_RANGE) {
$validator = new Range($min, $max, Database::VAR_FLOAT);
if (!is_null($default)) {
$default = \floatval($default);
}
} else {
// intRange and bigintRange share the same integer range semantics
$validator = new Range($min, $max, Range::TYPE_INTEGER);
}
if (!is_null($default) && !$validator->isValid($default)) {
@@ -0,0 +1,117 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Deprecated;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
class Create extends Action
{
public static function getName(): string
{
return 'createBigIntAttribute';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint')
->desc('Create bigint attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/databases/create-bigint-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: $this->getResponseModel(),
)
],
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'tablesDB.createBigIntColumn',
),
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$min ??= \PHP_INT_MIN;
$max ??= \PHP_INT_MAX;
if ($min > $max) {
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
}
$validator = new Range($min, $max, Range::TYPE_INTEGER);
if (!\is_null($default) && !$validator->isValid($default)) {
throw new Exception($this->getInvalidValueException(), $validator->getDescription());
}
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_BIGINT,
'size' => 8,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$attribute->setAttribute('min', \intval($formatOptions['min']));
$attribute->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($attribute, $this->getResponseModel());
}
}
@@ -0,0 +1,106 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Deprecated;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Update extends Action
{
public static function getName(): string
{
return 'updateBigIntAttribute';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint/:key')
->desc('Update bigint attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/databases/update-bigint-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: $this->getResponseModel(),
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'tablesDB.updateBigIntColumn',
),
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.')
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_BIGINT,
default: $default,
required: $required,
min: $min,
max: $max,
newKey: $newKey
);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$attribute->setAttribute('min', \intval($formatOptions['min']));
$attribute->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($attribute, $this->getResponseModel());
}
}
@@ -290,13 +290,15 @@ class Create extends Action
}
if (isset($attribute['min']) || isset($attribute['max'])) {
$format = $type === Database::VAR_INTEGER
? APP_DATABASE_ATTRIBUTE_INT_RANGE
: APP_DATABASE_ATTRIBUTE_FLOAT_RANGE;
$format = match($type) {
Database::VAR_INTEGER => APP_DATABASE_ATTRIBUTE_INT_RANGE,
Database::VAR_BIGINT => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
default => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE,
};
$formatOptions = [
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
];
}
@@ -0,0 +1,70 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as BigIntCreate;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Create extends BigIntCreate
{
public static function getName(): string
{
return 'createBigIntColumn';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_COLUMN_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint')
->desc('Create bigint column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/tablesdb/create-bigint-column.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: $this->getResponseModel(),
)
]
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
}
@@ -0,0 +1,71 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as BigIntUpdate;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Update extends BigIntUpdate
{
public static function getName(): string
{
return 'updateBigIntColumn';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_COLUMN_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint/:key')
->desc('Update bigint column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/tablesdb/update-bigint-column.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: $this->getResponseModel(),
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.')
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Column Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
}
@@ -34,7 +34,7 @@ class Create extends BooleanCreate
->desc('Create boolean column')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
@@ -34,7 +34,7 @@ class Update extends BooleanUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/boolean/:key')
->desc('Update boolean column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -34,7 +34,7 @@ class Create extends DatetimeCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/datetime')
->desc('Create datetime column')
->groups(['api', 'database'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -35,7 +35,7 @@ class Update extends DatetimeUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/datetime/:key')
->desc('Update dateTime column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -33,7 +33,7 @@ class Delete extends AttributesDelete
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/:key')
->desc('Delete column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.delete')
@@ -34,7 +34,7 @@ class Create extends EmailCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/email')
->desc('Create email column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -35,7 +35,7 @@ class Update extends EmailUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/email/:key')
->desc('Update email column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -35,7 +35,7 @@ class Create extends EnumCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/enum')
->desc('Create enum column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -36,7 +36,7 @@ class Update extends EnumUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/enum/:key')
->desc('Update enum column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -34,7 +34,7 @@ class Create extends FloatCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/float')
->desc('Create float column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -35,7 +35,7 @@ class Update extends FloatUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/float/:key')
->desc('Update float column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -42,7 +42,7 @@ class Get extends AttributesGet
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/:key')
->desc('Get column')
->groups(['api', 'database'])
->label('scope', ['tables.read', 'collections.read'])
->label('scope', ['tables.read', 'collections.read', 'columns.read', 'attributes.read'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
@@ -34,7 +34,7 @@ class Create extends IPCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/ip')
->desc('Create IP address column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -35,7 +35,7 @@ class Update extends IPUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/ip/:key')
->desc('Update IP address column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -34,7 +34,7 @@ class Create extends IntegerCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/integer')
->desc('Create integer column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -35,7 +35,7 @@ class Update extends IntegerUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/integer/:key')
->desc('Update integer column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -35,7 +35,7 @@ class Create extends LineCreate
->desc('Create line column')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
@@ -35,7 +35,7 @@ class Update extends LineUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/line/:key')
->desc('Update line column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -33,7 +33,7 @@ class Create extends LongtextCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/longtext')
->desc('Create longtext column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -34,7 +34,7 @@ class Update extends LongtextUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/longtext/:key')
->desc('Update longtext column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -33,7 +33,7 @@ class Create extends MediumtextCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/mediumtext')
->desc('Create mediumtext column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -34,7 +34,7 @@ class Update extends MediumtextUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/mediumtext/:key')
->desc('Update mediumtext column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -35,7 +35,7 @@ class Create extends PointCreate
->desc('Create point column')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
@@ -35,7 +35,7 @@ class Update extends PointUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/point/:key')
->desc('Update point column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -35,7 +35,7 @@ class Create extends PolygonCreate
->desc('Create polygon column')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
@@ -35,7 +35,7 @@ class Update extends PolygonUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/polygon/:key')
->desc('Update polygon column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -34,7 +34,7 @@ class Create extends RelationshipCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/relationship')
->desc('Create relationship column')
->groups(['api', 'database'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -34,7 +34,7 @@ class Update extends RelationshipUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/:key/relationship')
->desc('Update relationship column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -37,7 +37,7 @@ class Create extends StringCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/string')
->desc('Create string column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -37,7 +37,7 @@ class Update extends StringUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/string/:key')
->desc('Update string column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -33,7 +33,7 @@ class Create extends TextCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/text')
->desc('Create text column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -34,7 +34,7 @@ class Update extends TextUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/text/:key')
->desc('Update text column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -34,7 +34,7 @@ class Create extends URLCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/url')
->desc('Create URL column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -35,7 +35,7 @@ class Update extends URLUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/url/:key')
->desc('Update URL column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -35,7 +35,7 @@ class Create extends VarcharCreate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/varchar')
->desc('Create varchar column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
@@ -36,7 +36,7 @@ class Update extends VarcharUpdate
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/varchar/:key')
->desc('Update varchar column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
@@ -33,7 +33,7 @@ class XList extends AttributesXList
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns')
->desc('List columns')
->groups(['api', 'database'])
->label('scope', ['tables.read', 'collections.read'])
->label('scope', ['tables.read', 'collections.read', 'columns.read', 'attributes.read'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
@@ -37,7 +37,7 @@ class Create extends IndexCreate
->desc('Create index')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create')
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'indexes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'index.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
@@ -36,7 +36,7 @@ class Delete extends IndexDelete
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/indexes/:key')
->desc('Delete index')
->groups(['api', 'database'])
->label('scope', ['tables.write', 'collections.write'])
->label('scope', ['tables.write', 'collections.write', 'indexes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update')
->label('audits.event', 'index.delete')
@@ -32,7 +32,7 @@ class Get extends IndexGet
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/indexes/:key')
->desc('Get index')
->groups(['api', 'database'])
->label('scope', ['tables.read', 'collections.read'])
->label('scope', ['tables.read', 'collections.read', 'indexes.read'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
@@ -33,7 +33,7 @@ class XList extends IndexXList
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/indexes')
->desc('List indexes')
->groups(['api', 'database'])
->label('scope', ['tables.read', 'collections.read'])
->label('scope', ['tables.read', 'collections.read', 'indexes.read'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
@@ -2,6 +2,8 @@
namespace Appwrite\Platform\Modules\Databases\Services\Registry;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as CreateBigIntAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as UpdateBigIntAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Create as CreateBooleanAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Update as UpdateBooleanAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Create as CreateDatetimeAttribute;
@@ -171,6 +173,10 @@ class Legacy extends Base
$service->addAction(CreateIntegerAttribute::getName(), new CreateIntegerAttribute());
$service->addAction(UpdateIntegerAttribute::getName(), new UpdateIntegerAttribute());
// Attribute: BigInt
$service->addAction(CreateBigIntAttribute::getName(), new CreateBigIntAttribute());
$service->addAction(UpdateBigIntAttribute::getName(), new UpdateBigIntAttribute());
// Attribute: IP
$service->addAction(CreateIPAttribute::getName(), new CreateIPAttribute());
$service->addAction(UpdateIPAttribute::getName(), new UpdateIPAttribute());
@@ -5,6 +5,8 @@ namespace Appwrite\Platform\Modules\Databases\Services\Registry;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Create as CreateTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Delete as DeleteTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Get as GetTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Create as CreateBigInt;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Update as UpdateBigInt;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Create as CreateBoolean;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Update as UpdateBoolean;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Datetime\Create as CreateDatetime;
@@ -151,6 +153,10 @@ class TablesDB extends Base
$service->addAction(CreateInteger::getName(), new CreateInteger());
$service->addAction(UpdateInteger::getName(), new UpdateInteger());
// Column: BigInt
$service->addAction(CreateBigInt::getName(), new CreateBigInt());
$service->addAction(UpdateBigInt::getName(), new UpdateBigInt());
// Column: IP
$service->addAction(CreateIP::getName(), new CreateIP());
$service->addAction(UpdateIP::getName(), new UpdateIP());
@@ -175,15 +175,8 @@ class Create extends Action
throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
}
// TODO remove the condition that checks `$end === $fileSize` in next breaking version
if ($end === $fileSize - 1 || $end === $fileSize) {
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to notify it's last chunk
$chunks = $chunk = -1;
} else {
// Calculate total number of chunks based on the chunk size i.e ($rangeEnd - $rangeStart)
$chunks = (int) ceil($fileSize / ($end + 1 - $start));
$chunk = (int) ($start / ($end + 1 - $start)) + 1;
}
$chunks = (int) ceil($fileSize / APP_LIMIT_UPLOAD_CHUNK_SIZE);
$chunk = (int) ($start / APP_LIMIT_UPLOAD_CHUNK_SIZE) + 1;
}
if (!$fileSizeValidator->isValid($fileSize) && $functionSizeLimit !== 0) { // Check if file size is exceeding allowed limit
@@ -202,15 +195,14 @@ class Create extends Action
$metadata = ['content_type' => $deviceForLocal->getFileMimeType($fileTmpName)];
if (!$deployment->isEmpty()) {
$chunks = $deployment->getAttribute('sourceChunksTotal', 1);
$uploaded = $deployment->getAttribute('sourceChunksUploaded', 0);
$metadata = $deployment->getAttribute('sourceMetadata', []);
if ($chunk === -1) {
$chunk = $chunks;
}
} else {
// Guard against manually setting range header for single chunk upload
if ($chunks === -1) {
$chunks = 1;
$chunk = 1;
if ($uploaded === $chunks) {
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
return;
}
}
@@ -258,6 +250,8 @@ class Create extends Action
'sourcePath' => $path,
'sourceSize' => $fileSize,
'totalSize' => $fileSize,
'sourceChunksTotal' => $chunks,
'sourceChunksUploaded' => $chunksUploaded,
'activate' => $activate,
'sourceMetadata' => $metadata,
'type' => $type
@@ -272,6 +266,7 @@ class Create extends Action
} else {
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, new Document([
'sourceSize' => $fileSize,
'sourceChunksUploaded' => $chunksUploaded,
'sourceMetadata' => $metadata,
]));
}

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