import { randomUUID } from 'crypto'; import { mkdir, writeFile, readFile } from 'fs/promises'; import { join } from 'path'; import { isSafeRouteName, type RouteConfig, type StoredRequest } from '../types/index.js'; import { Either, type IEither } from '@emprespresso/pengueno'; export class Storage { private routes: Map = new Map(); constructor(private readonly dataDir: string = './data') {} async init(): Promise> { try { await mkdir(this.dataDir, { recursive: true }); await this.loadRoutes(); return Either.right(undefined); } catch (err) { return Either.left(err instanceof Error ? err : new Error(String(err))); } } private async loadRoutes(): Promise { try { const routesPath = join(this.dataDir, 'routes.json'); const data = await readFile(routesPath, 'utf-8'); const routes = JSON.parse(data) as RouteConfig[]; for (const route of routes) { if (!isSafeRouteName(route.name)) { continue; } this.routes.set(route.name, route); } } catch { // routes file doesn't exist yet, that's ok } } private async saveRoutes(): Promise> { try { const routesPath = join(this.dataDir, 'routes.json'); const routes = Array.from(this.routes.values()); await writeFile(routesPath, JSON.stringify(routes, null, 2)); return Either.right(undefined); } catch (err) { return Either.left(err instanceof Error ? err : new Error(String(err))); } } async registerRoute(config: RouteConfig): Promise> { if (!isSafeRouteName(config.name)) { return Either.left(new Error('Invalid route name')); } this.routes.set(config.name, config); const routeDir = join(this.dataDir, config.name); try { await mkdir(routeDir, { recursive: true }); } catch (err) { return Either.left(err instanceof Error ? err : new Error(String(err))); } return this.saveRoutes(); } getRoute(name: string): RouteConfig | undefined { if (!isSafeRouteName(name)) return undefined; return this.routes.get(name); } listRoutes(): RouteConfig[] { return Array.from(this.routes.values()); } async deleteRoute(name: string): Promise> { if (!isSafeRouteName(name)) { return Either.left(new Error('Invalid route name')); } this.routes.delete(name); return this.saveRoutes(); } async storeRequest( routeName: string, method: string, headers: Record, body: unknown, files?: StoredRequest['files'], ): Promise> { if (!isSafeRouteName(routeName)) { return Either.left(new Error('Invalid route name')); } const timestamp = Date.now(); const uuid = randomUUID(); const filename = `${timestamp}_${uuid}.json`; const stored: StoredRequest = { timestamp, uuid, routeName, method, headers, body, files, }; const filepath = join(this.dataDir, routeName, filename); try { await writeFile(filepath, JSON.stringify(stored, null, 2)); return Either.right(stored); } catch (err) { return Either.left(err instanceof Error ? err : new Error(String(err))); } } }