feat(route-management): add programmatic route management API with API tokens and admin UI

This commit is contained in:
2026-02-23 12:40:26 +00:00
parent 90016d1217
commit f5028ffb60
33 changed files with 2183 additions and 30 deletions

View File

@@ -0,0 +1,96 @@
import * as plugins from '../../plugins.js';
import type { OpsServer } from '../classes.opsserver.js';
import * as interfaces from '../../../ts_interfaces/index.js';
export class ApiTokenHandler {
public typedrouter = new plugins.typedrequest.TypedRouter();
constructor(private opsServerRef: OpsServer) {
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
this.registerHandlers();
}
/**
* Token management requires admin JWT only (tokens cannot manage tokens).
*/
private async requireAdmin(identity?: interfaces.data.IIdentity): Promise<string> {
if (!identity?.jwt) {
throw new plugins.typedrequest.TypedResponseError('unauthorized');
}
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({ identity });
if (!isAdmin) {
throw new plugins.typedrequest.TypedResponseError('admin access required');
}
return identity.userId;
}
private registerHandlers(): void {
// Create API token
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateApiToken>(
'createApiToken',
async (dataArg) => {
const userId = await this.requireAdmin(dataArg.identity);
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
if (!manager) {
return { success: false, message: 'Token management not initialized' };
}
const result = await manager.createToken(
dataArg.name,
dataArg.scopes,
dataArg.expiresInDays ?? null,
userId,
);
return { success: true, tokenId: result.id, tokenValue: result.rawToken };
},
),
);
// List API tokens
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListApiTokens>(
'listApiTokens',
async (dataArg) => {
await this.requireAdmin(dataArg.identity);
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
if (!manager) {
return { tokens: [] };
}
return { tokens: manager.listTokens() };
},
),
);
// Revoke API token
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RevokeApiToken>(
'revokeApiToken',
async (dataArg) => {
await this.requireAdmin(dataArg.identity);
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
if (!manager) {
return { success: false, message: 'Token management not initialized' };
}
const ok = await manager.revokeToken(dataArg.id);
return { success: ok, message: ok ? undefined : 'Token not found' };
},
),
);
// Toggle API token
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ToggleApiToken>(
'toggleApiToken',
async (dataArg) => {
await this.requireAdmin(dataArg.identity);
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
if (!manager) {
return { success: false, message: 'Token management not initialized' };
}
const ok = await manager.toggleToken(dataArg.id, dataArg.enabled);
return { success: ok, message: ok ? undefined : 'Token not found' };
},
),
);
}
}