Initial commit
This commit is contained in:
30
dist/shared/middleware/auth.js
vendored
Normal file
30
dist/shared/middleware/auth.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
import { ApiError } from "../errors";
|
||||
import { env } from "../../config/env";
|
||||
function extractAdminToken(req) {
|
||||
const raw = req.header("authorization") || "";
|
||||
if (raw.startsWith("Bearer ")) {
|
||||
return raw.slice(7);
|
||||
}
|
||||
return raw || req.header("x-admin-token") || "";
|
||||
}
|
||||
export function requireAdminToken(req, _res, next) {
|
||||
const token = extractAdminToken(req);
|
||||
if (!token) {
|
||||
return next(new ApiError("UNAUTHORIZED", "Missing admin bearer token", 401));
|
||||
}
|
||||
if (token !== env.ADMIN_TOKEN) {
|
||||
return next(new ApiError("UNAUTHORIZED", "Invalid admin token", 401));
|
||||
}
|
||||
return next();
|
||||
}
|
||||
export function requireDeviceToken(req, _res, next) {
|
||||
const raw = req.header("authorization") || "";
|
||||
const token = raw.startsWith("Bearer ") ? raw.slice(7) : raw;
|
||||
if (!token) {
|
||||
return next(new ApiError("UNAUTHORIZED", "Missing device bearer token", 401));
|
||||
}
|
||||
if (token !== env.DEVICE_TOKEN) {
|
||||
return next(new ApiError("UNAUTHORIZED", "Invalid device token", 401));
|
||||
}
|
||||
return next();
|
||||
}
|
||||
22
dist/shared/middleware/errorMiddleware.js
vendored
Normal file
22
dist/shared/middleware/errorMiddleware.js
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
import { ApiError, errorEnvelope } from "../errors";
|
||||
export function successResponse(req, data) {
|
||||
return {
|
||||
data,
|
||||
request_id: req.requestId,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
export function handleErrors(err, req, res, _next) {
|
||||
if (err instanceof ApiError) {
|
||||
res.status(err.statusCode).json(errorEnvelope({
|
||||
...err
|
||||
}, req.requestId));
|
||||
return;
|
||||
}
|
||||
res.status(500).json({
|
||||
code: "INTERNAL_ERROR",
|
||||
message: err.message || "Unexpected server error",
|
||||
request_id: req.requestId,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
37
dist/shared/middleware/idempotency.js
vendored
Normal file
37
dist/shared/middleware/idempotency.js
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
import { ApiError } from "../errors";
|
||||
import { readIdempotency, writeIdempotency } from "../idempotency/idempotencyStore";
|
||||
import { env } from "../../config/env";
|
||||
export function idempotency(options) {
|
||||
return function idempotencyMiddleware(req, _res, next) {
|
||||
const idempotencyKey = req.header("idempotency-key");
|
||||
if (!idempotencyKey) {
|
||||
if (options.required === false) {
|
||||
return next();
|
||||
}
|
||||
return next(new ApiError("DUPLICATE_REQUEST", "Missing Idempotency-Key", 400));
|
||||
}
|
||||
const cached = readIdempotency(options.scope, idempotencyKey);
|
||||
if (cached) {
|
||||
const cachedPayload = cached.response ?? cached;
|
||||
const cachedStatus = cached.statusCode || 200;
|
||||
return _res.status(cachedStatus).json(cachedPayload);
|
||||
}
|
||||
req.body = { ...(req.body || {}), __idempotencyKey: idempotencyKey };
|
||||
const originalJson = _res.json.bind(_res);
|
||||
const originalStatus = _res.status.bind(_res);
|
||||
let statusCode = 200;
|
||||
_res.status = function statusWithStore(code) {
|
||||
statusCode = code;
|
||||
return originalStatus(code);
|
||||
};
|
||||
_res.json = function jsonWithStore(payload) {
|
||||
writeIdempotency(options.scope, idempotencyKey, {
|
||||
response: payload,
|
||||
statusCode,
|
||||
at: Date.now()
|
||||
}, options.ttlMs || env.IDEMPOTENCY_TTL_MS);
|
||||
return originalJson(payload);
|
||||
};
|
||||
next();
|
||||
};
|
||||
}
|
||||
11
dist/shared/middleware/requestContext.js
vendored
Normal file
11
dist/shared/middleware/requestContext.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { env } from "../../config/env";
|
||||
export function requestContext(req, _res, next) {
|
||||
const requestId = req.header(env.TRACE_HEADER) ||
|
||||
req.header("x-trace-id") ||
|
||||
randomUUID();
|
||||
const traceId = req.header("x-trace-id") || requestId;
|
||||
req.requestId = requestId;
|
||||
req.traceId = traceId;
|
||||
next();
|
||||
}
|
||||
Reference in New Issue
Block a user