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>
The subQueryPlatforms database filter loads platforms as a sub-attribute
when project documents are fetched. Old platform type values stored in
the database (e.g. flutter-android, flutter-ios) were not being mapped
to the new consolidated types before being included in the project
response sent to the frontend/console.
This adds Platform::mapDeprecatedType() to the filter so all platforms
returned as part of a project document have their types mapped
consistently, complementing the existing mapping in the dedicated
platform Get and List endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Old platform types stored in the database (e.g. flutter-web, apple-ios,
react-native-android) are now mapped to the new consolidated types (web,
apple, android, windows, linux) before being sent in API responses. This
ensures the response models' $conditions correctly select the right model
for each platform document.
Adds Platform::mapDeprecatedType() as a reusable static method and applies
the mapping in both Get and XList platform endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Platform::getHostnames() and Platform::getSchemes() only handled the new
consolidated type names, causing "invalid origin" errors for projects still
using old granular types (flutter-*, apple-*, react-native-*, unity) stored
in the database. Add switch fall-through cases for all deprecated type values
and a V24 migration to convert old types to their new equivalents.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>