Files
smartdb/ts_debugserver/classes.debugserver.ts

121 lines
3.6 KiB
TypeScript
Raw Normal View History

import * as plugins from './plugins.js';
import { files as bundledFiles } from './bundled.js';
import type { SmartdbServer } from '../ts/index.js';
export interface IDebugServerOptions {
/** Port to listen on (default: 4000). */
port?: number;
}
/**
* Serves the SmartDB debug UI as a web application with API proxy endpoints.
*
* @example
* ```typescript
* import { SmartdbServer } from '@push.rocks/smartdb';
* import { SmartdbDebugServer } from '@push.rocks/smartdb/debugserver';
*
* const db = new SmartdbServer({ storage: 'memory' });
* await db.start();
*
* const debugServer = new SmartdbDebugServer(db, { port: 4000 });
* await debugServer.start();
* // Open http://localhost:4000
* ```
*/
export class SmartdbDebugServer {
private server: plugins.typedserver.TypedServer;
private smartdbServer: SmartdbServer;
private port: number;
constructor(smartdbServer: SmartdbServer, options: IDebugServerOptions = {}) {
this.smartdbServer = smartdbServer;
this.port = options.port ?? 4000;
this.server = new plugins.typedserver.TypedServer({
cors: true,
port: this.port,
bundledContent: bundledFiles,
spaFallback: true,
noCache: true,
});
this.setupApiRoutes();
}
private setupApiRoutes() {
const jsonResponse = (data: any, status = 200) =>
new Response(JSON.stringify(data), {
status,
headers: { 'Content-Type': 'application/json' },
});
// Metrics
this.server.addRoute('/api/smartdb/metrics', 'GET', async () => {
return jsonResponse(await this.smartdbServer.getMetrics());
});
// OpLog stats
this.server.addRoute('/api/smartdb/oplog/stats', 'GET', async () => {
return jsonResponse(await this.smartdbServer.getOpLogStats());
});
// OpLog entries
this.server.addRoute('/api/smartdb/oplog', 'GET', async (ctx) => {
const result = await this.smartdbServer.getOpLog({
sinceSeq: ctx.query.sinceSeq ? parseInt(ctx.query.sinceSeq) : undefined,
limit: ctx.query.limit ? parseInt(ctx.query.limit) : undefined,
db: ctx.query.db || undefined,
collection: ctx.query.collection || undefined,
});
return jsonResponse(result);
});
// Collections
this.server.addRoute('/api/smartdb/collections', 'GET', async (ctx) => {
const collections = await this.smartdbServer.getCollections(ctx.query.db || undefined);
return jsonResponse({ collections });
});
// Documents
this.server.addRoute('/api/smartdb/documents', 'GET', async (ctx) => {
const { db, collection } = ctx.query;
if (!db || !collection) {
return jsonResponse({ error: 'db and collection required' }, 400);
}
const limit = parseInt(ctx.query.limit ?? '50');
const skip = parseInt(ctx.query.skip ?? '0');
const result = await this.smartdbServer.getDocuments(db, collection, limit, skip);
return jsonResponse(result);
});
// Revert
this.server.addRoute('/api/smartdb/revert', 'GET', async (ctx) => {
const { seq, dryRun } = ctx.query;
if (!seq) {
return jsonResponse({ error: 'seq required' }, 400);
}
const result = await this.smartdbServer.revertToSeq(
parseInt(seq),
dryRun === 'true',
);
return jsonResponse(result);
});
}
/**
* Start the debug server.
*/
async start(): Promise<void> {
await this.server.start();
console.log(`SmartDB Debug UI available at http://localhost:${this.port}`);
}
/**
* Stop the debug server.
*/
async stop(): Promise<void> {
await this.server.stop();
}
}