[pull] main from appwrite:main - #270
Merged
Merged
Conversation
- Store the photo ID in users.avatar instead of the full path - Rename avatars collection to photos and drop its path attribute - Rename Avatar response model to Photo - Lock uploads per upload ID instead of per user; drop the lock from delete - Generate the chunked test payload instead of shipping a 17 MB fixture Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Lock per user around chunks, activation and deletion via the lock pool - Validate Content-Range shape and actual chunk size against the range - Accept only PNG, JPEG, GIF and WebP by content - Delete storage before records so failed deletes stay retriable - Retry activation when a completed upload was never made active - Audit photo deletion against the session user - Store photos under _photos/ so bucket deletion can't remove them - Test the served image instead of upload metadata Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replace the users.avatar pointer with a users.photos subquery attribute that returns the newest completed photo, cached with the user document. Photo rows are the only state; Update and Delete purge the user cache after changing them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rename users.photos to users.photo and return the live photo with findOne instead of a one-item list. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Drop chunked uploads: a photo is one request of up to one upload chunk (5MB), validated by content before it is stored. Every photos row is now a finished photo, so the chunk counters, upload metadata and the per-user lock go away; each upload removes only older photos, which lets concurrent uploads settle on the newest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…fore aggregating Execution reads resolve the latest version of each execution with argMax, grouped by id. Only the resource filters ran before that grouping, so a listing for one hour of a busy function still grouped the function's whole history, document included, before discarding everything outside the hour. A burst of such listings exhausted a regional ClickHouse's memory and failed every other query on the node with it. A $createdAt range now also selects, before aggregation, the executions that have at least one version inside it. Versions themselves are not filtered by it: executions queued through the API get a later createdAt on the versions the functions worker writes, so filtering versions could leave an older one as the latest and return a stale status or a deleted execution.
Replace the photos collection and its subquery with photoId and photoSize attributes on users, drop the stored mime type, and accept PNG and JPEG only. Files live in a per-user folder that user deletion removes whole, which also clears anything a racing request left behind. Update now returns the user. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Move photo file deletion out of the provider and into the endpoints and the Deletes worker. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replace the provider's getPath() with APP_STORAGE_PHOTOS and build the path where it is needed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The unit test matched the whole generated SQL with regular expressions, which AGENTS.md rules out and which could not tell whether the returned executions are right. Keep it to plain checks that the window adds the id pre-filter and never filters versions by createdAt, and add an e2e test that lists a sync and an async execution through windows around their createdAt values, including the async execution's queued createdAt, which must never return the stale queued version.
- Serialise photo updates and deletes per user, so the replaced photo is always the one whose file is removed - Return MODEL_ACCOUNT instead of MODEL_USER, which exposed the password hash - Accept WebP alongside PNG and JPEG - Check that JPEG and WebP uploads are served Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…g windows Sync executions also reach the store through the executions queue. The test read the sync execution's createdAt without checking it was stored yet, so under parallel CI load the covering window could be built from a missing value and see only the async execution.
Only remove the uploaded file when the user was not switched to it yet; a failure after the switch is logged and the request succeeds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The lock added more code than the race it prevents is worth. A file left behind by concurrent requests is removed with the user's photo folder when the user is deleted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The async create response returns $createdAt in the database format (2026-09-29 12:46:25.848) while reads return ISO 8601. Taking min() of the two as strings sorts the space before the T, so the window started at the later queued time and left out the sync execution. Normalise every timestamp to ISO first, and drop the diagnostic message that found this.
…-pushdown fix(executions): narrow listings to the requested createdAt window before aggregating
Adds optional bool param current (default true) to DELETE /account/sessions. When false, all sessions except the calling session are deleted, allowing sign-out everywhere while staying signed in on the current device. Uses the session resource from #13900 so JWT auth works correctly for both cookie and JWT authenticated callers. X-Fallback-Cookies header is now only sent when current=true to avoid wiping the caller's stored session cookie when signing out other sessions. E2E tests cover cookie and JWT auth paths with three-session setup per spec: other two sessions get 401, calling session stays 200, listSessions returns exactly one session marked current.
Swoole drops a multipart request without a boundary before it reaches the app, so the test got a bare 400 instead of the missing-file error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Photo updates and deletes are guarded by the user's update timestamp, so a concurrent upload is never cleared by a delete and a replaced photo is never left behind. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
feat: custom user avatar upload and delete
…-param-fresh feat: add current param to deleteSessions endpoint
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User JWTs now carry a projectId claim, and the HTTP and realtime user resources reject a JWT minted for another project. Tokens minted before the claim existed are still accepted when they name a session, since the session ID is server-generated and exists only in the minting project. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…only The X-Appwrite-Organization header belongs to the Organization API, which is where the console SDK sends it. The console team resource resolved it for every console route; it now does so on organization routes only, so every other console route keeps naming its own team. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Organization module already declares its routes with the organization group, and its init hook requires the team on that same group. Resolve the header by the group rather than by path prefix, so the two sides of the contract read the same declaration. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Bind user JWTs to the project that issued them
fix(console): resolve the organization header on organization routes only
Move the public-domain and public-address checks for the image, favicon and screenshots endpoints into a PublicURL param validator, reject screenshot headers that target cloud metadata services, and give the browser service its own network. Co-authored-by: Cursor <cursoragent@cursor.com>
The chunked-upload branch of the function and site deployment endpoints read the x-appwrite-id header straight into the on-disk deployment name without validating it. A `..` value escapes the per-project storage root (Device::getAbsolutePath collapses `..` lexically with no containment check), letting any key with functions.write/sites.write write into other tenants' storage and overwrite their deployment archives. The escaped path is also persisted as the deployment's sourcePath, which the deletes worker later trusts, turning a normal delete into a cross-project file delete (CWE-22). Storage file uploads already UID-validate the same header; the two deployment endpoints did not. Apply the identical check right after the content-range parse, rejecting invalid ids with STORAGE_INVALID_APPWRITE_ID.
…versal fix(deployments): validate x-appwrite-id to prevent path traversal
The browser service can no longer resolve internal Docker service names, so a user-supplied screenshot URL cannot target internal containers by name even if it reaches the browser. Co-authored-by: Cursor <cursoragent@cursor.com>
fix(avatars): validate remote URLs with a public URL param validator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )