import fs from 'fs'; import path from 'path'; import crypto from 'crypto'; export type AccessMode = 'private' | 'public'; export interface TunnelConfig { id: string; name: string; description: string; purpose: string; ownerId: string; access: AccessMode; allowedUserIds: string[]; localPort: number; createdAt: string; } const DB_PATH = path.join( __dirname, '..', '..', '..', 'build', 'data', 'db', 'tunnels.json' ); function ensureDir(): void { fs.mkdirSync( path.dirname( DB_PATH ), { recursive: true } ); } function readAll(): TunnelConfig[] { try { return JSON.parse( fs.readFileSync( DB_PATH, 'utf-8' ) ); } catch { return []; } } function writeAll( tunnels: TunnelConfig[] ): void { ensureDir(); fs.writeFileSync( DB_PATH, JSON.stringify( tunnels, null, 2 ) ); } export function createTunnel( data: Omit ): TunnelConfig { const tunnel: TunnelConfig = { ...data, id: crypto.randomUUID(), createdAt: new Date().toISOString(), }; const all = readAll(); all.push( tunnel ); writeAll( all ); return tunnel; } export function getTunnel( id: string ): TunnelConfig | undefined { return readAll().find( t => t.id === id ); } export function getTunnelsByOwner( ownerId: string ): TunnelConfig[] { return readAll().filter( t => t.ownerId === ownerId ); } export function getAvailableTunnels( userId: string, purpose?: string ): TunnelConfig[] { let tunnels = readAll().filter( t => t.ownerId === userId || t.access === 'public' || t.allowedUserIds.includes( userId ) ); if ( purpose ) tunnels = tunnels.filter( t => t.purpose === purpose ); return tunnels; } export function updateTunnel( id: string, patch: Partial> ): TunnelConfig | undefined { const all = readAll(); const idx = all.findIndex( t => t.id === id ); if ( idx === -1 ) return undefined; all[ idx ] = { ...all[ idx ], ...patch }; writeAll( all ); return all[ idx ]; } export function deleteTunnel( id: string ): boolean { const all = readAll(); const filtered = all.filter( t => t.id !== id ); if ( filtered.length === all.length ) return false; writeAll( filtered ); return true; }