implemented the domain logic + processor endpoints

This commit is contained in:
user
2026-03-01 06:33:32 +02:00
parent c74523e4bc
commit 8aa8ddfa7d
10 changed files with 1629 additions and 32 deletions

View File

@@ -1,15 +1,266 @@
import {
pingMobileDeviceSchema,
registerMobileDeviceSchema,
syncMobileMediaSchema,
syncMobileSMSSchema,
} from "@pkg/logic/domains/mobile/data";
import { getMobileController } from "@pkg/logic/domains/mobile/controller";
import type { FlowExecCtx } from "@pkg/logic/core/flow.execution.context";
import { errorStatusMap, type Err } from "@pkg/result";
import { logDomainEvent } from "@pkg/logger";
import type { Context } from "hono";
import * as v from "valibot";
import { Hono } from "hono";
const mobileController = getMobileController();
const DEVICE_ID_HEADER = "x-device-id";
const FLOW_ID_HEADER = "x-flow-id";
function buildFlowExecCtx(c: Context): FlowExecCtx {
return {
flowId: c.req.header(FLOW_ID_HEADER) || crypto.randomUUID(),
};
}
function respondError(c: Context, fctx: FlowExecCtx, err: Err) {
const status = errorStatusMap[err.code] || 500;
c.status(status as any);
return c.json({ data: null, error: { ...err, flowId: fctx.flowId } });
}
function requireExternalDeviceId(c: Context): string | null {
const externalDeviceId = c.req.header(DEVICE_ID_HEADER);
if (!externalDeviceId || externalDeviceId.trim().length === 0) {
return null;
}
return externalDeviceId.trim();
}
async function parseJson(c: Context) {
try {
return await c.req.json();
} catch {
return null;
}
}
export const mobileRouter = new Hono()
.post("/register", async (c) => {
const fctx = buildFlowExecCtx(c);
const startedAt = Date.now();
logDomainEvent({
event: "processor.mobile.register.started",
fctx,
});
const payload = await parseJson(c);
const parsed = v.safeParse(registerMobileDeviceSchema, payload);
if (!parsed.success) {
const error = {
flowId: fctx.flowId,
code: "VALIDATION_ERROR",
message: "Invalid register payload",
description: "Please validate the payload and retry",
detail: parsed.issues.map((issue) => issue.message).join(", "),
} as Err;
return respondError(c, fctx, error);
}
const result = await mobileController.registerDevice(
fctx,
parsed.output,
);
if (result.isErr()) {
logDomainEvent({
level: "error",
event: "processor.mobile.register.failed",
fctx,
durationMs: Date.now() - startedAt,
error: result.error,
});
return respondError(c, fctx, result.error);
}
logDomainEvent({
event: "processor.mobile.register.succeeded",
fctx,
durationMs: Date.now() - startedAt,
meta: { deviceId: result.value.id },
});
return c.json({ data: result.value, error: null });
})
.put("/ping", async (c) => {
return c.json({ data: "" });
const fctx = buildFlowExecCtx(c);
const startedAt = Date.now();
const externalDeviceId = requireExternalDeviceId(c);
if (!externalDeviceId) {
const error = {
flowId: fctx.flowId,
code: "AUTH_ERROR",
message: "Missing device id header",
description: `Request must include ${DEVICE_ID_HEADER}`,
detail: `${DEVICE_ID_HEADER} header is required`,
} as Err;
return respondError(c, fctx, error);
}
const payload = await parseJson(c);
const parsed = v.safeParse(pingMobileDeviceSchema, payload ?? {});
if (!parsed.success) {
const error = {
flowId: fctx.flowId,
code: "VALIDATION_ERROR",
message: "Invalid ping payload",
description: "Please validate the payload and retry",
detail: parsed.issues.map((issue) => issue.message).join(", "),
} as Err;
return respondError(c, fctx, error);
}
const result = await mobileController.pingByExternalDeviceId(
fctx,
externalDeviceId,
parsed.output,
);
if (result.isErr()) {
logDomainEvent({
level: "error",
event: "processor.mobile.ping.failed",
fctx,
durationMs: Date.now() - startedAt,
error: result.error,
meta: { externalDeviceId },
});
return respondError(c, fctx, result.error);
}
logDomainEvent({
event: "processor.mobile.ping.succeeded",
fctx,
durationMs: Date.now() - startedAt,
meta: { externalDeviceId },
});
return c.json({ data: { ok: result.value }, error: null });
})
.put("/sms/sync", async (c) => {
return c.json({ data: "" });
const fctx = buildFlowExecCtx(c);
const startedAt = Date.now();
const externalDeviceId = requireExternalDeviceId(c);
if (!externalDeviceId) {
const error = {
flowId: fctx.flowId,
code: "AUTH_ERROR",
message: "Missing device id header",
description: `Request must include ${DEVICE_ID_HEADER}`,
detail: `${DEVICE_ID_HEADER} header is required`,
} as Err;
return respondError(c, fctx, error);
}
const payload = await parseJson(c);
const parsed = v.safeParse(syncMobileSMSSchema, payload);
if (!parsed.success) {
const error = {
flowId: fctx.flowId,
code: "VALIDATION_ERROR",
message: "Invalid SMS sync payload",
description: "Please validate the payload and retry",
detail: parsed.issues.map((issue) => issue.message).join(", "),
} as Err;
return respondError(c, fctx, error);
}
const result = await mobileController.syncSMSByExternalDeviceId(
fctx,
externalDeviceId,
parsed.output.messages,
);
if (result.isErr()) {
logDomainEvent({
level: "error",
event: "processor.mobile.sms_sync.failed",
fctx,
durationMs: Date.now() - startedAt,
error: result.error,
meta: {
externalDeviceId,
received: parsed.output.messages.length,
},
});
return respondError(c, fctx, result.error);
}
logDomainEvent({
event: "processor.mobile.sms_sync.succeeded",
fctx,
durationMs: Date.now() - startedAt,
meta: {
externalDeviceId,
...result.value,
},
});
return c.json({ data: result.value, error: null });
})
.put("/media/sync", async (c) => {
return c.json({ data: "" });
})
.post("/register", async (c) => {
return c.json({ data: "" });
const fctx = buildFlowExecCtx(c);
const startedAt = Date.now();
const externalDeviceId = requireExternalDeviceId(c);
if (!externalDeviceId) {
const error = {
flowId: fctx.flowId,
code: "AUTH_ERROR",
message: "Missing device id header",
description: `Request must include ${DEVICE_ID_HEADER}`,
detail: `${DEVICE_ID_HEADER} header is required`,
} as Err;
return respondError(c, fctx, error);
}
const payload = await parseJson(c);
const parsed = v.safeParse(syncMobileMediaSchema, payload);
if (!parsed.success) {
const error = {
flowId: fctx.flowId,
code: "VALIDATION_ERROR",
message: "Invalid media sync payload",
description: "Please validate the payload and retry",
detail: parsed.issues.map((issue) => issue.message).join(", "),
} as Err;
return respondError(c, fctx, error);
}
const result = await mobileController.syncMediaByExternalDeviceId(
fctx,
externalDeviceId,
parsed.output.assets,
);
if (result.isErr()) {
logDomainEvent({
level: "error",
event: "processor.mobile.media_sync.failed",
fctx,
durationMs: Date.now() - startedAt,
error: result.error,
meta: {
externalDeviceId,
received: parsed.output.assets.length,
},
});
return respondError(c, fctx, result.error);
}
logDomainEvent({
event: "processor.mobile.media_sync.succeeded",
fctx,
durationMs: Date.now() - startedAt,
meta: {
externalDeviceId,
...result.value,
},
});
return c.json({ data: result.value, error: null });
});