/** * Registry Manager for Onebox * * Manages Docker registry credentials and authentication */ import * as plugins from '../plugins.ts'; import type { IRegistry } from '../types.ts'; import { logger } from '../logging.ts'; import { getErrorMessage } from '../utils/error.ts'; import { OneboxDatabase } from './database.ts'; export class OneboxRegistriesManager { private oneboxRef: any; // Will be Onebox instance private database: OneboxDatabase; constructor(oneboxRef: any) { this.oneboxRef = oneboxRef; this.database = oneboxRef.database; } /** * Encrypt a password (simple base64 for now, should use proper encryption) */ private encryptPassword(password: string): string { // TODO: Use proper encryption with a secret key // For now, using base64 encoding (NOT SECURE, just for structure) return plugins.encoding.encodeBase64(new TextEncoder().encode(password)); } /** * Decrypt a password */ private decryptPassword(encrypted: string): string { // TODO: Use proper decryption return new TextDecoder().decode(plugins.encoding.decodeBase64(encrypted)); } /** * Add a registry */ async addRegistry(url: string, username: string, password: string): Promise { try { // Check if registry already exists const existing = this.database.getRegistryByURL(url); if (existing) { throw new Error(`Registry already exists: ${url}`); } // Encrypt password const passwordEncrypted = this.encryptPassword(password); // Create registry in database const registry = await this.database.createRegistry({ url, username, passwordEncrypted, createdAt: Date.now(), }); logger.success(`Registry added: ${url}`); // Perform Docker login await this.loginToRegistry(registry); return registry; } catch (error) { logger.error(`Failed to add registry ${url}: ${getErrorMessage(error)}`); throw error; } } /** * Remove a registry */ async removeRegistry(url: string): Promise { try { const registry = this.database.getRegistryByURL(url); if (!registry) { throw new Error(`Registry not found: ${url}`); } this.database.deleteRegistry(url); logger.success(`Registry removed: ${url}`); // Note: We don't perform docker logout as it might affect other users } catch (error) { logger.error(`Failed to remove registry ${url}: ${getErrorMessage(error)}`); throw error; } } /** * List all registries */ listRegistries(): IRegistry[] { return this.database.getAllRegistries(); } /** * Get registry by URL */ getRegistry(url: string): IRegistry | null { return this.database.getRegistryByURL(url); } /** * Perform Docker login for a registry */ async loginToRegistry(registry: IRegistry): Promise { try { logger.info(`Logging into registry: ${registry.url}`); const password = this.decryptPassword(registry.passwordEncrypted); // Use docker login command const command = [ 'docker', 'login', registry.url, '--username', registry.username, '--password-stdin', ]; const process = new Deno.Command('docker', { args: ['login', registry.url, '--username', registry.username, '--password-stdin'], stdin: 'piped', stdout: 'piped', stderr: 'piped', }); const child = process.spawn(); // Write password to stdin const writer = child.stdin.getWriter(); await writer.write(new TextEncoder().encode(password)); await writer.close(); const { code, stdout, stderr } = await child.output(); if (code !== 0) { const errorMsg = new TextDecoder().decode(stderr); throw new Error(`Docker login failed: ${errorMsg}`); } logger.success(`Logged into registry: ${registry.url}`); } catch (error) { logger.error(`Failed to login to registry ${registry.url}: ${getErrorMessage(error)}`); throw error; } } /** * Login to all registries (useful on daemon start) */ async loginToAllRegistries(): Promise { const registries = this.listRegistries(); for (const registry of registries) { try { await this.loginToRegistry(registry); } catch (error) { logger.warn(`Failed to login to ${registry.url}: ${getErrorMessage(error)}`); // Continue with other registries } } } /** * Test registry connection */ async testRegistry(url: string, username: string, password: string): Promise { try { const command = new Deno.Command('docker', { args: ['login', url, '--username', username, '--password-stdin'], stdin: 'piped', stdout: 'piped', stderr: 'piped', }); const child = command.spawn(); const writer = child.stdin.getWriter(); await writer.write(new TextEncoder().encode(password)); await writer.close(); const { code } = await child.output(); return code === 0; } catch (error) { logger.error(`Failed to test registry ${url}: ${getErrorMessage(error)}`); return false; } } }