118 lines
4.3 KiB
TypeScript
118 lines
4.3 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import { GatewayClientDoc } from '../db/index.js';
|
|
import type { IGatewayClient } from '../../ts_interfaces/data/workhoster.js';
|
|
|
|
const defaultCapabilities: IGatewayClient['capabilities'] = {
|
|
readDomains: true,
|
|
readDnsRecords: true,
|
|
syncRoutes: true,
|
|
syncDnsRecords: false,
|
|
requestCertificates: false,
|
|
};
|
|
|
|
export class GatewayClientManager {
|
|
public async initialize(): Promise<void> {}
|
|
|
|
public async listClients(): Promise<IGatewayClient[]> {
|
|
const docs = await GatewayClientDoc.findAll();
|
|
return docs.map((doc) => this.toPublicClient(doc));
|
|
}
|
|
|
|
public async getClient(id: string): Promise<IGatewayClient | null> {
|
|
const doc = await GatewayClientDoc.findById(id);
|
|
return doc ? this.toPublicClient(doc) : null;
|
|
}
|
|
|
|
public async createClient(options: {
|
|
id?: string;
|
|
type: IGatewayClient['type'];
|
|
name: string;
|
|
description?: string;
|
|
hostnamePatterns?: string[];
|
|
allowedRouteTargets?: IGatewayClient['allowedRouteTargets'];
|
|
capabilities?: IGatewayClient['capabilities'];
|
|
createdBy: string;
|
|
}): Promise<IGatewayClient> {
|
|
const id = this.normalizeId(options.id || `${options.type}-${plugins.uuid.v4()}`);
|
|
if (!id) {
|
|
throw new Error('gateway client id is required');
|
|
}
|
|
if (await GatewayClientDoc.findById(id)) {
|
|
throw new Error('gateway client already exists');
|
|
}
|
|
|
|
const now = Date.now();
|
|
const doc = new GatewayClientDoc();
|
|
doc.id = id;
|
|
doc.type = options.type;
|
|
doc.name = options.name.trim();
|
|
doc.description = options.description?.trim() || undefined;
|
|
doc.hostnamePatterns = this.normalizeStringList(options.hostnamePatterns || []);
|
|
doc.allowedRouteTargets = this.normalizeAllowedRouteTargets(options.allowedRouteTargets || []);
|
|
doc.capabilities = { ...defaultCapabilities, ...(options.capabilities || {}) };
|
|
doc.enabled = true;
|
|
doc.createdAt = now;
|
|
doc.updatedAt = now;
|
|
doc.createdBy = options.createdBy;
|
|
await doc.save();
|
|
return this.toPublicClient(doc);
|
|
}
|
|
|
|
public async updateClient(
|
|
id: string,
|
|
patch: Partial<Pick<IGatewayClient, 'name' | 'description' | 'hostnamePatterns' | 'allowedRouteTargets' | 'capabilities' | 'enabled'>>,
|
|
): Promise<IGatewayClient | null> {
|
|
const doc = await GatewayClientDoc.findById(id);
|
|
if (!doc) return null;
|
|
if (patch.name !== undefined) doc.name = patch.name.trim();
|
|
if (patch.description !== undefined) doc.description = patch.description.trim() || undefined;
|
|
if (patch.hostnamePatterns !== undefined) doc.hostnamePatterns = this.normalizeStringList(patch.hostnamePatterns);
|
|
if (patch.allowedRouteTargets !== undefined) doc.allowedRouteTargets = this.normalizeAllowedRouteTargets(patch.allowedRouteTargets);
|
|
if (patch.capabilities !== undefined) doc.capabilities = { ...defaultCapabilities, ...patch.capabilities };
|
|
if (patch.enabled !== undefined) doc.enabled = patch.enabled;
|
|
doc.updatedAt = Date.now();
|
|
await doc.save();
|
|
return this.toPublicClient(doc);
|
|
}
|
|
|
|
public async deleteClient(id: string): Promise<boolean> {
|
|
const doc = await GatewayClientDoc.findById(id);
|
|
if (!doc) return false;
|
|
await doc.delete();
|
|
return true;
|
|
}
|
|
|
|
private normalizeId(id: string): string {
|
|
return id.trim().toLowerCase().replace(/[^a-z0-9._-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
}
|
|
|
|
private normalizeStringList(values: string[]): string[] {
|
|
return values.map((value) => value.trim().toLowerCase()).filter(Boolean);
|
|
}
|
|
|
|
private normalizeAllowedRouteTargets(targets: IGatewayClient['allowedRouteTargets']): IGatewayClient['allowedRouteTargets'] {
|
|
return targets
|
|
.map((target) => ({
|
|
host: target.host.trim().toLowerCase(),
|
|
ports: target.ports.filter((port) => Number.isInteger(port) && port > 0 && port <= 65535),
|
|
}))
|
|
.filter((target) => target.host && target.ports.length > 0);
|
|
}
|
|
|
|
private toPublicClient(doc: GatewayClientDoc): IGatewayClient {
|
|
return {
|
|
id: doc.id,
|
|
type: doc.type,
|
|
name: doc.name,
|
|
description: doc.description,
|
|
hostnamePatterns: doc.hostnamePatterns || [],
|
|
allowedRouteTargets: doc.allowedRouteTargets || [],
|
|
capabilities: doc.capabilities || {},
|
|
enabled: doc.enabled,
|
|
createdAt: doc.createdAt,
|
|
updatedAt: doc.updatedAt,
|
|
createdBy: doc.createdBy,
|
|
};
|
|
}
|
|
}
|