- Remove cors from inject chain; resolve via getResource() inside
try-catch so DB failures don't cascade when resolving the cors
resource dependency chain (cors -> allowedHostnames -> rule -> DB)
- Use override:true on addHeader to prevent duplicate CORS headers
when init() already set them before the exception was thrown
- Degrades gracefully: if cors resolution fails, error response is
sent without CORS headers (same behavior as before this PR)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Http::error() handler was missing CORS headers, causing browsers to
block error responses (e.g. 403 PROJECT_PAUSED) with a generic CORS
error instead of showing the actual error message. This injects the cors
resource into the error handler and adds CORS headers before sending the
error response, matching the pattern already used in Http::init().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove team deletion for sole owner+sole member case; let orphan teams
be cleaned up by Cloud's inactive project cleanup (safer, avoids
accidental data loss)
- Add explicit ordering by $createdAt so the most veteran member gets
ownership transfer, with limit(1) for clarity
- Remove confirm filter on primary user transfer in membership deletion
so all members (including unconfirmed) are considered
- Remove redundant ownership transfer from Deletes worker since the API
controller already handles it before queueing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent unconfirmed (pending invite) members from being promoted to
owner or set as the team's primary user during membership/account
deletion by adding a Query::equal('confirm', [true]) filter to the
relevant findOne queries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of blocking account deletion when the user has confirmed team
memberships, handle memberships gracefully during deletion:
- Sole owner + sole member: delete the team and queue project cleanup
- Sole owner + other members: transfer ownership to the next member
- Non-owner / multiple owners: no special handling needed (worker cleans up)
Also update the Deletes worker to transfer the team's primary user
reference when removing a deleted user's memberships.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The users.php and messaging.php legacy controllers were missing the
userType field in their log output, creating an inconsistency with the
new audit user type distinction feature. Also adds missing mode field
to users.php logs endpoint.
https://claude.ai/code/session_01J9gKXwbHoLggsGwJi6KUnM
addHeader() already accumulates multiple values for the same key into an
array internally, so calling it once per value is the correct approach.
Comma-joining violates RFC 6265 for Set-Cookie headers.
- Add getDatabaseResourceType() helper to map database types to resource constants
- Use database-specific resourceType for CSV/JSON import/export instead of hardcoded TYPE_DATABASE
- Skip attribute validation for schemaless databases (DocumentsDB/VectorsDB) in exports
- Parse JSON export queries in migration worker
- Restore MigrationsBase from 1.9.x and append VectorsDB/DocumentsDB E2E tests
- general.php: add instanceof guard in error handler to prevent calling
isPrivileged() on a plain Document if getResource('user') returns
an unexpected type
- graphql.php: add setUser() calls on request/response in graphql group
init so sensitive field filtering works correctly for GraphQL routes
- api.php: fix session group init type hint from Document to User for
consistency with all other init blocks
https://claude.ai/code/session_01JLPDurUgyj7qViA8JqQFTH
The error handler in general.php was calling User::isPrivileged()
statically, but the method was converted to an instance method.
This caused a fatal error on every request.
https://claude.ai/code/session_01JLPDurUgyj7qViA8JqQFTH
All call sites now use $user->isApp() and $user->isPrivileged() instance
syntax instead of static User::isApp() / $user::isPrivileged() calls.
Added setUser() to Request class for consistency with Response.
https://claude.ai/code/session_01JLPDurUgyj7qViA8JqQFTH