fix(demo): seal the baked insights cache and carry it across a migration

The demo seed wrote the cached insights payload into the readable column,
and the edge migration copied only that column, so a migrated demo lost
any sealed payload. The seed now seals it the way the app does and the
migration copies insights_cached_text_encrypted beside it.
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 09:46:48 +02:00
1 parent f0c20151a9
commit b402c65f6a
2 files changed
+14 -5

No files matched your search

+4 -2
View File
@@ -329,7 +329,8 @@ readonly USERS_COLS="id, username, email, password_hash, role, created_at, updat
height_cm, date_of_birth, gender, timezone,
codex_access_token_encrypted, codex_refresh_token_encrypted, codex_token_expires_at,
codex_connected_at, codex_connection_status,
insights_privacy_mode, insights_cached_at, insights_cached_text, insights_exclude_metrics,
insights_privacy_mode, insights_cached_at, insights_cached_text, insights_cached_text_encrypted,
insights_exclude_metrics,
global_excluded_injection_sites,
telegram_bot_token, telegram_chat_id, telegram_enabled,
withings_client_id_encrypted, withings_client_secret_encrypted,
@@ -361,7 +362,8 @@ readonly USERS_SELECT="id, username, email, password_hash,
NULL::timestamptz AS codex_token_expires_at,
NULL::timestamptz AS codex_connected_at,
'disconnected' AS codex_connection_status,
insights_privacy_mode, insights_cached_at, insights_cached_text, insights_exclude_metrics,
insights_privacy_mode, insights_cached_at, insights_cached_text, insights_cached_text_encrypted,
insights_exclude_metrics,
global_excluded_injection_sites,
NULL::text AS telegram_bot_token,
NULL::text AS telegram_chat_id,
+10 -3
View File
@@ -88,6 +88,7 @@ import {
getKeyFingerprint,
} from "../src/lib/crypto";
import { ENVIRONMENT_LOCATION_AAD } from "../src/lib/crypto/encrypted-columns";
import { sealInsightsCachedText } from "../src/lib/insights/cached-text-cipher";
import {
DEFAULT_DASHBOARD_LAYOUT,
serializeDashboardLayout,
@@ -3343,7 +3344,8 @@ async function seed() {
const bakedProvider = "anthropic";
// ── Comprehensive insight + daily briefing ──
// Stored on users.insights_cached_text (JSON). The GET read parses it and
// Stored sealed on users.insights_cached_text_encrypted (JSON). The GET
// read opens and parses it and
// returns it as-is; setting insights_cached_at = NOW() keeps it inside the
// 24 h freshness window for the dashboard-snapshot briefing path. The shape
// is the union of insightResultSchema (summary / findings / correlations /
@@ -3527,14 +3529,19 @@ async function seed() {
await client.query(
`UPDATE users
SET insights_cached_text = $2,
SET insights_cached_text_encrypted = $2,
insights_cached_text = NULL,
insights_cached_at = NOW(),
insights_snapshot_hash = $3,
insights_briefing_reroll_date = $4
WHERE id = $1`,
[
userId,
JSON.stringify(comprehensiveInsight),
// Sealed like every write since v1.43.7; the app never reads the
// readable column while a ciphertext is there.
Buffer.from(
sealInsightsCachedText(JSON.stringify(comprehensiveInsight)),
),
// A stable fingerprint. The 24 h cache window keeps the row fresh on
// its own; this value just has to be present + non-null so a
// hypothetical regeneration would treat the snapshot as known. The