Implement events api routes

This commit is contained in:
Dominic Ferrando
2026-07-16 00:55:04 -04:00
parent ee9b03796d
commit 48765b4b89
6 changed files with 225 additions and 97 deletions
+42 -4
View File
@@ -19,6 +19,7 @@ import { randomUUIDv7, sleep } from "bun";
import { CalculatorService, CalculatorUnavailableError } from "./services/calculator";
import { StandardsParamsSchema } from "@blade-and-brawn/calculator";
import { StandardsService } from "./services/standards";
import { EventsService, EventStatusSchema } from "./services/events";
// CONSTANTS
// -----------------------
@@ -30,9 +31,17 @@ const s = (() => {
const Standards = new StandardsService();
const Calculator = new CalculatorService(DEFAULT_NAME);
const Commerce = new CommerceService();
return { Standards, Calculator, Commerce };
const Events = new EventsService();
return { Standards, Calculator, Commerce, Events };
})();
// QUEUES
// -----------------------
const queues = [
s.Commerce.Apparel.Syncs.Queue,
s.Commerce.Apparel.Orders.Queue,
] as const;
// PLUGINS
// -----------------------
const authPlugin = new Elysia({ name: "auth" })
@@ -224,8 +233,38 @@ export const app = new Elysia()
return { pProduct, wProduct };
}, {
params: t.Object({ pProductId: t.Numeric() })
})))
// EVENTS
.group("/events", { auth: true }, (app) => app
.get("/", async ({ query }) => {
const events = await s.Events.list({
filter: { status: query.status, type: query.type, group: query.group },
limit: query.limit,
offset: query.offset,
});
return events.map((event) => ({ ...event, status: EventsService.status(event) }));
}, {
query: t.Object({
status: t.Optional(EventStatusSchema),
type: t.Optional(t.String()),
group: t.Optional(t.String()),
limit: t.Optional(t.Numeric()),
offset: t.Optional(t.Numeric()),
})
))
})
.get("/:id", async ({ params: { id } }) => {
const event = await s.Events.get(id);
if (!event) throw new NotFoundError("Event not found");
return { ...event, status: EventsService.status(event) };
}, {
params: t.Object({ id: t.String() })
})
.post("/:id/replay", async ({ params: { id } }) => {
const replayed = await s.Events.replay(id);
if (!replayed) throw new NotFoundError("Event not found or not in a failed state");
}, { params: t.Object({ id: t.String() }) })
)
// WEBHOOKS
.post("/webhooks/printful", async ({ body }) => {
@@ -307,13 +346,12 @@ app.listen(3000, async () => {
log.info({ port: 3000 }, "server started")
// MANAGE QUEUES
const queues = [s.Commerce.Apparel.Syncs.Queue, s.Commerce.Apparel.Orders.Queue];
for (const queue of queues) {
(async () => {
while (true) {
try {
// clean, if ready
if ((Date.now() - queue.lastCleanDate.getTime()) >= queue.cfg.concurrency.timeoutMs)
if ((Date.now() - queue.lastCleanDate.getTime()) >= EventsService.CONCURRENCY_TIMEOUT_MS)
await queue.clean().catch((err) => log.error({ name: queue.group, err }));
// drain
await queue.drain();