feat(sync): add sync subsystem: SyncManager, OpsServer sync handlers, Sync UI and state, provider groupFilter support, and realtime sync log streaming via TypedSocket

This commit is contained in:
2026-02-28 16:33:53 +00:00
parent 2f050744bc
commit f7e16aa350
30 changed files with 2983 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
export type TActionType = 'create' | 'update' | 'delete' | 'pause' | 'resume' | 'test' | 'scan';
export type TActionEntity = 'connection' | 'secret' | 'pipeline';
export type TActionType = 'create' | 'update' | 'delete' | 'pause' | 'resume' | 'test' | 'scan' | 'sync' | 'obsolete';
export type TActionEntity = 'connection' | 'secret' | 'pipeline' | 'sync';
export interface IActionLogEntry {
id: string;

View File

@@ -8,4 +8,6 @@ export interface IProviderConnection {
token: string;
createdAt: number;
status: 'connected' | 'disconnected' | 'error' | 'paused';
groupFilter?: string; // Restricts which repos this connection can see (e.g. "foss.global")
groupFilterId?: string; // Resolved filter group ID (numeric for GitLab, org name for Gitea)
}

View File

@@ -5,3 +5,4 @@ export * from './group.ts';
export * from './secret.ts';
export * from './pipeline.ts';
export * from './actionlog.ts';
export * from './sync.ts';

View File

@@ -0,0 +1,36 @@
export type TSyncStatus = 'active' | 'paused' | 'error';
export interface ISyncConfig {
id: string;
name: string;
sourceConnectionId: string;
targetConnectionId: string;
targetGroupOffset?: string; // Path prefix for target repos (e.g. "mirror/gitlab")
intervalMinutes: number; // Default 5
status: TSyncStatus;
lastSyncAt: number;
lastSyncError?: string;
lastSyncDurationMs?: number;
reposSynced: number;
enforceDelete: boolean; // When true, stale target repos are moved to obsolete
enforceGroupDelete: boolean; // When true, stale target groups/orgs are moved to obsolete
addMirrorHint?: boolean; // When true, target descriptions get "(This is a mirror of ...)" appended
createdAt: number;
}
export interface ISyncRepoStatus {
id: string;
syncConfigId: string;
sourceFullPath: string; // e.g. "push.rocks/smartstate"
targetFullPath: string; // e.g. "foss.global/push.rocks/smartstate"
lastSyncAt: number;
lastSyncError?: string;
status: 'synced' | 'error' | 'pending';
}
export interface ISyncLogEntry {
timestamp: number;
level: 'info' | 'warn' | 'error' | 'success' | 'debug';
message: string;
source?: string; // e.g. 'preview', 'sync', 'git', 'api'
}