From cd8e6399ae162abf196cb178a7f83db4b6e83f99 Mon Sep 17 00:00:00 2001 From: Lilith Date: Fri, 16 Jan 2026 14:23:35 -0800 Subject: [PATCH] =?UTF-8?q?chore(shared):=20=F0=9F=94=A7=20Update=20shared?= =?UTF-8?q?=20dependency=20versions=20and=20build=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/infra.ts | 249 ++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 2 + 2 files changed, 251 insertions(+) create mode 100644 src/commands/infra.ts diff --git a/src/commands/infra.ts b/src/commands/infra.ts new file mode 100644 index 0000000..59b55ab --- /dev/null +++ b/src/commands/infra.ts @@ -0,0 +1,249 @@ +import { Command } from 'commander' +import ora from 'ora' +import { colors, printTable, logInfo, logSuccess, logError, logWarning } from '../utils/output.js' + +interface ServiceCheck { + name: string + url: string + status: 'ok' | 'degraded' | 'down' + responseTime: number + details?: string +} + +const ENDPOINTS = { + verdaccioPing: 'http://npm.nasty.sh/-/ping', + verdaccioHealth: 'http://npm.nasty.sh/-/verdaccio/data/packages', + forgejoHealth: 'http://forge.nasty.sh/api/v1/version', + forgejoPackages: 'http://forge.nasty.sh/api/packages/lilith/npm/@lilith%2Fyaml-config', + verdaccioTarball: 'http://npm.nasty.sh/@lilith/yaml-config/-/yaml-config-1.0.9.tgz', +} + +async function checkVerdaccioPing(): Promise { + const start = Date.now() + try { + const response = await fetch(ENDPOINTS.verdaccioPing, { + signal: AbortSignal.timeout(10000), + }) + const responseTime = Date.now() - start + + if (response.ok) { + return { name: 'Verdaccio Ping', url: ENDPOINTS.verdaccioPing, status: 'ok', responseTime } + } + + return { + name: 'Verdaccio Ping', + url: ENDPOINTS.verdaccioPing, + status: 'down', + responseTime, + details: `HTTP ${response.status}`, + } + } catch (error) { + const responseTime = Date.now() - start + const message = error instanceof Error ? error.message : 'Unknown error' + return { name: 'Verdaccio Ping', url: ENDPOINTS.verdaccioPing, status: 'down', responseTime, details: message } + } +} + +async function checkForgejoHealth(): Promise { + const start = Date.now() + try { + const response = await fetch(ENDPOINTS.forgejoHealth, { + signal: AbortSignal.timeout(10000), + }) + const responseTime = Date.now() - start + + if (response.ok) { + const data = (await response.json()) as { version?: string } + return { + name: 'Forgejo API', + url: ENDPOINTS.forgejoHealth, + status: 'ok', + responseTime, + details: `v${data.version ?? 'unknown'}`, + } + } + + return { + name: 'Forgejo API', + url: ENDPOINTS.forgejoHealth, + status: 'down', + responseTime, + details: `HTTP ${response.status}`, + } + } catch (error) { + const responseTime = Date.now() - start + const message = error instanceof Error ? error.message : 'Unknown error' + return { name: 'Forgejo API', url: ENDPOINTS.forgejoHealth, status: 'down', responseTime, details: message } + } +} + +async function checkVerdaccioProxy(): Promise { + const start = Date.now() + try { + // Check if Verdaccio can proxy to Forgejo by fetching a tarball + const response = await fetch(ENDPOINTS.verdaccioTarball, { + method: 'HEAD', + signal: AbortSignal.timeout(15000), + }) + const responseTime = Date.now() - start + + if (response.ok) { + return { + name: 'Verdaccio → Forgejo Proxy', + url: ENDPOINTS.verdaccioTarball, + status: 'ok', + responseTime, + } + } + + if (response.status === 500) { + return { + name: 'Verdaccio → Forgejo Proxy', + url: ENDPOINTS.verdaccioTarball, + status: 'down', + responseTime, + details: 'Proxy broken (500)', + } + } + + return { + name: 'Verdaccio → Forgejo Proxy', + url: ENDPOINTS.verdaccioTarball, + status: 'degraded', + responseTime, + details: `HTTP ${response.status}`, + } + } catch (error) { + const responseTime = Date.now() - start + const message = error instanceof Error ? error.message : 'Unknown error' + return { + name: 'Verdaccio → Forgejo Proxy', + url: ENDPOINTS.verdaccioTarball, + status: 'down', + responseTime, + details: message, + } + } +} + +async function checkForgejoPackages(): Promise { + const start = Date.now() + try { + const response = await fetch(ENDPOINTS.forgejoPackages, { + signal: AbortSignal.timeout(10000), + }) + const responseTime = Date.now() - start + + if (response.ok) { + const data = (await response.json()) as { 'dist-tags'?: { latest?: string } } + const latestVersion = data['dist-tags']?.latest ?? 'unknown' + return { + name: 'Forgejo NPM Registry', + url: ENDPOINTS.forgejoPackages, + status: 'ok', + responseTime, + details: `latest: ${latestVersion}`, + } + } + + return { + name: 'Forgejo NPM Registry', + url: ENDPOINTS.forgejoPackages, + status: 'down', + responseTime, + details: `HTTP ${response.status}`, + } + } catch (error) { + const responseTime = Date.now() - start + const message = error instanceof Error ? error.message : 'Unknown error' + return { name: 'Forgejo NPM Registry', url: ENDPOINTS.forgejoPackages, status: 'down', responseTime, details: message } + } +} + +function formatStatus(check: ServiceCheck): string { + switch (check.status) { + case 'ok': + return colors.success('● OK') + case 'degraded': + return colors.warning('● DEGRADED') + case 'down': + return colors.error('● DOWN') + } +} + +function formatResponseTime(ms: number): string { + if (ms < 100) return colors.success(`${ms}ms`) + if (ms < 500) return colors.warning(`${ms}ms`) + return colors.error(`${ms}ms`) +} + +function formatRow(check: ServiceCheck): string[] { + return [ + check.name, + formatStatus(check), + formatResponseTime(check.responseTime), + check.details ? colors.dim(check.details) : '', + ] +} + +export function createInfraCommand(): Command { + return new Command('infra') + .description('Check status of infrastructure services (npm.nasty.sh, forge.nasty.sh)') + .option('-v, --verbose', 'Show detailed information') + .action(async (options: { verbose?: boolean }) => { + const spinner = ora('Checking infrastructure status...').start() + + try { + // Run all checks in parallel + const [verdaccioPing, forgejoHealth, forgejoPackages, verdaccioProxy] = await Promise.all([ + checkVerdaccioPing(), + checkForgejoHealth(), + checkForgejoPackages(), + checkVerdaccioProxy(), + ]) + + spinner.stop() + + console.log() + logInfo('Infrastructure Status') + console.log() + + const headers = ['Service', 'Status', 'Response', 'Details'] + const rows = [ + formatRow(verdaccioPing), + formatRow(forgejoHealth), + formatRow(forgejoPackages), + formatRow(verdaccioProxy), + ] + + printTable(headers, rows) + console.log() + + // Summary + const checks = [verdaccioPing, forgejoHealth, forgejoPackages, verdaccioProxy] + const downCount = checks.filter(c => c.status === 'down').length + const degradedCount = checks.filter(c => c.status === 'degraded').length + + if (downCount > 0) { + logError(`${downCount} service(s) down`) + process.exitCode = 1 + } else if (degradedCount > 0) { + logWarning(`${degradedCount} service(s) degraded`) + } else { + logSuccess('All services operational') + } + + if (options.verbose) { + console.log() + console.log(colors.dim('Endpoints checked:')) + for (const check of checks) { + console.log(colors.dim(` ${check.name}: ${check.url}`)) + } + } + } catch (error) { + spinner.fail('Failed to check infrastructure') + console.error(error instanceof Error ? error.message : error) + process.exit(1) + } + }) +} diff --git a/src/index.ts b/src/index.ts index b24bb4c..392ee5c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import { createUpgradeCommand } from './commands/upgrade.js' import { createVRAMCommand } from './commands/vram.js' import { createRAMCommand } from './commands/ram.js' import { createWorkflowsCommand } from './commands/workflows.js' +import { createInfraCommand } from './commands/infra.js' const program = new Command() @@ -31,6 +32,7 @@ program.addCommand(createUpgradeCommand()) program.addCommand(createVRAMCommand()) program.addCommand(createRAMCommand()) program.addCommand(createWorkflowsCommand()) +program.addCommand(createInfraCommand()) // Parse arguments program.parse()