UNPKG

@statezero/statezero-tunnel

Version:

Tunnelmole client for StateZero users to create secure tunnels to their local services.

107 lines (93 loc) 3.3 kB
import fs from 'fs'; import path from 'path'; import os from 'os'; import crypto from 'crypto'; export interface ProjectConfig { id: string; name: string; subdomain: string; userEmail: string; userName: string; createdAt: string; lastUsed: string; } interface ProjectStore { projects: ProjectConfig[]; activeProject?: string; } export class ProjectManager { private configPath: string; private store: ProjectStore; constructor() { this.configPath = path.join(os.homedir(), '.tunnelmole-projects.json'); this.store = this.loadStore(); } private loadStore(): ProjectStore { try { if (fs.existsSync(this.configPath)) { const data = fs.readFileSync(this.configPath, 'utf8'); return JSON.parse(data); } } catch (error) { console.warn('Failed to load project store:', error); } return { projects: [] }; } private saveStore(): void { try { fs.writeFileSync(this.configPath, JSON.stringify(this.store, null, 2)); } catch (error) { console.error('Failed to save project store:', error); } } public getProjects(): ProjectConfig[] { return this.store.projects.sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime() ); } public generateUniqueId(): string { return crypto.randomBytes(8).toString('hex'); } public createProject(name: string, userEmail: string, userName: string): ProjectConfig { const project: ProjectConfig = { id: this.generateUniqueId(), name, subdomain: this.generateSubdomain(name), userEmail, userName, createdAt: new Date().toISOString(), lastUsed: new Date().toISOString() }; this.store.projects.push(project); this.saveStore(); return project; } private generateSubdomain(projectName: string): string { // Create a consistent subdomain based on project name + unique suffix const sanitized = projectName.toLowerCase().replace(/[^a-z0-9]/g, ''); const truncated = sanitized.substring(0, 10); // Limit length const suffix = crypto.createHash('md5').update(projectName).digest('hex').substring(0, 6); return `${truncated}-${suffix}`; } public updateLastUsed(projectId: string): void { const project = this.store.projects.find(p => p.id === projectId); if (project) { project.lastUsed = new Date().toISOString(); this.saveStore(); } } public deleteProject(projectId: string): boolean { const index = this.store.projects.findIndex(p => p.id === projectId); if (index !== -1) { this.store.projects.splice(index, 1); this.saveStore(); return true; } return false; } public findByName(name: string): ProjectConfig | undefined { return this.store.projects.find(p => p.name.toLowerCase() === name.toLowerCase() ); } }