fix: resend offline-queued entries with an idempotency key
When the server saved an inspection, harvest, treatment, feeding, varroa measurement or swarm check but the answer was lost (dead zone at the apiary), the form queued the entry and the sync later created it a second time - including a second automation task. Each opened form now creates one Idempotency-Key, sends it with the request and stores it with the queued entry, so the retry returns the first record. Entries queued before this change still sync without a key. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
5d11756a87
commit
5c2ea7bf1e
16 files changed
+139
-33
No files matched your search
@@ -18,6 +18,8 @@ interface PendingEntry<T> {
|
||||
id: number;
|
||||
input: T;
|
||||
queuedAt: string;
|
||||
// Missing on entries queued before keys existed - those sync as before.
|
||||
idempotencyKey?: string;
|
||||
}
|
||||
|
||||
interface OfflineDB extends DBSchema {
|
||||
@@ -70,9 +72,13 @@ function getDb(): Promise<IDBPDatabase<OfflineDB>> {
|
||||
return dbPromise;
|
||||
}
|
||||
|
||||
async function queue<T>(store: PendingStoreName, input: T): Promise<void> {
|
||||
async function queue<T>(
|
||||
store: PendingStoreName,
|
||||
input: T,
|
||||
idempotencyKey?: string,
|
||||
): Promise<void> {
|
||||
const db = await getDb();
|
||||
await db.add(store, { input, queuedAt: new Date().toISOString() } as never);
|
||||
await db.add(store, { input, idempotencyKey, queuedAt: new Date().toISOString() } as never);
|
||||
}
|
||||
|
||||
async function getPending<T>(store: PendingStoreName): Promise<PendingEntry<T>[]> {
|
||||
@@ -91,8 +97,8 @@ async function countPending(store: PendingStoreName): Promise<number> {
|
||||
}
|
||||
|
||||
export type PendingInspection = PendingEntry<CreateInspectionInput>;
|
||||
export function queueInspection(input: CreateInspectionInput): Promise<void> {
|
||||
return queue("pending-inspections", input);
|
||||
export function queueInspection(input: CreateInspectionInput, idempotencyKey?: string): Promise<void> {
|
||||
return queue("pending-inspections", input, idempotencyKey);
|
||||
}
|
||||
export function getPendingInspections(): Promise<PendingInspection[]> {
|
||||
return getPending("pending-inspections");
|
||||
@@ -105,8 +111,8 @@ export function countPendingInspections(): Promise<number> {
|
||||
}
|
||||
|
||||
export type PendingTreatment = PendingEntry<CreateTreatmentInput>;
|
||||
export function queueTreatment(input: CreateTreatmentInput): Promise<void> {
|
||||
return queue("pending-treatments", input);
|
||||
export function queueTreatment(input: CreateTreatmentInput, idempotencyKey?: string): Promise<void> {
|
||||
return queue("pending-treatments", input, idempotencyKey);
|
||||
}
|
||||
export function getPendingTreatments(): Promise<PendingTreatment[]> {
|
||||
return getPending("pending-treatments");
|
||||
@@ -119,8 +125,8 @@ export function countPendingTreatments(): Promise<number> {
|
||||
}
|
||||
|
||||
export type PendingFeeding = PendingEntry<CreateFeedingInput>;
|
||||
export function queueFeeding(input: CreateFeedingInput): Promise<void> {
|
||||
return queue("pending-feedings", input);
|
||||
export function queueFeeding(input: CreateFeedingInput, idempotencyKey?: string): Promise<void> {
|
||||
return queue("pending-feedings", input, idempotencyKey);
|
||||
}
|
||||
export function getPendingFeedings(): Promise<PendingFeeding[]> {
|
||||
return getPending("pending-feedings");
|
||||
@@ -133,8 +139,8 @@ export function countPendingFeedings(): Promise<number> {
|
||||
}
|
||||
|
||||
export type PendingVarroaMeasurement = PendingEntry<CreateVarroaMeasurementInput>;
|
||||
export function queueVarroaMeasurement(input: CreateVarroaMeasurementInput): Promise<void> {
|
||||
return queue("pending-varroa-measurements", input);
|
||||
export function queueVarroaMeasurement(input: CreateVarroaMeasurementInput, idempotencyKey?: string): Promise<void> {
|
||||
return queue("pending-varroa-measurements", input, idempotencyKey);
|
||||
}
|
||||
export function getPendingVarroaMeasurements(): Promise<PendingVarroaMeasurement[]> {
|
||||
return getPending("pending-varroa-measurements");
|
||||
@@ -147,8 +153,8 @@ export function countPendingVarroaMeasurements(): Promise<number> {
|
||||
}
|
||||
|
||||
export type PendingSwarmControlCheck = PendingEntry<CreateSwarmControlCheckInput>;
|
||||
export function queueSwarmControlCheck(input: CreateSwarmControlCheckInput): Promise<void> {
|
||||
return queue("pending-swarm-control-checks", input);
|
||||
export function queueSwarmControlCheck(input: CreateSwarmControlCheckInput, idempotencyKey?: string): Promise<void> {
|
||||
return queue("pending-swarm-control-checks", input, idempotencyKey);
|
||||
}
|
||||
export function getPendingSwarmControlChecks(): Promise<PendingSwarmControlCheck[]> {
|
||||
return getPending("pending-swarm-control-checks");
|
||||
@@ -161,8 +167,8 @@ export function countPendingSwarmControlChecks(): Promise<number> {
|
||||
}
|
||||
|
||||
export type PendingHarvest = PendingEntry<CreateHarvestInput>;
|
||||
export function queueHarvest(input: CreateHarvestInput): Promise<void> {
|
||||
return queue("pending-harvests", input);
|
||||
export function queueHarvest(input: CreateHarvestInput, idempotencyKey?: string): Promise<void> {
|
||||
return queue("pending-harvests", input, idempotencyKey);
|
||||
}
|
||||
export function getPendingHarvests(): Promise<PendingHarvest[]> {
|
||||
return getPending("pending-harvests");
|
||||
|
||||
Reference in new issue
Block a user