import * as plugins from '../../plugins.ts'; import type { OpsServer } from '../classes.opsserver.ts'; import * as interfaces from '../../../ts_interfaces/index.ts'; import { requireValidIdentity } from '../helpers/guards.ts'; import { logger } from '../../logging.ts'; export class SyncHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); this.setupBroadcast(); } /** * Wire up the logger's broadcast function to push sync log entries * to all connected frontends via TypedSocket. */ private setupBroadcast(): void { logger.setBroadcastFn((entry) => { try { const typedsocket = this.opsServerRef.server?.typedserver?.typedsocket; if (!typedsocket) return; typedsocket.findAllTargetConnectionsByTag('syncLogClient').then((connections) => { for (const conn of connections) { typedsocket .createTypedRequest('pushSyncLog', conn) .fire({ entry }) .catch(() => {}); } }).catch(() => {}); } catch { // Server may not be ready yet — ignore } }); } private get syncManager() { return this.opsServerRef.gitopsAppRef.syncManager; } private get actionLog() { return this.opsServerRef.gitopsAppRef.actionLog; } private registerHandlers(): void { // Get all sync configs this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getSyncConfigs', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); return { configs: this.syncManager.getConfigs() }; }, ), ); // Create sync config this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'createSyncConfig', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const config = await this.syncManager.createConfig({ name: dataArg.name, sourceConnectionId: dataArg.sourceConnectionId, targetConnectionId: dataArg.targetConnectionId, targetGroupOffset: dataArg.targetGroupOffset, intervalMinutes: dataArg.intervalMinutes, enforceDelete: dataArg.enforceDelete, enforceGroupDelete: dataArg.enforceGroupDelete, addMirrorHint: dataArg.addMirrorHint, useGroupAvatarsForProjects: dataArg.useGroupAvatarsForProjects, }); this.actionLog.append({ actionType: 'create', entityType: 'sync', entityId: config.id, entityName: config.name, details: `Created sync config "${config.name}" (${config.intervalMinutes}m interval)`, username: dataArg.identity.username, }); return { config }; }, ), ); // Update sync config this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'updateSyncConfig', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const config = await this.syncManager.updateConfig(dataArg.syncConfigId, { name: dataArg.name, targetGroupOffset: dataArg.targetGroupOffset, intervalMinutes: dataArg.intervalMinutes, enforceDelete: dataArg.enforceDelete, enforceGroupDelete: dataArg.enforceGroupDelete, addMirrorHint: dataArg.addMirrorHint, useGroupAvatarsForProjects: dataArg.useGroupAvatarsForProjects, }); this.actionLog.append({ actionType: 'update', entityType: 'sync', entityId: config.id, entityName: config.name, details: `Updated sync config "${config.name}"`, username: dataArg.identity.username, }); return { config }; }, ), ); // Delete sync config this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'deleteSyncConfig', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const config = this.syncManager.getConfig(dataArg.syncConfigId); await this.syncManager.deleteConfig(dataArg.syncConfigId); this.actionLog.append({ actionType: 'delete', entityType: 'sync', entityId: dataArg.syncConfigId, entityName: config?.name || dataArg.syncConfigId, details: `Deleted sync config "${config?.name || dataArg.syncConfigId}"`, username: dataArg.identity.username, }); return { ok: true }; }, ), ); // Pause/resume sync config this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'pauseSyncConfig', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const config = await this.syncManager.pauseConfig( dataArg.syncConfigId, dataArg.paused, ); this.actionLog.append({ actionType: dataArg.paused ? 'pause' : 'resume', entityType: 'sync', entityId: config.id, entityName: config.name, details: `${dataArg.paused ? 'Paused' : 'Resumed'} sync config "${config.name}"`, username: dataArg.identity.username, }); return { config }; }, ), ); // Trigger sync manually this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'triggerSync', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const config = this.syncManager.getConfig(dataArg.syncConfigId); if (!config) { return { ok: false, message: 'Sync config not found' }; } // Fire and forget — force=true bypasses paused check for manual triggers this.syncManager.executeSync(dataArg.syncConfigId, true).catch((err) => { console.error(`Manual sync trigger failed: ${err}`); }); this.actionLog.append({ actionType: 'sync', entityType: 'sync', entityId: config.id, entityName: config.name, details: `Manually triggered sync "${config.name}"`, username: dataArg.identity.username, }); return { ok: true, message: 'Sync triggered' }; }, ), ); // Preview sync (dry run — shows source → target mappings) this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'previewSync', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const result = await this.syncManager.previewSync(dataArg.syncConfigId); return { mappings: result.mappings, deletions: result.deletions, groupDeletions: result.groupDeletions }; }, ), ); // Get repo statuses for a sync config this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getSyncRepoStatuses', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const statuses = await this.syncManager.getRepoStatuses(dataArg.syncConfigId); return { statuses }; }, ), ); // Get recent sync log entries this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getSyncLogs', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const logs = logger.getSyncLogs(dataArg.limit || 200); return { logs }; }, ), ); } }