smartproxy/ts/proxies/smart-proxy/acme-state-manager.ts

112 lines
3.2 KiB
TypeScript

import type { IRouteConfig } from './models/route-types.js';
/**
* Global state store for ACME operations
* Tracks active challenge routes and port allocations
*/
export class AcmeStateManager {
private activeChallengeRoutes: Map<string, IRouteConfig> = new Map();
private acmePortAllocations: Set<number> = new Set();
private primaryChallengeRoute: IRouteConfig | null = null;
/**
* Check if a challenge route is active
*/
public isChallengeRouteActive(): boolean {
return this.activeChallengeRoutes.size > 0;
}
/**
* Register a challenge route as active
*/
public addChallengeRoute(route: IRouteConfig): void {
this.activeChallengeRoutes.set(route.name, route);
// Track the primary challenge route
if (!this.primaryChallengeRoute || route.priority > (this.primaryChallengeRoute.priority || 0)) {
this.primaryChallengeRoute = route;
}
// Track port allocations
const ports = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
ports.forEach(port => this.acmePortAllocations.add(port));
}
/**
* Remove a challenge route
*/
public removeChallengeRoute(routeName: string): void {
const route = this.activeChallengeRoutes.get(routeName);
if (!route) return;
this.activeChallengeRoutes.delete(routeName);
// Update primary challenge route if needed
if (this.primaryChallengeRoute?.name === routeName) {
this.primaryChallengeRoute = null;
// Find new primary route with highest priority
let highestPriority = -1;
for (const [_, activeRoute] of this.activeChallengeRoutes) {
const priority = activeRoute.priority || 0;
if (priority > highestPriority) {
highestPriority = priority;
this.primaryChallengeRoute = activeRoute;
}
}
}
// Update port allocations - only remove if no other routes use this port
const ports = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
ports.forEach(port => {
let portStillUsed = false;
for (const [_, activeRoute] of this.activeChallengeRoutes) {
const activePorts = Array.isArray(activeRoute.match.ports) ?
activeRoute.match.ports : [activeRoute.match.ports];
if (activePorts.includes(port)) {
portStillUsed = true;
break;
}
}
if (!portStillUsed) {
this.acmePortAllocations.delete(port);
}
});
}
/**
* Get all active challenge routes
*/
public getActiveChallengeRoutes(): IRouteConfig[] {
return Array.from(this.activeChallengeRoutes.values());
}
/**
* Get the primary challenge route
*/
public getPrimaryChallengeRoute(): IRouteConfig | null {
return this.primaryChallengeRoute;
}
/**
* Check if a port is allocated for ACME
*/
public isPortAllocatedForAcme(port: number): boolean {
return this.acmePortAllocations.has(port);
}
/**
* Get all ACME ports
*/
public getAcmePorts(): number[] {
return Array.from(this.acmePortAllocations);
}
/**
* Clear all state (for shutdown or reset)
*/
public clear(): void {
this.activeChallengeRoutes.clear();
this.acmePortAllocations.clear();
this.primaryChallengeRoute = null;
}
}