fix: case-sensitive label matching broken on PostgreSQL
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 42s

syncLabels created Labels with original case (e.g. "Projet") but
searched with .toLowerCase() (e.g. "projet"). On PostgreSQL (case-
sensitive), findMany returned [] → labelRelations disconnected →
orphan cleanup deleted ALL notebook labels.

Fix: use Prisma mode:'insensitive' for findFirst and findMany,
deduplicate case-insensitively while preserving original case.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 00:36:03 +02:00
parent fb996e22ac
commit 6e8abc5091

View File

@@ -107,12 +107,22 @@ async function syncLabels(userId: string, noteLabels: string[] = [], notebookId?
const nbScope = notebookId ?? null const nbScope = notebookId ?? null
if (noteLabels.length > 0) { if (noteLabels.length > 0) {
const trimmedNames = [...new Set( // Deduplicate case-insensitively, keep original case
noteLabels.map(name => name?.trim()).filter((n): n is string => Boolean(n)) const seen = new Set<string>()
)] const trimmedNames = noteLabels
.map(name => name?.trim())
.filter((n): n is string => Boolean(n))
.filter(n => {
const key = n.toLowerCase()
if (seen.has(key)) return false
seen.add(key)
return true
})
for (const name of trimmedNames) { for (const name of trimmedNames) {
// Case-insensitive find on PostgreSQL
const existing = await prisma.label.findFirst({ const existing = await prisma.label.findFirst({
where: { userId, name, notebookId: nbScope }, where: { userId, name: { equals: name, mode: 'insensitive' }, notebookId: nbScope },
}) })
if (!existing) { if (!existing) {
await prisma.label.create({ await prisma.label.create({
@@ -120,14 +130,16 @@ async function syncLabels(userId: string, noteLabels: string[] = [], notebookId?
}) })
} }
} }
if (trimmedNames.length === 0) return []
// Search with original case (case-insensitive on PostgreSQL)
return prisma.label.findMany({
where: { userId, notebookId: nbScope, name: { in: trimmedNames, mode: 'insensitive' } },
select: { id: true, name: true },
})
} }
if (noteLabels.length === 0) return [] return []
const uniqueNames = [...new Set(noteLabels.map(n => n.trim().toLowerCase()).filter(Boolean))]
return prisma.label.findMany({
where: { userId, notebookId: nbScope, name: { in: uniqueNames } },
select: { id: true, name: true },
})
} catch (error) { } catch (error) {
console.error('Fatal error in syncLabels:', error) console.error('Fatal error in syncLabels:', error)
return [] return []