import * as plugins from '../plugins.ts'; import { logger } from '../logging.ts'; import type * as interfaces from '../../ts_interfaces/index.ts'; import { BaseProvider, GiteaProvider, GitLabProvider } from '../providers/index.ts'; const CONNECTIONS_FILE = './.nogit/connections.json'; /** * Manages provider connections - persists to .nogit/connections.json * and creates provider instances on demand. */ export class ConnectionManager { private connections: interfaces.data.IProviderConnection[] = []; async init(): Promise { await this.loadConnections(); } private async loadConnections(): Promise { try { const text = await Deno.readTextFile(CONNECTIONS_FILE); this.connections = JSON.parse(text); logger.info(`Loaded ${this.connections.length} connection(s)`); } catch { this.connections = []; logger.debug('No existing connections file found, starting fresh'); } } private async saveConnections(): Promise { // Ensure .nogit directory exists try { await Deno.mkdir('./.nogit', { recursive: true }); } catch { /* already exists */ } await Deno.writeTextFile(CONNECTIONS_FILE, JSON.stringify(this.connections, null, 2)); } getConnections(): interfaces.data.IProviderConnection[] { // Return connections without exposing tokens return this.connections.map((c) => ({ ...c, token: '***' })); } getConnection(id: string): interfaces.data.IProviderConnection | undefined { return this.connections.find((c) => c.id === id); } async createConnection( name: string, providerType: interfaces.data.TProviderType, baseUrl: string, token: string, ): Promise { const connection: interfaces.data.IProviderConnection = { id: crypto.randomUUID(), name, providerType, baseUrl: baseUrl.replace(/\/+$/, ''), token, createdAt: Date.now(), status: 'disconnected', }; this.connections.push(connection); await this.saveConnections(); logger.success(`Connection created: ${name} (${providerType})`); return { ...connection, token: '***' }; } async updateConnection( id: string, updates: { name?: string; baseUrl?: string; token?: string }, ): Promise { const conn = this.connections.find((c) => c.id === id); if (!conn) throw new Error(`Connection not found: ${id}`); if (updates.name) conn.name = updates.name; if (updates.baseUrl) conn.baseUrl = updates.baseUrl.replace(/\/+$/, ''); if (updates.token) conn.token = updates.token; await this.saveConnections(); return { ...conn, token: '***' }; } async deleteConnection(id: string): Promise { const idx = this.connections.findIndex((c) => c.id === id); if (idx === -1) throw new Error(`Connection not found: ${id}`); this.connections.splice(idx, 1); await this.saveConnections(); logger.info(`Connection deleted: ${id}`); } async testConnection(id: string): Promise<{ ok: boolean; error?: string }> { const provider = this.getProvider(id); const result = await provider.testConnection(); const conn = this.connections.find((c) => c.id === id)!; conn.status = result.ok ? 'connected' : 'error'; await this.saveConnections(); return result; } /** * Factory: returns the correct provider instance for a connection ID */ getProvider(connectionId: string): BaseProvider { const conn = this.connections.find((c) => c.id === connectionId); if (!conn) throw new Error(`Connection not found: ${connectionId}`); switch (conn.providerType) { case 'gitea': return new GiteaProvider(conn.id, conn.baseUrl, conn.token); case 'gitlab': return new GitLabProvider(conn.id, conn.baseUrl, conn.token); default: throw new Error(`Unknown provider type: ${conn.providerType}`); } } }