/** * Service Repository * Handles CRUD operations for services table */ import { BaseRepository } from '../base.repository.ts'; import type { TBindValue } from '../types.ts'; import type { IService, IPlatformRequirements } from '../../types.ts'; import { logger } from '../../logging.ts'; import { getErrorMessage } from '../../utils/error.ts'; export class ServiceRepository extends BaseRepository { async create(service: Omit): Promise { const now = Date.now(); this.query( `INSERT INTO services ( name, image, registry, env_vars, port, domain, container_id, status, created_at, updated_at, use_onebox_registry, registry_repository, registry_image_tag, auto_update_on_push, image_digest, platform_requirements ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ service.name, service.image, service.registry || null, JSON.stringify(service.envVars), service.port, service.domain || null, service.containerID || null, service.status, now, now, service.useOneboxRegistry ? 1 : 0, service.registryRepository || null, service.registryImageTag || 'latest', service.autoUpdateOnPush ? 1 : 0, service.imageDigest || null, JSON.stringify(service.platformRequirements || {}), ] ); return this.getByName(service.name)!; } getByName(name: string): IService | null { const rows = this.query('SELECT * FROM services WHERE name = ?', [name]); if (rows.length > 0) { logger.info(`getServiceByName: raw row data: ${JSON.stringify(rows[0])}`); const service = this.rowToService(rows[0]); logger.info(`getServiceByName: service object containerID: ${service.containerID}`); return service; } return null; } getById(id: number): IService | null { const rows = this.query('SELECT * FROM services WHERE id = ?', [id]); return rows.length > 0 ? this.rowToService(rows[0]) : null; } getAll(): IService[] { const rows = this.query('SELECT * FROM services ORDER BY created_at DESC'); return rows.map((row) => this.rowToService(row)); } update(id: number, updates: Partial): void { const fields: string[] = []; const values: TBindValue[] = []; if (updates.image !== undefined) { fields.push('image = ?'); values.push(updates.image); } if (updates.registry !== undefined) { fields.push('registry = ?'); values.push(updates.registry); } if (updates.envVars !== undefined) { fields.push('env_vars = ?'); values.push(JSON.stringify(updates.envVars)); } if (updates.port !== undefined) { fields.push('port = ?'); values.push(updates.port); } if (updates.domain !== undefined) { fields.push('domain = ?'); values.push(updates.domain); } if (updates.containerID !== undefined) { fields.push('container_id = ?'); values.push(updates.containerID); } if (updates.status !== undefined) { fields.push('status = ?'); values.push(updates.status); } if (updates.useOneboxRegistry !== undefined) { fields.push('use_onebox_registry = ?'); values.push(updates.useOneboxRegistry ? 1 : 0); } if (updates.registryRepository !== undefined) { fields.push('registry_repository = ?'); values.push(updates.registryRepository); } if (updates.registryImageTag !== undefined) { fields.push('registry_image_tag = ?'); values.push(updates.registryImageTag); } if (updates.autoUpdateOnPush !== undefined) { fields.push('auto_update_on_push = ?'); values.push(updates.autoUpdateOnPush ? 1 : 0); } if (updates.imageDigest !== undefined) { fields.push('image_digest = ?'); values.push(updates.imageDigest); } if (updates.platformRequirements !== undefined) { fields.push('platform_requirements = ?'); values.push(JSON.stringify(updates.platformRequirements)); } fields.push('updated_at = ?'); values.push(Date.now()); values.push(id); this.query(`UPDATE services SET ${fields.join(', ')} WHERE id = ?`, values); } delete(id: number): void { this.query('DELETE FROM services WHERE id = ?', [id]); } private rowToService(row: any): IService { let envVars = {}; const envVarsRaw = row.env_vars || row[4]; if (envVarsRaw && envVarsRaw !== 'undefined' && envVarsRaw !== 'null') { try { envVars = JSON.parse(String(envVarsRaw)); } catch (e) { logger.warn(`Failed to parse env_vars for service: ${getErrorMessage(e)}`); envVars = {}; } } let platformRequirements: IPlatformRequirements | undefined; const platformReqRaw = row.platform_requirements; if (platformReqRaw && platformReqRaw !== 'undefined' && platformReqRaw !== 'null' && platformReqRaw !== '{}') { try { platformRequirements = JSON.parse(String(platformReqRaw)); } catch (e) { logger.warn(`Failed to parse platform_requirements for service: ${getErrorMessage(e)}`); platformRequirements = undefined; } } return { id: Number(row.id || row[0]), name: String(row.name || row[1]), image: String(row.image || row[2]), registry: (row.registry || row[3]) ? String(row.registry || row[3]) : undefined, envVars, port: Number(row.port || row[5]), domain: (row.domain || row[6]) ? String(row.domain || row[6]) : undefined, containerID: (row.container_id || row[7]) ? String(row.container_id || row[7]) : undefined, status: String(row.status || row[8]) as IService['status'], createdAt: Number(row.created_at || row[9]), updatedAt: Number(row.updated_at || row[10]), useOneboxRegistry: row.use_onebox_registry ? Boolean(row.use_onebox_registry) : undefined, registryRepository: row.registry_repository ? String(row.registry_repository) : undefined, registryImageTag: row.registry_image_tag ? String(row.registry_image_tag) : undefined, autoUpdateOnPush: row.auto_update_on_push ? Boolean(row.auto_update_on_push) : undefined, imageDigest: row.image_digest ? String(row.image_digest) : undefined, platformRequirements, }; } }