feat(registry): add declarative protocol routing and request-scoped storage hook context across registries
This commit is contained in:
@@ -1,14 +1,131 @@
|
||||
import type { IRequestContext, IResponse, IAuthToken } from './interfaces.core.js';
|
||||
import * as plugins from '../plugins.js';
|
||||
import type { IRequestContext, IResponse, IAuthToken, IRequestActor } from './interfaces.core.js';
|
||||
|
||||
/**
|
||||
* Abstract base class for all registry protocol implementations
|
||||
*/
|
||||
export abstract class BaseRegistry {
|
||||
protected getHeader(contextOrHeaders: IRequestContext | Record<string, string>, name: string): string | undefined {
|
||||
const headers = 'headers' in contextOrHeaders ? contextOrHeaders.headers : contextOrHeaders;
|
||||
if (headers[name] !== undefined) {
|
||||
return headers[name];
|
||||
}
|
||||
|
||||
const lowerName = name.toLowerCase();
|
||||
for (const [headerName, value] of Object.entries(headers)) {
|
||||
if (headerName.toLowerCase() === lowerName) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected getAuthorizationHeader(context: IRequestContext): string | undefined {
|
||||
return this.getHeader(context, 'authorization');
|
||||
}
|
||||
|
||||
protected getClientIp(context: IRequestContext): string | undefined {
|
||||
const forwardedFor = this.getHeader(context, 'x-forwarded-for');
|
||||
if (forwardedFor) {
|
||||
return forwardedFor.split(',')[0]?.trim();
|
||||
}
|
||||
|
||||
return this.getHeader(context, 'x-real-ip');
|
||||
}
|
||||
|
||||
protected getUserAgent(context: IRequestContext): string | undefined {
|
||||
return this.getHeader(context, 'user-agent');
|
||||
}
|
||||
|
||||
protected extractBearerToken(contextOrHeader: IRequestContext | string | undefined): string | null {
|
||||
const authHeader = typeof contextOrHeader === 'string'
|
||||
? contextOrHeader
|
||||
: contextOrHeader
|
||||
? this.getAuthorizationHeader(contextOrHeader)
|
||||
: undefined;
|
||||
|
||||
if (!authHeader || !/^Bearer\s+/i.test(authHeader)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return authHeader.replace(/^Bearer\s+/i, '');
|
||||
}
|
||||
|
||||
protected parseBasicAuthHeader(authHeader: string | undefined): { username: string; password: string } | null {
|
||||
if (!authHeader || !/^Basic\s+/i.test(authHeader)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const base64 = authHeader.replace(/^Basic\s+/i, '');
|
||||
const decoded = Buffer.from(base64, 'base64').toString('utf-8');
|
||||
const separatorIndex = decoded.indexOf(':');
|
||||
|
||||
if (separatorIndex < 0) {
|
||||
return {
|
||||
username: decoded,
|
||||
password: '',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
username: decoded.substring(0, separatorIndex),
|
||||
password: decoded.substring(separatorIndex + 1),
|
||||
};
|
||||
}
|
||||
|
||||
protected buildRequestActor(context: IRequestContext, token: IAuthToken | null): IRequestActor {
|
||||
const actor: IRequestActor = {
|
||||
...(context.actor ?? {}),
|
||||
};
|
||||
|
||||
if (token?.userId) {
|
||||
actor.userId = token.userId;
|
||||
}
|
||||
|
||||
const ip = this.getClientIp(context);
|
||||
if (ip) {
|
||||
actor.ip = ip;
|
||||
}
|
||||
|
||||
const userAgent = this.getUserAgent(context);
|
||||
if (userAgent) {
|
||||
actor.userAgent = userAgent;
|
||||
}
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
protected createProtocolLogger(
|
||||
containerName: string,
|
||||
zone: string
|
||||
): plugins.smartlog.Smartlog {
|
||||
const logger = new plugins.smartlog.Smartlog({
|
||||
logContext: {
|
||||
company: 'push.rocks',
|
||||
companyunit: 'smartregistry',
|
||||
containerName,
|
||||
environment: (process.env.NODE_ENV as any) || 'development',
|
||||
runtime: 'node',
|
||||
zone,
|
||||
}
|
||||
});
|
||||
logger.enableConsole();
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the registry
|
||||
*/
|
||||
abstract init(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Clean up timers, connections, and other registry resources.
|
||||
*/
|
||||
public destroy(): void {
|
||||
// Default no-op for registries without persistent resources.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming HTTP request
|
||||
* @param context - Request context
|
||||
|
||||
Reference in New Issue
Block a user