import * as plugins from '../../plugins.ts'; import * as interfaces from '../../../ts_interfaces/index.ts'; import type { OpsServer } from '../classes.opsserver.ts'; import { requireValidIdentity } from '../helpers/guards.ts'; import { Organization, Repository } from '../../models/index.ts'; import { PermissionService } from '../../services/permission.service.ts'; import { AuditService } from '../../services/audit.service.ts'; export class RepositoryHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); private permissionService = new PermissionService(); constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } private registerHandlers(): void { // Get Repositories this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getRepositories', async (dataArg) => { await requireValidIdentity(this.opsServerRef.authHandler, dataArg); try { const repositories = await this.permissionService.getAccessibleRepositories( dataArg.identity.userId, dataArg.organizationId, ); return { repositories: repositories.map((repo) => ({ id: repo.id, organizationId: repo.organizationId, name: repo.name, description: repo.description, protocol: repo.protocol as interfaces.data.TRegistryProtocol, visibility: repo.visibility as interfaces.data.TRepositoryVisibility, isPublic: repo.isPublic, packageCount: repo.packageCount, storageBytes: repo.storageBytes || 0, downloadCount: (repo as any).downloadCount || 0, createdAt: repo.createdAt instanceof Date ? repo.createdAt.toISOString() : String(repo.createdAt), })), }; } catch (error) { if (error instanceof plugins.typedrequest.TypedResponseError) throw error; throw new plugins.typedrequest.TypedResponseError('Failed to list repositories'); } }, ), ); // Get Repository this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getRepository', async (dataArg) => { await requireValidIdentity(this.opsServerRef.authHandler, dataArg); try { const repo = await Repository.findById(dataArg.repositoryId); if (!repo) { throw new plugins.typedrequest.TypedResponseError('Repository not found'); } // Check access if (!repo.isPublic) { const permissions = await this.permissionService.resolvePermissions({ userId: dataArg.identity.userId, organizationId: repo.organizationId, repositoryId: repo.id, }); if (!permissions.canRead) { throw new plugins.typedrequest.TypedResponseError('Access denied'); } } return { repository: { id: repo.id, organizationId: repo.organizationId, name: repo.name, description: repo.description, protocol: repo.protocol as interfaces.data.TRegistryProtocol, visibility: repo.visibility as interfaces.data.TRepositoryVisibility, isPublic: repo.isPublic, packageCount: repo.packageCount, storageBytes: repo.storageBytes || 0, downloadCount: (repo as any).downloadCount || 0, createdAt: repo.createdAt instanceof Date ? repo.createdAt.toISOString() : String(repo.createdAt), }, }; } catch (error) { if (error instanceof plugins.typedrequest.TypedResponseError) throw error; throw new plugins.typedrequest.TypedResponseError('Failed to get repository'); } }, ), ); // Create Repository this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'createRepository', async (dataArg) => { await requireValidIdentity(this.opsServerRef.authHandler, dataArg); try { const { organizationId, name, description, protocol, visibility } = dataArg; // Check admin permission const canManage = await this.permissionService.canManageOrganization( dataArg.identity.userId, organizationId, ); if (!canManage) { throw new plugins.typedrequest.TypedResponseError('Admin access required'); } if (!name) { throw new plugins.typedrequest.TypedResponseError('Repository name is required'); } // Validate name format if (!/^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$/.test(name)) { throw new plugins.typedrequest.TypedResponseError( 'Name must be lowercase alphanumeric with optional dots, hyphens, or underscores', ); } // Check org exists const org = await Organization.findById(organizationId); if (!org) { throw new plugins.typedrequest.TypedResponseError('Organization not found'); } // Create repository const repo = await Repository.createRepository({ organizationId, name, description, protocol: protocol || 'npm', visibility: visibility || 'private', createdById: dataArg.identity.userId, }); // Audit log await AuditService.withContext({ actorId: dataArg.identity.userId, actorType: 'user', organizationId, }).logRepositoryCreated(repo.id, repo.name, organizationId); return { repository: { id: repo.id, organizationId: repo.organizationId, name: repo.name, description: repo.description, protocol: repo.protocol as interfaces.data.TRegistryProtocol, visibility: repo.visibility as interfaces.data.TRepositoryVisibility, isPublic: repo.isPublic, packageCount: repo.packageCount, storageBytes: repo.storageBytes || 0, downloadCount: 0, createdAt: repo.createdAt instanceof Date ? repo.createdAt.toISOString() : String(repo.createdAt), }, }; } catch (error) { if (error instanceof plugins.typedrequest.TypedResponseError) throw error; throw new plugins.typedrequest.TypedResponseError('Failed to create repository'); } }, ), ); // Update Repository this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'updateRepository', async (dataArg) => { await requireValidIdentity(this.opsServerRef.authHandler, dataArg); try { const repo = await Repository.findById(dataArg.repositoryId); if (!repo) { throw new plugins.typedrequest.TypedResponseError('Repository not found'); } // Check admin permission const canManage = await this.permissionService.canManageRepository( dataArg.identity.userId, repo.organizationId, dataArg.repositoryId, ); if (!canManage) { throw new plugins.typedrequest.TypedResponseError('Admin access required'); } if (dataArg.description !== undefined) repo.description = dataArg.description; if (dataArg.visibility !== undefined) repo.visibility = dataArg.visibility as any; await repo.save(); return { repository: { id: repo.id, organizationId: repo.organizationId, name: repo.name, description: repo.description, protocol: repo.protocol as interfaces.data.TRegistryProtocol, visibility: repo.visibility as interfaces.data.TRepositoryVisibility, isPublic: repo.isPublic, packageCount: repo.packageCount, storageBytes: repo.storageBytes || 0, downloadCount: (repo as any).downloadCount || 0, createdAt: repo.createdAt instanceof Date ? repo.createdAt.toISOString() : String(repo.createdAt), }, }; } catch (error) { if (error instanceof plugins.typedrequest.TypedResponseError) throw error; throw new plugins.typedrequest.TypedResponseError('Failed to update repository'); } }, ), ); // Delete Repository this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'deleteRepository', async (dataArg) => { await requireValidIdentity(this.opsServerRef.authHandler, dataArg); try { const repo = await Repository.findById(dataArg.repositoryId); if (!repo) { throw new plugins.typedrequest.TypedResponseError('Repository not found'); } // Check admin permission const canManage = await this.permissionService.canManageRepository( dataArg.identity.userId, repo.organizationId, dataArg.repositoryId, ); if (!canManage) { throw new plugins.typedrequest.TypedResponseError('Admin access required'); } // Check for packages if (repo.packageCount > 0) { throw new plugins.typedrequest.TypedResponseError( 'Cannot delete repository with packages. Remove all packages first.', ); } await repo.delete(); return { message: 'Repository deleted successfully' }; } catch (error) { if (error instanceof plugins.typedrequest.TypedResponseError) throw error; throw new plugins.typedrequest.TypedResponseError('Failed to delete repository'); } }, ), ); } }