Commit graph

5 commits

Author SHA1 Message Date
1a0500b812
fix: prevent data loss in routes, fragments, and group operations
Found and fixed multiple critical bugs with similar patterns:

1. routeStatements() used DELETE FROM d1_routes before re-inserting
   - Could cause data loss if interrupted or if logic changes
   - Now uses DELETE with WHERE clause + INSERT...ON CONFLICT (upsert)

2. saveFragments() used DELETE FROM d1_fragments before re-inserting
   - Same pattern as routes, fixed with targeted deletes + upsert
   - Added loadFragments() call to determine what to delete

3. adminGroupRename() didn't update fragment groupId
   - When renaming a group, fragments were left pointing to old groupId
   - Now updates fragments alongside routes, secrets, and invites

All changes follow the same safe pattern:
- Load existing data to identify what needs deletion
- Delete only removed items with WHERE clauses
- Use INSERT...ON CONFLICT DO UPDATE for upserts
- Never use bare DELETE FROM table

Testing: All 339 tests pass.
2026-09-10 22:35:43 +08:00
github-actions[bot]
6c6c67fd73 chore: auto-fix lint & formatting [skip ci] 2026-09-10 14:30:55 +00:00
b313cdc2fe
fix: prevent cascade deletion of routes when updating groups
Previously, saveGroups() used DELETE FROM d1_groups followed by re-insertion,
which triggered ON DELETE CASCADE and wiped all routes whenever any group was updated.

Changes:
- Use INSERT...ON CONFLICT DO UPDATE (upsert) instead of delete-then-insert
- Only delete groups that are actually being removed
- Delete routes first, then delete groups (proper cascade order)
- Load existing group IDs before saving to detect deletions

This fixes the critical bug where updating one group's metadata would
delete all routes across all groups.

Data recovery: Used D1 Time Travel to restore from bookmark
000018f9-00000002-000050de-f23b7bfe5ccdc00e9c9dec3a4de8bc81 (before
the problematic group update), recovering 18 routes.
2026-09-10 22:29:47 +08:00
github-actions[bot]
7c37f5d8b6 chore: auto-fix lint & formatting [skip ci] 2026-08-17 07:51:36 +00:00
25ebae4ae5
feat(storage): migrate config, dedup and delivery state to D1
- Move oversized queue payloads from KV to R2 (PAYLOAD binding, webhooks/YYYY/MM/DD/*.json, KV queue:payload:* fallback)
- Persist routes/groups to D1 (d1_routes/d1_groups) with memory -> KV -> D1 three-tier cache, seeded from legacy KV config keys
- Move webhook dedup (dedup_keys), delivery state (delivery_state) and message tracking (message_tracking) to D1 via canUseD1 probe with automatic KV fallback
- Batch send_logs inserts (recordSendBatch) and add group_id/ts index
- Add storage-prune scheduled task for expired dedup/state/tracking rows
- Add TTL to invite:group:{id} index and audit all ephemeral KV keys
- Add D1 indexes for the new tables
- Sync AGENTS.md, README.md/zh and docs/ (en/zh) with the new storage layout
2026-08-17 15:01:20 +08:00