Files
multi-simadmin/apps/api/src/control-plane.ts
T

65 lines
2.8 KiB
TypeScript

import type Database from 'better-sqlite3';
import type { FastifyInstance } from 'fastify';
import { buildApp, type BuildAppOptions } from './app.js';
import {
ConnectionProbe,
type ConnectionTransport,
} from './application/connections/connection-probe.js';
import { InstanceCredentialResolver } from './application/connections/instance-credential-resolver.js';
import { InstanceLoginService } from './application/connections/instance-login-service.js';
import {
InstanceSessionStore,
UpstreamSessionClient,
type UpstreamSessionClientOptions,
} from './application/connections/upstream-session-client.js';
import { InstanceService } from './application/instances/instance-service.js';
import { DeleteInstanceOperation } from './application/operations/delete-instance-operation.js';
import type { SecretStore } from './infrastructure/secrets/secret-store.js';
import { registerInstanceRoutes } from './interface/http/instance-routes.js';
export interface SafeControlPlaneUpstream extends ConnectionTransport {
request: UpstreamSessionClientOptions['request'];
}
export interface ControlPlaneOptions {
readonly db: Database.Database;
readonly store: SecretStore;
readonly upstream: SafeControlPlaneUpstream;
readonly now?: () => Date;
readonly app?: Omit<BuildAppOptions, 'registerRoutes'>;
}
export interface ControlPlaneApp extends FastifyInstance {
retryPendingSecretCleanup(): Promise<unknown>;
}
export function buildControlPlaneApp(options: ControlPlaneOptions): ControlPlaneApp {
const instances = options.now
? new InstanceService({ db: options.db, store: options.store, now: options.now })
: new InstanceService({ db: options.db, store: options.store });
const connections = options.now
? new ConnectionProbe({
db: options.db,
instances,
transport: options.upstream,
now: options.now,
})
: new ConnectionProbe({ db: options.db, instances, transport: options.upstream });
const sessions = new InstanceSessionStore();
const client = new UpstreamSessionClient({ sessions, request: options.upstream.request });
const resolver = new InstanceCredentialResolver({ db: options.db, store: options.store });
const login = options.now
? new InstanceLoginService({ db: options.db, client, resolver, now: options.now })
: new InstanceLoginService({ db: options.db, client, resolver });
const deletion = options.now
? new DeleteInstanceOperation({ db: options.db, instances, now: options.now })
: new DeleteInstanceOperation({ db: options.db, instances });
deletion.reconcileInterruptedJobs();
const app = buildApp({
...options.app,
registerRoutes: (app) =>
registerInstanceRoutes(app, { instances, connections, login, deletion }),
});
Object.assign(app, {
retryPendingSecretCleanup: () => instances.retryPendingSecretCleanup(),
});
return app as ControlPlaneApp;
}