67 lines
2.4 KiB
TypeScript
67 lines
2.4 KiB
TypeScript
import {
|
|
createTenantMiddleware,
|
|
type TenantResolver,
|
|
} from "@suaveplan/api-tenant";
|
|
import type { Context, MiddlewareHandler, Next } from "hono";
|
|
import { getTenantPluginInstruments } from "../otel/instruments.js";
|
|
|
|
/** Options accepted by {@link buildTenantPluginMiddleware}. */
|
|
export interface BuildTenantPluginMiddlewareOptions {
|
|
readonly resolver: TenantResolver;
|
|
readonly required: boolean;
|
|
readonly allowSuspended: boolean;
|
|
readonly excludePaths?: readonly string[];
|
|
}
|
|
|
|
/**
|
|
* Build the Hono middleware installed by `createTenantPlugin`'s
|
|
* `onInit`. Composes `createTenantMiddleware` from `@suaveplan/api-tenant`
|
|
* with an outer wrapper that records OTel resolution timing + outcome
|
|
* for every request — even the short-circuited / unresolved paths.
|
|
*
|
|
* The inner middleware is the source of truth for `c.set("tenant",
|
|
* tenant)`, status-gating (deleted/suspended/pending), and the `required`
|
|
* branch. The wrapper just observes.
|
|
*/
|
|
export function buildTenantPluginMiddleware(
|
|
options: BuildTenantPluginMiddlewareOptions
|
|
): MiddlewareHandler {
|
|
const { resolver, required, allowSuspended, excludePaths } = options;
|
|
const inner = createTenantMiddleware({
|
|
resolver,
|
|
required,
|
|
allowSuspended,
|
|
...(excludePaths !== undefined ? { excludePaths } : {}),
|
|
});
|
|
return async function tenantPluginMiddleware(
|
|
c: Context,
|
|
next: Next
|
|
): Promise<Response | undefined> {
|
|
const instruments = getTenantPluginInstruments();
|
|
const start = performance.now();
|
|
try {
|
|
const result = await inner(c, next);
|
|
const elapsed = performance.now() - start;
|
|
const tenant = c.get("tenant");
|
|
instruments.resolutionsTotal.add(1, {
|
|
outcome: tenant !== undefined ? "resolved" : "missing",
|
|
strategy: resolver.name,
|
|
});
|
|
instruments.resolutionDurationMs.record(elapsed, {
|
|
strategy: resolver.name,
|
|
});
|
|
return result ?? undefined;
|
|
} catch (err) {
|
|
const elapsed = performance.now() - start;
|
|
instruments.resolutionsTotal.add(1, {
|
|
outcome: "error",
|
|
strategy: resolver.name,
|
|
});
|
|
instruments.resolutionDurationMs.record(elapsed, {
|
|
strategy: resolver.name,
|
|
});
|
|
throw err;
|
|
}
|
|
};
|
|
}
|