Files

93 lines
2.9 KiB
TypeScript
Raw Permalink Normal View History

2026-04-29 19:48:14 +00:00
import { UptimeRunnerApiClient } from './api-client.ts';
import { CheckExecutor } from './check-executor.ts';
import type {
IRunOnceResult,
IUptimeCheckResult,
IUptimeRunnerConfig,
TCheckJob,
} from './interfaces.ts';
import denoConfig from '../deno.json' with { type: 'json' };
export class UptimeRunner {
private readonly apiClient: UptimeRunnerApiClient;
private readonly checkExecutor: CheckExecutor;
private running = false;
constructor(private readonly config: IUptimeRunnerConfig, apiClientArg?: UptimeRunnerApiClient) {
this.apiClient = apiClientArg ?? new UptimeRunnerApiClient(config);
this.checkExecutor = new CheckExecutor(config.runnerId);
}
public async run(): Promise<void> {
this.running = true;
console.log(`uptimerunner ${this.config.runnerId} connected to ${this.config.instanceUrl}`);
while (this.running) {
const started = Date.now();
try {
await this.heartbeat().catch((error) => {
console.warn(
`heartbeat failed: ${error instanceof Error ? error.message : String(error)}`,
);
});
const result = await this.runOnce();
if (result.results.length > 0) {
console.log(`reported ${result.results.length} check result(s)`);
}
} catch (error) {
console.error(
`runner iteration failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
const elapsed = Date.now() - started;
const delayMs = Math.max((this.config.pollIntervalMs ?? 30000) - elapsed, 1000);
await delayFor(delayMs);
}
}
public stop(): void {
this.running = false;
}
public async runOnce(): Promise<IRunOnceResult> {
const checks = await this.apiClient.fetchAssignedChecks(
this.config.runnerId,
this.config.labels ?? [],
);
const results = await this.executeChecks(checks);
await this.apiClient.submitResults(this.config.runnerId, results);
return { checks, results };
}
private async heartbeat(): Promise<void> {
await this.apiClient.heartbeat({
runnerId: this.config.runnerId,
labels: this.config.labels,
version: denoConfig.version,
});
}
private async executeChecks(checksArg: TCheckJob[]): Promise<IUptimeCheckResult[]> {
const maxConcurrentChecks = this.config.maxConcurrentChecks ?? 8;
const results: IUptimeCheckResult[] = [];
let nextIndex = 0;
const worker = async () => {
while (nextIndex < checksArg.length) {
const currentIndex = nextIndex++;
results[currentIndex] = await this.checkExecutor.execute(checksArg[currentIndex]);
}
};
await Promise.all(
Array.from({ length: Math.min(maxConcurrentChecks, checksArg.length) }, () => worker()),
);
return results;
}
}
async function delayFor(millisecondsArg: number): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, millisecondsArg));
}