import * as interfaces from '../ts_interfaces/index.js'; import type { IRouteConfig } from '@push.rocks/smartproxy'; import type { DcRouterApiClient } from './classes.dcrouterapiclient.js'; export class Route { private clientRef: DcRouterApiClient; // Data from IMergedRoute public routeConfig: IRouteConfig; public source: 'hardcoded' | 'programmatic'; public enabled: boolean; public overridden: boolean; public storedRouteId?: string; public createdAt?: number; public updatedAt?: number; // Convenience accessors public get name(): string { return this.routeConfig.name || ''; } constructor(clientRef: DcRouterApiClient, data: interfaces.data.IMergedRoute) { this.clientRef = clientRef; this.routeConfig = data.route; this.source = data.source; this.enabled = data.enabled; this.overridden = data.overridden; this.storedRouteId = data.storedRouteId; this.createdAt = data.createdAt; this.updatedAt = data.updatedAt; } public async update(changes: Partial): Promise { if (!this.storedRouteId) { throw new Error('Cannot update a hardcoded route. Use setOverride() instead.'); } const response = await this.clientRef.request( 'updateRoute', this.clientRef.buildRequestPayload({ id: this.storedRouteId, route: changes }) as any, ); if (!response.success) { throw new Error(response.message || 'Failed to update route'); } } public async delete(): Promise { if (!this.storedRouteId) { throw new Error('Cannot delete a hardcoded route. Use setOverride() instead.'); } const response = await this.clientRef.request( 'deleteRoute', this.clientRef.buildRequestPayload({ id: this.storedRouteId }) as any, ); if (!response.success) { throw new Error(response.message || 'Failed to delete route'); } } public async toggle(enabled: boolean): Promise { if (!this.storedRouteId) { throw new Error('Cannot toggle a hardcoded route. Use setOverride() instead.'); } const response = await this.clientRef.request( 'toggleRoute', this.clientRef.buildRequestPayload({ id: this.storedRouteId, enabled }) as any, ); if (!response.success) { throw new Error(response.message || 'Failed to toggle route'); } this.enabled = enabled; } public async setOverride(enabled: boolean): Promise { const response = await this.clientRef.request( 'setRouteOverride', this.clientRef.buildRequestPayload({ routeName: this.name, enabled }) as any, ); if (!response.success) { throw new Error(response.message || 'Failed to set route override'); } this.overridden = true; this.enabled = enabled; } public async removeOverride(): Promise { const response = await this.clientRef.request( 'removeRouteOverride', this.clientRef.buildRequestPayload({ routeName: this.name }) as any, ); if (!response.success) { throw new Error(response.message || 'Failed to remove route override'); } this.overridden = false; } } export class RouteBuilder { private clientRef: DcRouterApiClient; private routeConfig: Partial = {}; private isEnabled: boolean = true; constructor(clientRef: DcRouterApiClient) { this.clientRef = clientRef; } public setName(name: string): this { this.routeConfig.name = name; return this; } public setMatch(match: IRouteConfig['match']): this { this.routeConfig.match = match; return this; } public setAction(action: IRouteConfig['action']): this { this.routeConfig.action = action; return this; } public setTls(tls: IRouteConfig['action']['tls']): this { if (!this.routeConfig.action) { this.routeConfig.action = { type: 'forward' } as IRouteConfig['action']; } this.routeConfig.action!.tls = tls; return this; } public setEnabled(enabled: boolean): this { this.isEnabled = enabled; return this; } public async save(): Promise { const response = await this.clientRef.request( 'createRoute', this.clientRef.buildRequestPayload({ route: this.routeConfig as IRouteConfig, enabled: this.isEnabled, }) as any, ); if (!response.success) { throw new Error(response.message || 'Failed to create route'); } // Return a Route instance by re-fetching the list // The created route is programmatic, so we find it by storedRouteId const { routes } = await new RouteManager(this.clientRef).list(); const created = routes.find((r) => r.storedRouteId === response.storedRouteId); if (created) { return created; } // Fallback: construct from known data return new Route(this.clientRef, { route: this.routeConfig as IRouteConfig, source: 'programmatic', enabled: this.isEnabled, overridden: false, storedRouteId: response.storedRouteId, }); } } export class RouteManager { private clientRef: DcRouterApiClient; constructor(clientRef: DcRouterApiClient) { this.clientRef = clientRef; } public async list(): Promise<{ routes: Route[]; warnings: interfaces.data.IRouteWarning[] }> { const response = await this.clientRef.request( 'getMergedRoutes', this.clientRef.buildRequestPayload() as any, ); return { routes: response.routes.map((r) => new Route(this.clientRef, r)), warnings: response.warnings, }; } public async create(routeConfig: IRouteConfig, enabled?: boolean): Promise { const response = await this.clientRef.request( 'createRoute', this.clientRef.buildRequestPayload({ route: routeConfig, enabled: enabled ?? true }) as any, ); if (!response.success) { throw new Error(response.message || 'Failed to create route'); } return new Route(this.clientRef, { route: routeConfig, source: 'programmatic', enabled: enabled ?? true, overridden: false, storedRouteId: response.storedRouteId, }); } public build(): RouteBuilder { return new RouteBuilder(this.clientRef); } }