feat: Hybride Passkey-Freigabe für Skipper und Crew

Skipper (nur Owner) und Crew (WRITE-Collaborators) können Logbuchseiten
optional per WebAuthn freigeben; klassische Unterschrift bleibt als Fallback.
Signatur ist an den Eintrags-Hash gebunden, Export in CSV/PDF angepasst.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-29 16:28:52 +02:00
co-authored by Cursor
parent 5706d1762d
commit ce47fe5fdc
18 changed files with 1471 additions and 68 deletions
+13 -3
View File
@@ -3,7 +3,7 @@ import { db } from './db.js'
import { getActiveMasterKey } from './auth.js'
import { getLogbookKey } from './logbookKeys.js'
import { decryptJson } from './crypto.js'
import { isSignatureImage } from '../utils/signatures.js'
import { isSignatureImage, isPasskeySignature } from '../utils/signatures.js'
export async function generateLogbookPagePdf(logbookId: string, entryId: string, preloadedData?: { yacht: any; entry: any }): Promise<jsPDF> {
let yachtName = '', homePort = '', registration = '', callsign = '', atis = '', mmsi = '';
@@ -230,7 +230,12 @@ export async function generateLogbookPagePdf(logbookId: string, entryId: string,
doc.line(sigX + 78.5, sigY, sigX + 78.5, sigY + rowHeight * 3);
doc.text('Skipper Unterschrift:', sigX + 2, sigY + 4.2);
if (isSignatureImage(entry.signSkipper)) {
if (isPasskeySignature(entry.signSkipper)) {
doc.setFont('Helvetica', 'normal');
const skipperDate = new Date(entry.signSkipper.signedAt).toLocaleString('de-DE');
doc.text(`Passkey: ${entry.signSkipper.username}`, sigX + 2, sigY + 9);
doc.text(skipperDate, sigX + 2, sigY + 13.5);
} else if (isSignatureImage(entry.signSkipper)) {
doc.addImage(entry.signSkipper, 'PNG', sigX + 2, sigY + 6, 72, 14)
} else {
doc.setFont('Helvetica', 'normal');
@@ -239,7 +244,12 @@ export async function generateLogbookPagePdf(logbookId: string, entryId: string,
doc.setFont('Helvetica', 'bold');
doc.text('Crew Unterschrift:', sigX + 80.5, sigY + 4.2);
if (isSignatureImage(entry.signCrew)) {
if (isPasskeySignature(entry.signCrew)) {
doc.setFont('Helvetica', 'normal');
const crewDate = new Date(entry.signCrew.signedAt).toLocaleString('de-DE');
doc.text(`Passkey: ${entry.signCrew.username}`, sigX + 80.5, sigY + 9);
doc.text(crewDate, sigX + 80.5, sigY + 13.5);
} else if (isSignatureImage(entry.signCrew)) {
doc.addImage(entry.signCrew, 'PNG', sigX + 80.5, sigY + 6, 72, 14)
} else {
doc.setFont('Helvetica', 'normal');