import { db } from './db.js' import { getActiveMasterKey } from './auth.js' import { decryptJson, encryptJson } from './crypto.js' import { getLogbookKey } from './logbookKeys.js' import type { PersonData } from '../types/person.js' import { buildLogbookCrewSelection, pickActiveSkipperId } from '../utils/personSnapshots.js' import { entryCrewFromLogbookSelection } from '../utils/personSnapshots.js' import { saveLogbookCrewSelection } from './logbookCrewSelection.js' const MIGRATION_FLAG = 'crew_pool_migration_v1_done' export async function migrateLegacyCrewToPoolIfNeeded(): Promise { const userId = localStorage.getItem('active_userid') if (!userId || localStorage.getItem(MIGRATION_FLAG) === userId) return const masterKey = getActiveMasterKey() if (!masterKey) return try { const ownedLogbooks = await db.logbooks.filter((lb) => lb.isShared !== 1).toArray() const poolByLegacyKey = new Map() const poolData = new Map() for (const logbook of ownedLogbooks) { const logbookKey = (await getLogbookKey(logbook.id)) || masterKey const legacyCrews = await db.crews.where({ logbookId: logbook.id }).toArray() const legacyIds: { skipperIds: string[]; crewIds: string[] } = { skipperIds: [], crewIds: [] } for (const record of legacyCrews) { const data = (await decryptJson(record.encryptedData, record.iv, record.tag, logbookKey)) as | PersonData | null if (!data) continue const role = record.payloadId === 'skipper' ? 'skipper' : 'crew' const personData: PersonData = { ...data, role } const dedupeKey = `${role}:${personData.name}:${personData.passportNumber}` let poolId = poolByLegacyKey.get(dedupeKey) if (!poolId) { poolId = record.payloadId === 'skipper' ? 'skipper' : record.payloadId const existing = await db.personPool.get(poolId) if (!existing) { const encrypted = await encryptJson(personData, masterKey) const now = new Date().toISOString() await db.personPool.put({ payloadId: poolId, encryptedData: encrypted.ciphertext, iv: encrypted.iv, tag: encrypted.tag, updatedAt: now }) await db.userSyncQueue.put({ action: 'create', type: 'person', payloadId: poolId, data: JSON.stringify(encrypted), updatedAt: now }) } poolByLegacyKey.set(dedupeKey, poolId) poolData.set(poolId, personData) } if (role === 'skipper') { if (!legacyIds.skipperIds.includes(poolId)) legacyIds.skipperIds.push(poolId) } else { legacyIds.crewIds.push(poolId) } } const activeSkipperId = pickActiveSkipperId(legacyIds.skipperIds) const existingSelection = await db.logbookCrewSelections.get(logbook.id) if (!existingSelection && (activeSkipperId || legacyIds.crewIds.length > 0)) { const selection = buildLogbookCrewSelection( activeSkipperId, legacyIds.crewIds, poolData ) await saveLogbookCrewSelection(logbook.id, selection) const entryCrew = entryCrewFromLogbookSelection(selection) const entries = await db.entries.where({ logbookId: logbook.id }).toArray() for (const entry of entries) { const dec = (await decryptJson(entry.encryptedData, entry.iv, entry.tag, logbookKey)) as Record< string, unknown > | null if (!dec) continue if (dec.selectedSkipperId != null || (Array.isArray(dec.selectedCrewIds) && dec.selectedCrewIds.length > 0)) { continue } const updated = { ...dec, ...entryCrew } const encrypted = await encryptJson(updated, logbookKey) const now = new Date().toISOString() await db.entries.put({ ...entry, encryptedData: encrypted.ciphertext, iv: encrypted.iv, tag: encrypted.tag, updatedAt: now }) await db.syncQueue.put({ action: 'update', type: 'entry', payloadId: entry.payloadId, logbookId: logbook.id, data: JSON.stringify(encrypted), updatedAt: now }) } } } localStorage.setItem(MIGRATION_FLAG, userId) } catch (err) { console.warn('Crew pool migration failed:', err) } }