fix(rcal): add post-load migration for tags field on existing events
Automerge docs created before the tags schema don't have the field. The migration runs 5s after startup (after loadAllDocs completes), patches missing tags onto events, and assigns known demo event tags. Also removes debug endpoint and tracing logs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9ae88e14b7
commit
d8736ba341
|
|
@ -275,10 +275,7 @@ function seedDemoIfEmpty(space: string) {
|
||||||
if (d.meta) (d.meta as any).seeded = true;
|
if (d.meta) (d.meta as any).seeded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verify tags survived Automerge change
|
console.log("[Cal] Demo data seeded: 2 sources, 7 events");
|
||||||
const verifyDoc = _syncServer!.getDoc<CalendarDoc>(docId);
|
|
||||||
const firstEv = verifyDoc ? Object.values(verifyDoc.events)[0] : null;
|
|
||||||
console.log(`[Cal] Demo data seeded: 2 sources, 7 events — tags: ${JSON.stringify(firstEv?.tags)}, has_key: ${'tags' in (firstEv || {})}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── API: Events ──
|
// ── API: Events ──
|
||||||
|
|
@ -340,25 +337,6 @@ routes.get("/api/events", async (c) => {
|
||||||
return c.json({ count: rows.length, results: rows });
|
return c.json({ count: rows.length, results: rows });
|
||||||
});
|
});
|
||||||
|
|
||||||
// DEBUG: raw Automerge event data (temporary)
|
|
||||||
routes.get("/api/events-debug", async (c) => {
|
|
||||||
const space = c.req.param("space") || "demo";
|
|
||||||
const dataSpace = c.get("effectiveSpace") || space;
|
|
||||||
const doc = ensureDoc(dataSpace);
|
|
||||||
const events = Object.values(doc.events).slice(0, 3);
|
|
||||||
const raw = events.map((e) => ({
|
|
||||||
title: e.title,
|
|
||||||
tags_value: e.tags,
|
|
||||||
tags_type: typeof e.tags,
|
|
||||||
tags_truthy: !!e.tags,
|
|
||||||
tags_isArray: Array.isArray(e.tags),
|
|
||||||
tags_json: JSON.stringify(e.tags),
|
|
||||||
tags_arrayFrom: e.tags ? Array.from(e.tags) : 'WAS_FALSY',
|
|
||||||
has_tags_key: 'tags' in e,
|
|
||||||
all_keys: Object.keys(e),
|
|
||||||
}));
|
|
||||||
return c.json(raw);
|
|
||||||
});
|
|
||||||
|
|
||||||
// POST /api/events — create event
|
// POST /api/events — create event
|
||||||
routes.post("/api/events", async (c) => {
|
routes.post("/api/events", async (c) => {
|
||||||
|
|
@ -1054,6 +1032,41 @@ export function getUpcomingEventsForMI(space: string, days = 14, limit = 5): MIC
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate existing events: add `tags` field where missing.
|
||||||
|
* Automerge docs created before the tags schema update won't have the key.
|
||||||
|
* This runs on every startup — it's a no-op if tags already exist.
|
||||||
|
*/
|
||||||
|
function migrateTagsField(space: string) {
|
||||||
|
if (!_syncServer) return;
|
||||||
|
const docId = calendarDocId(space);
|
||||||
|
const doc = _syncServer.getDoc<CalendarDoc>(docId);
|
||||||
|
if (!doc) return;
|
||||||
|
|
||||||
|
const needsMigration = Object.values(doc.events).some((e) => !('tags' in e));
|
||||||
|
if (!needsMigration) return;
|
||||||
|
|
||||||
|
// Tag assignment for known demo events
|
||||||
|
const demoTags: Record<string, string[]> = {
|
||||||
|
"rSpace Launch Party": ["launch", "community"],
|
||||||
|
"Provider Onboarding Workshop": ["onboarding", "cosmolocal"],
|
||||||
|
"Weekly Community Standup": ["recurring", "community"],
|
||||||
|
"Sprint: Module Seeding & Polish": ["sprint", "dev"],
|
||||||
|
"rFlows Budget Review": ["governance", "finance"],
|
||||||
|
"Cosmolocal Design Sprint": ["sprint", "design", "travel"],
|
||||||
|
"Q1 Retrospective": ["recurring", "community", "retrospective"],
|
||||||
|
};
|
||||||
|
|
||||||
|
_syncServer.changeDoc<CalendarDoc>(docId, 'migrate: add tags field', (d) => {
|
||||||
|
for (const ev of Object.values(d.events)) {
|
||||||
|
if (!('tags' in ev)) {
|
||||||
|
(ev as any).tags = demoTags[ev.title] || null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(`[Cal] Migrated tags field for ${space}`);
|
||||||
|
}
|
||||||
|
|
||||||
export const calModule: RSpaceModule = {
|
export const calModule: RSpaceModule = {
|
||||||
id: "rcal",
|
id: "rcal",
|
||||||
name: "rCal",
|
name: "rCal",
|
||||||
|
|
@ -1069,6 +1082,16 @@ export const calModule: RSpaceModule = {
|
||||||
_syncServer = ctx.syncServer;
|
_syncServer = ctx.syncServer;
|
||||||
// Seed demo data for the default space
|
// Seed demo data for the default space
|
||||||
seedDemoIfEmpty("demo");
|
seedDemoIfEmpty("demo");
|
||||||
|
|
||||||
|
// Migrate tags field for all spaces that have cal docs (runs after loadAllDocs via setTimeout)
|
||||||
|
setTimeout(() => {
|
||||||
|
for (const docId of _syncServer!.listDocs()) {
|
||||||
|
if (docId.endsWith(':cal:events')) {
|
||||||
|
const space = docId.split(':')[0];
|
||||||
|
migrateTagsField(space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 5000); // Run after loadAllDocs completes
|
||||||
},
|
},
|
||||||
feeds: [
|
feeds: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -186,14 +186,6 @@ async function scanDir(dir: string, syncServer: SyncServer): Promise<number> {
|
||||||
|
|
||||||
const doc = Automerge.load(bytes);
|
const doc = Automerge.load(bytes);
|
||||||
const docId = pathToDocId(fullPath);
|
const docId = pathToDocId(fullPath);
|
||||||
// Debug: check cal doc tags during load
|
|
||||||
if (docId.includes(':cal:events')) {
|
|
||||||
const events = (doc as any).events;
|
|
||||||
if (events) {
|
|
||||||
const first = Object.values(events)[0] as any;
|
|
||||||
console.log(`[DocStore] Loading ${docId}: tags=${JSON.stringify(first?.tags)}, has_tags=${'tags' in (first || {})}, keys=${Object.keys(first || {}).filter((k: string) => k.startsWith('t'))}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syncServer.setDoc(docId, doc);
|
syncServer.setDoc(docId, doc);
|
||||||
count++;
|
count++;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue