161 lines
4.1 KiB
TypeScript
161 lines
4.1 KiB
TypeScript
import {
|
|
ApiVersionClass,
|
|
AppEnv,
|
|
AuthType,
|
|
LATEST_VERSION,
|
|
type Organization,
|
|
tryCatch,
|
|
} from "@autumn/shared";
|
|
import type { Context, Next } from "hono";
|
|
import { createRequestScopedDb } from "@/db/initDrizzle.js";
|
|
import { createLogger } from "@/external/logtail/logtailUtils.js";
|
|
import { resolveRequestCacheStore } from "@/external/storage/cache/resolveRequestCacheStore.js";
|
|
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
|
import { generateId } from "@/utils/genUtils.js";
|
|
import { addRequestToLogs } from "@/utils/logging/addContextToLogs";
|
|
import { resolveCustomerId } from "./utils/resolveCustomerId.js";
|
|
import { resolveEntityId } from "./utils/resolveEntityId.js";
|
|
|
|
const SENSITIVE_REQUEST_BODY_KEYS = new Set(["connectionString"]);
|
|
const REDACTED_REQUEST_BODY_VALUE = "[REDACTED]";
|
|
|
|
const redactSensitiveRequestBody = ({ body }: { body: unknown }): unknown => {
|
|
if (!body || typeof body !== "object") return body;
|
|
|
|
if (Array.isArray(body)) {
|
|
return body.map((item) => redactSensitiveRequestBody({ body: item }));
|
|
}
|
|
|
|
return Object.fromEntries(
|
|
Object.entries(body).map(([key, value]) => [
|
|
key,
|
|
SENSITIVE_REQUEST_BODY_KEYS.has(key)
|
|
? REDACTED_REQUEST_BODY_VALUE
|
|
: redactSensitiveRequestBody({ body: value }),
|
|
]),
|
|
);
|
|
};
|
|
|
|
const shouldSkipRequestBodyParse = ({
|
|
method,
|
|
path,
|
|
}: {
|
|
method: string;
|
|
path: string;
|
|
}) => {
|
|
if (method === "GET" || method === "HEAD") return true;
|
|
return path.startsWith("/webhooks/stripe/") || path.startsWith("/webhooks/connect/");
|
|
};
|
|
|
|
/**
|
|
* Base middleware that sets up the request context
|
|
* Sets up: db, logger, id, timestamp
|
|
*/
|
|
export const baseMiddleware = async (c: Context<HonoEnv>, next: Next) => {
|
|
const env = c.env;
|
|
const logger = createLogger(env);
|
|
// const env = (c.req.header("app_env") as AppEnv) || AppEnv.Sandbox;
|
|
const id =
|
|
c.req.header("rndr-id") ||
|
|
c.req.header("X-Amzn-Trace-Id") ||
|
|
c.req.header("x-amzn-trace-id") ||
|
|
generateId("local_req");
|
|
|
|
const timestamp = Date.now();
|
|
|
|
const { data: body } = shouldSkipRequestBodyParse({
|
|
method: c.req.method,
|
|
path: c.req.path,
|
|
})
|
|
? { data: undefined }
|
|
: await tryCatch(c.req.json());
|
|
|
|
const customerId = resolveCustomerId({
|
|
method: c.req.method,
|
|
path: c.req.path,
|
|
body,
|
|
query: c.req.query(),
|
|
});
|
|
const entityId = resolveEntityId({
|
|
method: c.req.method,
|
|
path: c.req.path,
|
|
body,
|
|
query: c.req.query(),
|
|
});
|
|
|
|
const childLogger = addRequestToLogs({
|
|
logger,
|
|
requestContext: {
|
|
id,
|
|
method: c.req.method,
|
|
url: c.req.url,
|
|
timestamp,
|
|
customer_id: customerId,
|
|
entity_id: entityId,
|
|
user_agent: c.req.header("user-agent"),
|
|
ip_address: c.req.header("x-forwarded-for"),
|
|
region: c.env.AWS_REGION,
|
|
query: c.req.query(),
|
|
body: redactSensitiveRequestBody({ body }),
|
|
|
|
name: `${c.req.method} ${c.req.path}`,
|
|
},
|
|
});
|
|
|
|
const cacheStore = resolveRequestCacheStore({ env: c.env });
|
|
const requestDb = createRequestScopedDb(env);
|
|
|
|
// Set up the request context
|
|
c.set("ctx", {
|
|
// Core objects
|
|
db: requestDb.db,
|
|
logger: childLogger,
|
|
workerEnv: c.env,
|
|
cacheStore,
|
|
|
|
// Request info
|
|
id,
|
|
timestamp,
|
|
isPublic: false,
|
|
apiVersion: new ApiVersionClass(LATEST_VERSION),
|
|
|
|
// Auth (will be populated by auth middleware)
|
|
org: undefined as unknown as Organization,
|
|
features: [],
|
|
userId: undefined,
|
|
customerId,
|
|
entityId,
|
|
authType: AuthType.Unknown,
|
|
env: AppEnv.Sandbox, // maybe use app_env headers
|
|
scopes: [],
|
|
|
|
// Query params
|
|
expand: [],
|
|
skipCache:
|
|
c.req.header("x-skip-cache") === "true" ||
|
|
c.req.query("skip_cache") === "true",
|
|
|
|
// Test params:
|
|
extraLogs: {},
|
|
|
|
testOptions: {
|
|
eventId: c.req.header("x-event-id"),
|
|
skipCacheDeletion: c.req.header("x-skip-cache-deletion") === "true",
|
|
skipWebhooks: c.req.header("x-skip-webhooks") === "true",
|
|
keepInternalFields: c.req.header("x-strip-internal") === "false",
|
|
mockVercelApi: c.req.header("x-mock-vercel-api") === "true",
|
|
allowVercelTestOidc:
|
|
c.env.NODE_ENV !== "production" &&
|
|
c.req.header("x-allow-vercel-test-oidc") === "true",
|
|
},
|
|
});
|
|
|
|
// childLogger.info(`${method} ${path}`);
|
|
|
|
try {
|
|
await next();
|
|
} finally {
|
|
c.executionCtx.waitUntil(requestDb.dispose());
|
|
}
|
|
};
|