Add code resolver, sync conflict handling, and user isolation

Introduce a CodeResolverService to classify user login codes, complete with detailed resolution logic and usability checks. Extend the sync system to handle conflicts via last-write-wins arbitration, with detailed conflict tracking for review. Update file permissions and runtime isolation in Docker to enhance security.
This commit is contained in:
2026-09-12 15:40:42 +02:00
parent 858c43a6aa
commit 5fd2d47a64
19 changed files with 950 additions and 131 deletions
+44 -8
View File
@@ -348,15 +348,19 @@ enum SyncOperation {
/// Append-only log of local mutations, replicated to the peer server (local
/// <-> cloud). `originId` is the SERVER_ID that made the change, so applying
/// an incoming entry never gets re-captured/re-pushed back (no echo loops).
/// `occurredAt` is the wall-clock moment of the mutation itself (set at
/// capture time), distinct from `createdAt` which is just row-insert time -
/// conflict resolution compares `occurredAt`, never sync/network timing.
model SyncLogEntry {
id String @id @default(cuid())
sequence Int @default(autoincrement())
model String
recordId String
operation SyncOperation
payload Json
originId String
createdAt DateTime @default(now())
id String @id @default(cuid())
sequence Int @default(autoincrement())
model String
recordId String
operation SyncOperation
payload Json
originId String
occurredAt DateTime @default(now())
createdAt DateTime @default(now())
}
/// Per-peer replication progress, kept on the side that initiates sync
@@ -368,3 +372,35 @@ model SyncCursor {
lastPushedSequence Int @default(0)
lastPulledSequence Int @default(0)
}
/// Last-write-wins register, one row per replicated record. Tracks the
/// wall-clock time and origin of whichever mutation - local or remote - is
/// currently considered authoritative for that record, so a concurrent edit
/// on both servers resolves deterministically by actual edit time instead
/// of by sync/network arrival order.
model SyncRecordVersion {
id String @id @default(cuid())
model String
recordId String
lastWriteAt DateTime
lastWriteOrigin String
updatedAt DateTime @updatedAt
@@unique([model, recordId])
}
/// Audit trail of detected conflicts: two servers wrote the same record
/// within the replication window. Resolution (last-write-wins by
/// occurredAt) still happens automatically and immediately - nothing here
/// blocks live sync - but Leitungsteam can review afterwards whether a
/// discarded edit needs to be manually reapplied.
model SyncConflict {
id String @id @default(cuid())
model String
recordId String
winningOrigin String
losingOrigin String
winningPayload Json
losingPayload Json
detectedAt DateTime @default(now())
}