- Sync deletions: a 2xx Hiboutik answer is the truth, even an empty list; a failed call (HTTP error or non-list body) deletes nothing (HiboutikClient.getList). - Validate the environment at startup (HIBOUTIK_BASE_URL must be HTTPS), global ValidationPipe, Helmet, CORS opt-in, HTTP timeouts, rate limiting (60 req/min/IP, 5 on POST /sync), AllExceptionsFilter. - Postgres advisory lock so only one synchronization runs at a time across instances; syncAll is private, syncs start through startSync. - Fix the Jest moduleNameMapper; document the validation and security rules in CLAUDE.md. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
28 lines
741 B
TypeScript
28 lines
741 B
TypeScript
import { Logger } from '@nestjs/common';
|
|
|
|
/**
|
|
* Runs `action` on each item, one at a time: a failing item is logged and skipped, the others are still processed.
|
|
* @param items
|
|
* @param action
|
|
* @param logger
|
|
* @param describe label of an item in the error log, e.g. "Hiboutik brand 12".
|
|
* @returns the number of failed items.
|
|
*/
|
|
export async function forEachSafely<T>(
|
|
items: T[],
|
|
action: (item: T) => Promise<unknown>,
|
|
logger: Logger,
|
|
describe: (item: T) => string,
|
|
): Promise<number> {
|
|
let nbFailed = 0;
|
|
for (const item of items) {
|
|
try {
|
|
await action(item);
|
|
} catch (error) {
|
|
nbFailed++;
|
|
logger.error(`Failed to sync ${describe(item)}`, (error as Error)?.stack);
|
|
}
|
|
}
|
|
return nbFailed;
|
|
}
|