/** * Registry Repository * Handles CRUD operations for registries and registry_tokens tables */ import { BaseRepository } from '../base.repository.ts'; import type { IRegistry, IRegistryToken } from '../../types.ts'; export class RegistryRepository extends BaseRepository { // ============ Registries ============ async createRegistry(registry: Omit): Promise { const now = Date.now(); this.query( 'INSERT INTO registries (url, username, password_encrypted, created_at) VALUES (?, ?, ?, ?)', [registry.url, registry.username, registry.passwordEncrypted, now] ); return this.getRegistryByURL(registry.url)!; } getRegistryByURL(url: string): IRegistry | null { const rows = this.query('SELECT * FROM registries WHERE url = ?', [url]); return rows.length > 0 ? this.rowToRegistry(rows[0]) : null; } getAllRegistries(): IRegistry[] { const rows = this.query('SELECT * FROM registries ORDER BY created_at DESC'); return rows.map((row) => this.rowToRegistry(row)); } deleteRegistry(url: string): void { this.query('DELETE FROM registries WHERE url = ?', [url]); } private rowToRegistry(row: any): IRegistry { return { id: Number(row.id || row[0]), url: String(row.url || row[1]), username: String(row.username || row[2]), passwordEncrypted: String(row.password_encrypted || row[3]), createdAt: Number(row.created_at || row[4]), }; } // ============ Registry Tokens ============ createToken(token: Omit): IRegistryToken { const scopeJson = Array.isArray(token.scope) ? JSON.stringify(token.scope) : token.scope; this.query( `INSERT INTO registry_tokens (name, token_hash, token_type, scope, expires_at, created_at, last_used_at, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [ token.name, token.tokenHash, token.type, scopeJson, token.expiresAt, token.createdAt, token.lastUsedAt, token.createdBy, ] ); const rows = this.query('SELECT * FROM registry_tokens WHERE id = last_insert_rowid()'); return this.rowToToken(rows[0]); } getTokenById(id: number): IRegistryToken | null { const rows = this.query('SELECT * FROM registry_tokens WHERE id = ?', [id]); return rows.length > 0 ? this.rowToToken(rows[0]) : null; } getTokenByHash(tokenHash: string): IRegistryToken | null { const rows = this.query('SELECT * FROM registry_tokens WHERE token_hash = ?', [tokenHash]); return rows.length > 0 ? this.rowToToken(rows[0]) : null; } getAllTokens(): IRegistryToken[] { const rows = this.query('SELECT * FROM registry_tokens ORDER BY created_at DESC'); return rows.map((row) => this.rowToToken(row)); } getTokensByType(type: 'global' | 'ci'): IRegistryToken[] { const rows = this.query('SELECT * FROM registry_tokens WHERE token_type = ? ORDER BY created_at DESC', [type]); return rows.map((row) => this.rowToToken(row)); } updateTokenLastUsed(id: number): void { this.query('UPDATE registry_tokens SET last_used_at = ? WHERE id = ?', [Date.now(), id]); } deleteToken(id: number): void { this.query('DELETE FROM registry_tokens WHERE id = ?', [id]); } private rowToToken(row: any): IRegistryToken { let scope: 'all' | string[]; const scopeRaw = row.scope || row[4]; if (scopeRaw === 'all') { scope = 'all'; } else { try { scope = JSON.parse(String(scopeRaw)); } catch { scope = 'all'; } } return { id: Number(row.id || row[0]), name: String(row.name || row[1]), tokenHash: String(row.token_hash || row[2]), type: String(row.token_type || row[3]) as IRegistryToken['type'], scope, expiresAt: row.expires_at || row[5] ? Number(row.expires_at || row[5]) : null, createdAt: Number(row.created_at || row[6]), lastUsedAt: row.last_used_at || row[7] ? Number(row.last_used_at || row[7]) : null, createdBy: String(row.created_by || row[8]), }; } }