fix(dev): Prisma db push beim Start und sichere Vessel-Pool-Sync-Abfragen
start-dev.sh synchronisiert Schema vor dem Backend; Sync/Collaboration liefern bei fehlenden Tabellen null statt 500. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -159,6 +159,29 @@ else
|
|||||||
echo "Warning: Docker command not found. Skipping PostgreSQL container management."
|
echo "Warning: Docker command not found. Skipping PostgreSQL container management."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Sync Prisma client and database schema (dev)
|
||||||
|
sync_prisma_schema() {
|
||||||
|
local server_dir="$REPO_ROOT/server"
|
||||||
|
if [ ! -f "$server_dir/prisma/schema.prisma" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ ! -d "$server_dir/node_modules" ]; then
|
||||||
|
echo "Warning: server/node_modules missing — skipping Prisma sync. Run: cd server && npm ci"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "Syncing Prisma client and database schema..."
|
||||||
|
(
|
||||||
|
cd "$server_dir" || exit 1
|
||||||
|
npx prisma generate && npx prisma db push
|
||||||
|
) || {
|
||||||
|
echo "Error: Prisma generate/db push failed. Check DATABASE_URL in .env and PostgreSQL."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
echo "Prisma schema is in sync."
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_prisma_schema
|
||||||
|
|
||||||
# Start backend server
|
# Start backend server
|
||||||
echo "Starting backend API server..."
|
echo "Starting backend API server..."
|
||||||
cd "$REPO_ROOT/server" || exit 1
|
cd "$REPO_ROOT/server" || exit 1
|
||||||
|
|||||||
@@ -77,16 +77,10 @@ router.get('/share-pull', async (req: any, res) => {
|
|||||||
const yacht = await prisma.yachtPayload.findUnique({ where: { logbookId } })
|
const yacht = await prisma.yachtPayload.findUnique({ where: { logbookId } })
|
||||||
const deviation = await prisma.deviationPayload.findUnique({ where: { logbookId } })
|
const deviation = await prisma.deviationPayload.findUnique({ where: { logbookId } })
|
||||||
const crews = await prisma.crewPayload.findMany({ where: { logbookId } })
|
const crews = await prisma.crewPayload.findMany({ where: { logbookId } })
|
||||||
const logbookCrewSelection = await prisma.logbookCrewSelectionPayload.findUnique({
|
const { findLogbookCrewSelectionSafe, findLogbookVesselSelectionSafe } =
|
||||||
where: { logbookId }
|
await import('../utils/crewPoolSchema.js')
|
||||||
})
|
const logbookCrewSelection = await findLogbookCrewSelectionSafe(logbookId)
|
||||||
let logbookVesselSelection = null
|
const logbookVesselSelection = await findLogbookVesselSelectionSafe(logbookId)
|
||||||
const { hasVesselPoolPrismaModels } = await import('../utils/crewPoolSchema.js')
|
|
||||||
if (hasVesselPoolPrismaModels()) {
|
|
||||||
logbookVesselSelection = await prisma.logbookVesselSelectionPayload.findUnique({
|
|
||||||
where: { logbookId }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const entries = await prisma.entryPayload.findMany({ where: { logbookId } })
|
const entries = await prisma.entryPayload.findMany({ where: { logbookId } })
|
||||||
const photos = await prisma.photoPayload.findMany({ where: { logbookId } })
|
const photos = await prisma.photoPayload.findMany({ where: { logbookId } })
|
||||||
const gpsTracks = await prisma.gpsTrackPayload.findMany({ where: { logbookId } })
|
const gpsTracks = await prisma.gpsTrackPayload.findMany({ where: { logbookId } })
|
||||||
|
|||||||
+12
-15
@@ -273,7 +273,7 @@ router.post('/push', async (req: any, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (type === 'logbookVessel') {
|
} else if (type === 'logbookVessel') {
|
||||||
const { hasVesselPoolPrismaModels, VESSEL_POOL_MIGRATION_HINT } =
|
const { hasVesselPoolPrismaModels, isMissingPrismaTable, VESSEL_POOL_MIGRATION_HINT } =
|
||||||
await import('../utils/crewPoolSchema.js')
|
await import('../utils/crewPoolSchema.js')
|
||||||
if (!hasVesselPoolPrismaModels()) {
|
if (!hasVesselPoolPrismaModels()) {
|
||||||
results.push({
|
results.push({
|
||||||
@@ -283,7 +283,7 @@ router.post('/push', async (req: any, res) => {
|
|||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
{
|
try {
|
||||||
const existing = await prisma.logbookVesselSelectionPayload.findUnique({ where: { logbookId } })
|
const existing = await prisma.logbookVesselSelectionPayload.findUnique({ where: { logbookId } })
|
||||||
if (existing && new Date(existing.updatedAt) > itemUpdatedAt) {
|
if (existing && new Date(existing.updatedAt) > itemUpdatedAt) {
|
||||||
results.push({ payloadId, status: 'conflict', reason: 'Server version is newer' })
|
results.push({ payloadId, status: 'conflict', reason: 'Server version is newer' })
|
||||||
@@ -294,6 +294,12 @@ router.post('/push', async (req: any, res) => {
|
|||||||
create: { logbookId, encryptedData, iv, tag, updatedAt: itemUpdatedAt },
|
create: { logbookId, encryptedData, iv, tag, updatedAt: itemUpdatedAt },
|
||||||
update: { encryptedData, iv, tag, updatedAt: itemUpdatedAt }
|
update: { encryptedData, iv, tag, updatedAt: itemUpdatedAt }
|
||||||
})
|
})
|
||||||
|
} catch (err: unknown) {
|
||||||
|
if (isMissingPrismaTable(err)) {
|
||||||
|
results.push({ payloadId, status: 'error', error: VESSEL_POOL_MIGRATION_HINT })
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,19 +366,10 @@ router.get('/pull', async (req: any, res) => {
|
|||||||
const entries = await prisma.entryPayload.findMany({ where: { logbookId } })
|
const entries = await prisma.entryPayload.findMany({ where: { logbookId } })
|
||||||
const photos = await prisma.photoPayload.findMany({ where: { logbookId } })
|
const photos = await prisma.photoPayload.findMany({ where: { logbookId } })
|
||||||
const gpsTracks = await prisma.gpsTrackPayload.findMany({ where: { logbookId } })
|
const gpsTracks = await prisma.gpsTrackPayload.findMany({ where: { logbookId } })
|
||||||
let logbookCrewSelection = null
|
const { findLogbookCrewSelectionSafe, findLogbookVesselSelectionSafe } =
|
||||||
let logbookVesselSelection = null
|
await import('../utils/crewPoolSchema.js')
|
||||||
const { hasCrewPoolPrismaModels, hasVesselPoolPrismaModels } = await import('../utils/crewPoolSchema.js')
|
const logbookCrewSelection = await findLogbookCrewSelectionSafe(logbookId)
|
||||||
if (hasCrewPoolPrismaModels()) {
|
const logbookVesselSelection = await findLogbookVesselSelectionSafe(logbookId)
|
||||||
logbookCrewSelection = await prisma.logbookCrewSelectionPayload.findUnique({
|
|
||||||
where: { logbookId }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (hasVesselPoolPrismaModels()) {
|
|
||||||
logbookVesselSelection = await prisma.logbookVesselSelectionPayload.findUnique({
|
|
||||||
where: { logbookId }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
yacht,
|
yacht,
|
||||||
|
|||||||
@@ -37,3 +37,25 @@ export function isMissingPrismaTable(error: unknown): boolean {
|
|||||||
(error as { code: string }).code === 'P2021'
|
(error as { code: string }).code === 'P2021'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Pull-safe: returns null when models or DB tables are missing (P2021). */
|
||||||
|
export async function findLogbookCrewSelectionSafe(logbookId: string) {
|
||||||
|
if (!hasCrewPoolPrismaModels()) return null
|
||||||
|
try {
|
||||||
|
return await prisma.logbookCrewSelectionPayload.findUnique({ where: { logbookId } })
|
||||||
|
} catch (error) {
|
||||||
|
if (isMissingPrismaTable(error)) return null
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pull-safe: returns null when models or DB tables are missing (P2021). */
|
||||||
|
export async function findLogbookVesselSelectionSafe(logbookId: string) {
|
||||||
|
if (!hasVesselPoolPrismaModels()) return null
|
||||||
|
try {
|
||||||
|
return await prisma.logbookVesselSelectionPayload.findUnique({ where: { logbookId } })
|
||||||
|
} catch (error) {
|
||||||
|
if (isMissingPrismaTable(error)) return null
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user