112 lines
3.2 KiB
TypeScript
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;
|
|
}
|
|
} |