Three bugs causing storage preview cache to be ineffective:
1. Cache keys included the `token` auth parameter, so requests using
resource tokens always generated unique keys and never hit cache.
Introduced `cache.params` label for routes to opt-in specific params
into the cache key; preview now declares only the transform params.
2. Cache hits never refreshed `accessedAt` in the DB or the filesystem
file mtime, because `$response->send()` in the init hook skips the
shutdown hook. After 30 days the maintenance job evicted still-active
cache entries, and after the original 30-day filesystem TTL the cache
file expired — causing periodic full re-renders. The cache-hit path
now updates both on the APP_CACHE_UPDATE (24h) interval.
3. `updateDocument` in the preview action passed the full file document
instead of a sparse one when updating `transformedAt`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SessionCreated event now carries only domain data (no isFirstSession)
- Mails listener uses ordered guard clauses, deferring the DB query
until cheaper checks pass
- Drop $user Document allocation in favour of direct array access
- Inline FileName validator and $smtpEnabled into their use sites
- Extract $isBranded to eliminate duplicate APP_BRANDED_EMAIL_BASE_TEMPLATE check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves session alert email side effect out of the account controller
into a dedicated `Mails` listener that reacts to a new `SessionCreated`
bus event. The event is now always dispatched on session creation; the
listener owns all conditional logic (first session, sessionAlerts flag,
email-link sessions, user email presence).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swap the order of createDocument('sessions') and purgeCachedDocument('users')
in the email/password session creation flow. Previously, the cache was purged
before the session was written, opening a race window in Swoole's async
environment where a concurrent account.get() could re-cache the user with no
sessions, causing sessionVerify to fail with a 401. This matches the correct
ordering already used by the token-based flows (magic URL, OTP, phone).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>