/** * Name-to-UUID resolver for CLI. * * Allows users to reference Coolify resources by friendly name instead * of UUID. Caches API responses in-memory during the CLI session. * * @module */ import { isOk } from "@mks2508/no-throw"; import { getCoolifyService } from "../coolify/index.js"; import type { ICoolifyApplication } from "../coolify/types.js"; import type { ICoolifyDatabase } from "../coolify/types.js"; import type { ICoolifyProject } from "../coolify/types.js"; import type { ICoolifyService as ICoolifyServiceType } from "../coolify/types.js"; /** In-memory cache for API responses during a single CLI session. */ const cache = { apps: null as ICoolifyApplication[] | null, databases: null as ICoolifyDatabase[] | null, services: null as ICoolifyServiceType[] | null, projects: null as ICoolifyProject[] | null, }; /** * Checks if a string looks like a Coolify UUID. * * Coolify UUIDs are lowercase alphanumeric strings, typically 20-30 chars. * * @param value - The string to check * @returns True if the string looks like a UUID */ function looksLikeUuid(value: string): boolean { return /^[a-z0-9]{16,40}$/.test(value); } /** * Fetches and caches the list of applications from Coolify API. * * @returns Array of applications, or empty array on error */ async function getApps(): Promise { if (cache.apps) return cache.apps; const coolify = getCoolifyService(); const initResult = await coolify.init(); if (isOk(initResult)) { const result = await coolify.listApplications(); if (isOk(result)) { cache.apps = result.value; return cache.apps; } } return []; } /** * Fetches and caches the list of databases from Coolify API. * * @returns Array of databases, or empty array on error */ async function getDatabases(): Promise { if (cache.databases) return cache.databases; const coolify = getCoolifyService(); const initResult = await coolify.init(); if (isOk(initResult)) { const result = await coolify.listDatabases(); if (isOk(result)) { cache.databases = result.value; return cache.databases; } } return []; } /** * Fetches and caches the list of services from Coolify API. * * @returns Array of services, or empty array on error */ async function getServices(): Promise { if (cache.services) return cache.services; const coolify = getCoolifyService(); const initResult = await coolify.init(); if (isOk(initResult)) { const result = await coolify.listServices(); if (isOk(result)) { cache.services = result.value; return cache.services; } } return []; } /** * Resolves a name or UUID to a UUID. * * Accepts: UUID directly, app name, database name, or service name. * Searches applications first, then databases, then services. * Results are cached in-memory for the duration of the CLI session. * * @param nameOrUuid - A friendly name or UUID string * @returns The resolved UUID, or null if not found */ export async function resolveNameOrUuid( nameOrUuid: string, ): Promise { if (looksLikeUuid(nameOrUuid)) return nameOrUuid; const query = nameOrUuid.toLowerCase(); // Search applications const apps = await getApps(); const app = apps.find((a) => a.name?.toLowerCase() === query); if (app) return app.uuid; // Search databases const databases = await getDatabases(); const db = databases.find((d) => d.name?.toLowerCase() === query); if (db) return db.uuid; // Search services const services = await getServices(); const svc = services.find((s) => s.name?.toLowerCase() === query); if (svc) return svc.uuid; return null; } /** * Resolves a name or UUID specifically for applications. * * @param nameOrUuid - A friendly name or UUID string * @returns The resolved application UUID, or null if not found */ export async function resolveAppNameOrUuid( nameOrUuid: string, ): Promise { if (looksLikeUuid(nameOrUuid)) return nameOrUuid; const query = nameOrUuid.toLowerCase(); const apps = await getApps(); const app = apps.find((a) => a.name?.toLowerCase() === query); return app?.uuid ?? null; } /** * Resolves a name or UUID specifically for databases. * * @param nameOrUuid - A friendly name or UUID string * @returns The resolved database UUID, or null if not found */ export async function resolveDbNameOrUuid( nameOrUuid: string, ): Promise { if (looksLikeUuid(nameOrUuid)) return nameOrUuid; const query = nameOrUuid.toLowerCase(); const databases = await getDatabases(); const db = databases.find((d) => d.name?.toLowerCase() === query); return db?.uuid ?? null; } /** * Fetches and caches the list of projects from Coolify API. * * @returns Array of projects, or empty array on error */ async function getProjects(): Promise { if (cache.projects) return cache.projects; const coolify = getCoolifyService(); const initResult = await coolify.init(); if (isOk(initResult)) { const result = await coolify.listProjects(); if (isOk(result)) { cache.projects = result.value; return cache.projects; } } return []; } /** * Resolves a name or UUID specifically for projects. * * @param nameOrUuid - A friendly name or UUID string * @returns The resolved project UUID, or null if not found */ export async function resolveProjectNameOrUuid( nameOrUuid: string, ): Promise { if (looksLikeUuid(nameOrUuid)) return nameOrUuid; const query = nameOrUuid.toLowerCase(); const projects = await getProjects(); const project = projects.find((p) => p.name?.toLowerCase() === query); return project?.uuid ?? null; } /** * Resolves a name or UUID specifically for services. * * @param nameOrUuid - A friendly name or UUID string * @returns The resolved service UUID, or null if not found */ export async function resolveSvcNameOrUuid( nameOrUuid: string, ): Promise { if (looksLikeUuid(nameOrUuid)) return nameOrUuid; const query = nameOrUuid.toLowerCase(); const services = await getServices(); const svc = services.find((s) => s.name?.toLowerCase() === query); return svc?.uuid ?? null; } /** * Clears the in-memory cache. Useful for testing or long-running sessions. */ export function clearResolverCache(): void { cache.apps = null; cache.databases = null; cache.services = null; cache.projects = null; }