fix bypass in production
This commit is contained in:
parent
098c9d8ace
commit
9c04b8652c
|
|
@ -16,7 +16,8 @@
|
||||||
"Bash(python3:*)",
|
"Bash(python3:*)",
|
||||||
"Bash(echo:*)",
|
"Bash(echo:*)",
|
||||||
"Bash(git -C ka-note check-ignore .env)",
|
"Bash(git -C ka-note check-ignore .env)",
|
||||||
"Bash(git -C . check-ignore ka-note/.env)"
|
"Bash(git -C . check-ignore ka-note/.env)",
|
||||||
|
"Bash(git check-ignore:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
**/.env.local
|
||||||
|
**/.env.*.local
|
||||||
|
**/node_modules
|
||||||
|
|
@ -3,6 +3,8 @@ dist/
|
||||||
.svelte-kit/
|
.svelte-kit/
|
||||||
build/
|
build/
|
||||||
.env
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
*.db
|
*.db
|
||||||
*.sqlite
|
*.sqlite
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.0.9
|
1.0.11
|
||||||
|
|
@ -42,25 +42,25 @@ async function pullAndMerge(since: Date | null): Promise<string> {
|
||||||
await db.transaction('rw', [db.contexts, db.topics, db.historyEntries, db.ratings], async () => {
|
await db.transaction('rw', [db.contexts, db.topics, db.historyEntries, db.ratings], async () => {
|
||||||
for (const serverCtx of contexts) {
|
for (const serverCtx of contexts) {
|
||||||
const local = await db.contexts.get(serverCtx.id);
|
const local = await db.contexts.get(serverCtx.id);
|
||||||
if (!local || serverCtx.version >= local.version) {
|
if (!local || serverCtx.version > local.version) {
|
||||||
await db.contexts.put(serverCtx);
|
await db.contexts.put(serverCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const serverTopic of topics) {
|
for (const serverTopic of topics) {
|
||||||
const local = await db.topics.get(serverTopic.id);
|
const local = await db.topics.get(serverTopic.id);
|
||||||
if (!local || serverTopic.version >= local.version) {
|
if (!local || serverTopic.version > local.version) {
|
||||||
await db.topics.put(serverTopic);
|
await db.topics.put(serverTopic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const serverHe of historyEntries) {
|
for (const serverHe of historyEntries) {
|
||||||
const local = await db.historyEntries.get(serverHe.id);
|
const local = await db.historyEntries.get(serverHe.id);
|
||||||
if (!local || serverHe.version >= local.version) {
|
if (!local || serverHe.version > local.version) {
|
||||||
await db.historyEntries.put(serverHe);
|
await db.historyEntries.put(serverHe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const serverRating of ratings) {
|
for (const serverRating of ratings) {
|
||||||
const local = await db.ratings.get(serverRating.id);
|
const local = await db.ratings.get(serverRating.id);
|
||||||
if (!local || serverRating.version >= local.version) {
|
if (!local || serverRating.version > local.version) {
|
||||||
await db.ratings.put(serverRating);
|
await db.ratings.put(serverRating);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,8 +95,8 @@ export async function sync(): Promise<void> {
|
||||||
async function doSync(since: Date | null): Promise<void> {
|
async function doSync(since: Date | null): Promise<void> {
|
||||||
syncStatus.set('syncing');
|
syncStatus.set('syncing');
|
||||||
try {
|
try {
|
||||||
const serverTimestamp = await pullAndMerge(since);
|
|
||||||
await pushAll();
|
await pushAll();
|
||||||
|
const serverTimestamp = await pullAndMerge(since);
|
||||||
const syncTime = new Date(serverTimestamp);
|
const syncTime = new Date(serverTimestamp);
|
||||||
lastSyncAt.set(syncTime);
|
lastSyncAt.set(syncTime);
|
||||||
localStorage.setItem('lastSyncAt', syncTime.toISOString());
|
localStorage.setItem('lastSyncAt', syncTime.toISOString());
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Ka-Note Roadmap
|
||||||
|
|
||||||
|
## [LOW] Sync: Soft-delete Restrisiko bei neuem Gerät / DB-Reset
|
||||||
|
|
||||||
|
**Problem**
|
||||||
|
Wenn ein Gerät noch nie gepusht hat (neues Gerät oder IndexedDB gelöscht), ist `!local = true` für alle Pull-Einträge. Der Pull überschreibt dann keine lokale Version, sondern legt neu an — auch wenn der Eintrag auf einem anderen Gerät bereits gelöscht wurde. Das gelöschte Item erscheint wieder, bis das löschende Gerät seinen Push durchführt.
|
||||||
|
|
||||||
|
**Bereits gefixt**
|
||||||
|
- Push läuft jetzt vor Pull (`doSync`)
|
||||||
|
- Merge-Bedingung auf `>` statt `>=` (lokale Änderung gewinnt bei Versionsgleichstand)
|
||||||
|
|
||||||
|
**Restrisiko**
|
||||||
|
Szenario: Gerät A löscht Item (version N+1), hat aber noch nie gepusht. Gerät B macht Full-Sync: Pull bringt Server-Version N (nicht gelöscht), `!local` → wird eingespielt. Gerät B sieht das Item als vorhanden.
|
||||||
|
|
||||||
|
**Lösungsoptionen**
|
||||||
|
|
||||||
|
1. **Serverseitig: `deletedAt`-Items aus Pull-Response ausfiltern**
|
||||||
|
Server gibt bei Pull keine soft-deleted Items zurück. Gerät B bekommt das Item gar nicht erst. Problem: Wenn Gerät B das Item schon lokal hat (z.B. eigene ältere Version), wird es nie als gelöscht markiert.
|
||||||
|
|
||||||
|
2. **Tombstone-Tabelle**
|
||||||
|
Separate Tabelle `deletions(entityId, deletedAt, version)` — wird immer mitübertragen. Client wertet Tombstones aus und löscht lokale Items explizit. Robusteste Lösung, erhöht aber Komplexität.
|
||||||
|
|
||||||
|
3. **Server filtert + Client löscht lokale Einträge die der Server nicht mehr liefert**
|
||||||
|
Server sendet nur aktive Items. Client vergleicht lokale IDs mit Pull-Response und markiert fehlende als `deletedAt`. Einfacher als Tombstones, aber fehleranfällig bei partiellen Pulls (since-Filter).
|
||||||
|
|
||||||
|
**Empfehlung**
|
||||||
|
Option 2 (Tombstones) für korrekte Multi-Gerät-Unterstützung. Option 3 als pragmatischer Zwischenschritt wenn Full-Sync (since=null) garantiert ist.
|
||||||
Loading…
Reference in New Issue