047a5b1bdb
Prefer canonical skipper id and stop overwriting activeSkipperId during legacy crew migration and read-only share conversion. Co-authored-by: Cursor <cursoragent@cursor.com>
126 lines
4.6 KiB
TypeScript
126 lines
4.6 KiB
TypeScript
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<void> {
|
|
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<string, string>()
|
|
const poolData = new Map<string, PersonData>()
|
|
|
|
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)
|
|
}
|
|
}
|