feat: automatické sledování doručení objednávky přes Bolt Food
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 21s
CI / Build server (push) Successful in 24s
CI / Build client (push) Successful in 36s
CI / Playwright E2E tests (push) Successful in 1m25s
CI / Build and push Docker image (push) Successful in 43s
CI / Notify (push) Successful in 2s
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 21s
CI / Build server (push) Successful in 24s
CI / Build client (push) Successful in 36s
CI / Playwright E2E tests (push) Successful in 1m25s
CI / Build and push Docker image (push) Successful in 43s
CI / Notify (push) Successful in 2s
Zakladatel skupiny může na stránce objednání vložit sdílecí odkaz Bolt Food. Server pak každou minutu dotazuje veřejné Bolt API a automaticky aktualizuje čas doručení skupiny (deliveryAt). Sledování se samo ukončí po doručení, zrušení objednávky nebo opakovaných chybách. Leader lease vytažena do znovupoužitelného modulu leaderLease.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
import webpush from 'web-push';
|
||||
import crypto from 'crypto';
|
||||
import getStorage from './storage';
|
||||
import { getRedisClient } from './storage/redis';
|
||||
import { createLeaderLease } from './leaderLease';
|
||||
import { getClientData, getToday } from './service';
|
||||
import { getIsWeekend } from './utils';
|
||||
import { LunchChoices } from '../../types';
|
||||
|
||||
const storage = getStorage();
|
||||
const REGISTRY_KEY = 'push_reminder_registry';
|
||||
const LEADER_LEASE_KEY = 'luncher:reminder:leader';
|
||||
const LEASE_TTL_SECONDS = 90;
|
||||
const lease = createLeaderLease('luncher:reminder:leader');
|
||||
|
||||
const POD_ID = process.env.POD_ID ?? `local-${process.pid}`;
|
||||
|
||||
@@ -43,49 +42,9 @@ function userHasChoice(choices: LunchChoices, login: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pokusí se získat nebo obnovit leader lease pro scheduler připomínek.
|
||||
* Vrátí true pokud tato instance smí spustit připomínky.
|
||||
* Při non-Redis storage vždy vrací true (single-process, leader election není potřeba).
|
||||
*/
|
||||
async function tryAcquireOrRenewLease(): Promise<boolean> {
|
||||
if (process.env.STORAGE?.toLowerCase() !== 'redis') return true;
|
||||
try {
|
||||
const c = getRedisClient();
|
||||
if (!c) return true;
|
||||
|
||||
// Zkusíme získat lease atomicky (SET NX EX)
|
||||
const acquired = await c.set(LEADER_LEASE_KEY, POD_ID, { NX: true, EX: LEASE_TTL_SECONDS });
|
||||
if (acquired !== null) return true; // lease čerstvě získána
|
||||
|
||||
// Pokud jsme ji nedostali, ověříme zda ji držíme my
|
||||
const currentHolder = await c.get(LEADER_LEASE_KEY);
|
||||
if (currentHolder === POD_ID) {
|
||||
// Naše lease — obnovíme TTL
|
||||
await c.set(LEADER_LEASE_KEY, POD_ID, { EX: LEASE_TTL_SECONDS });
|
||||
return true;
|
||||
}
|
||||
return false; // lease drží jiná instance
|
||||
} catch (e) {
|
||||
console.error('Push reminder: chyba při získávání lease, připomínky budou odeslány', e);
|
||||
return true; // při chybě raději spustíme, než vynecháme
|
||||
}
|
||||
}
|
||||
|
||||
/** Uvolní leader lease při graceful shutdown. */
|
||||
export async function releaseReminderLease(): Promise<void> {
|
||||
if (process.env.STORAGE?.toLowerCase() !== 'redis') return;
|
||||
try {
|
||||
const c = getRedisClient();
|
||||
if (!c) return;
|
||||
const currentHolder = await c.get(LEADER_LEASE_KEY);
|
||||
if (currentHolder === POD_ID) {
|
||||
await c.del(LEADER_LEASE_KEY);
|
||||
console.log('Push reminder: lease uvolněna');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Push reminder: chyba při uvolňování lease', e);
|
||||
}
|
||||
await lease.release();
|
||||
}
|
||||
|
||||
/** Stopne scheduler připomínek. Volá se při graceful shutdown. */
|
||||
@@ -140,7 +99,7 @@ async function checkAndSendReminders(): Promise<void> {
|
||||
if (getIsWeekend(getToday())) return;
|
||||
|
||||
// Leader election — pouze jeden pod spouští připomínky
|
||||
const isLeader = await tryAcquireOrRenewLease();
|
||||
const isLeader = await lease.tryAcquireOrRenew();
|
||||
if (!isLeader) return;
|
||||
|
||||
const registry = await storage.getData<Registry>(REGISTRY_KEY) ?? {};
|
||||
|
||||
Reference in New Issue
Block a user