fix(registry): align registry integrations with updated auth, storage, repository, and audit models

This commit is contained in:
2026-03-20 14:14:39 +00:00
parent fe3cb75095
commit d71ae08645
18 changed files with 451 additions and 523 deletions

View File

@@ -109,7 +109,7 @@ export class AdminAuthApi {
},
attributeMapping: body.attributeMapping,
provisioning: body.provisioning,
createdById: ctx.actor!.userId,
createdById: ctx.actor!.userId!,
});
} else if (body.type === 'ldap' && body.ldapConfig) {
// Encrypt bind password
@@ -124,7 +124,7 @@ export class AdminAuthApi {
},
attributeMapping: body.attributeMapping,
provisioning: body.provisioning,
createdById: ctx.actor!.userId,
createdById: ctx.actor!.userId!,
});
} else {
return {
@@ -138,11 +138,10 @@ export class AdminAuthApi {
actorId: ctx.actor!.userId,
actorType: 'user',
actorIp: ctx.ip,
}).log('ORGANIZATION_CREATED', 'system', {
}).log('AUTH_PROVIDER_CREATED', 'auth_provider', {
resourceId: provider.id,
success: true,
metadata: {
action: 'auth_provider_created',
providerName: provider.name,
providerType: provider.type,
},
@@ -270,11 +269,10 @@ export class AdminAuthApi {
actorId: ctx.actor!.userId,
actorType: 'user',
actorIp: ctx.ip,
}).log('ORGANIZATION_UPDATED', 'system', {
}).log('AUTH_PROVIDER_UPDATED', 'auth_provider', {
resourceId: provider.id,
success: true,
metadata: {
action: 'auth_provider_updated',
providerName: provider.name,
},
});
@@ -321,11 +319,10 @@ export class AdminAuthApi {
actorId: ctx.actor!.userId,
actorType: 'user',
actorIp: ctx.ip,
}).log('ORGANIZATION_DELETED', 'system', {
}).log('AUTH_PROVIDER_DELETED', 'auth_provider', {
resourceId: provider.id,
success: true,
metadata: {
action: 'auth_provider_disabled',
providerName: provider.name,
},
});
@@ -360,11 +357,10 @@ export class AdminAuthApi {
actorId: ctx.actor!.userId,
actorType: 'user',
actorIp: ctx.ip,
}).log('ORGANIZATION_UPDATED', 'system', {
}).log('AUTH_PROVIDER_TESTED', 'auth_provider', {
resourceId: id,
success: result.success,
metadata: {
action: 'auth_provider_tested',
result: result.success ? 'success' : 'failure',
latencyMs: result.latencyMs,
error: result.error,
@@ -433,12 +429,9 @@ export class AdminAuthApi {
actorId: ctx.actor!.userId,
actorType: 'user',
actorIp: ctx.ip,
}).log('ORGANIZATION_UPDATED', 'system', {
}).log('PLATFORM_SETTINGS_UPDATED', 'platform_settings', {
resourceId: 'platform-settings',
success: true,
metadata: {
action: 'platform_settings_updated',
},
});
return {

View File

@@ -39,7 +39,13 @@ export class OrganizationApi {
if (ctx.actor.user?.isSystemAdmin) {
organizations = await Organization.getInstances({});
} else {
organizations = await OrganizationMember.getUserOrganizations(ctx.actor.userId);
const memberships = await OrganizationMember.getUserOrganizations(ctx.actor.userId);
const orgs: Organization[] = [];
for (const m of memberships) {
const org = await Organization.findById(m.organizationId);
if (org) orgs.push(org);
}
organizations = orgs;
}
return {
@@ -155,8 +161,8 @@ export class OrganizationApi {
membership.organizationId = org.id;
membership.userId = ctx.actor.userId;
membership.role = 'owner';
membership.addedById = ctx.actor.userId;
membership.addedAt = new Date();
membership.invitedBy = ctx.actor.userId;
membership.joinedAt = new Date();
await membership.save();
@@ -310,7 +316,7 @@ export class OrganizationApi {
return {
userId: m.userId,
role: m.role,
addedAt: m.addedAt,
addedAt: m.joinedAt,
user: user
? {
username: user.username,
@@ -384,8 +390,8 @@ export class OrganizationApi {
membership.organizationId = org.id;
membership.userId = userId;
membership.role = role;
membership.addedById = ctx.actor.userId;
membership.addedAt = new Date();
membership.invitedBy = ctx.actor.userId;
membership.joinedAt = new Date();
await membership.save();
@@ -398,7 +404,7 @@ export class OrganizationApi {
body: {
userId: membership.userId,
role: membership.role,
addedAt: membership.addedAt,
addedAt: membership.joinedAt,
},
};
} catch (error) {

View File

@@ -174,7 +174,7 @@ export class PackageApi {
publishedAt: data.publishedAt,
size: data.size,
downloads: data.downloads,
checksum: data.checksum,
checksum: data.metadata?.checksum,
}));
return {

View File

@@ -6,7 +6,7 @@ import type { IApiContext, IApiResponse } from '../router.ts';
import { PermissionService } from '../../services/permission.service.ts';
import { AuditService } from '../../services/audit.service.ts';
import { Repository, Organization } from '../../models/index.ts';
import type { TRegistryProtocol } from '../../interfaces/auth.interfaces.ts';
import type { TRegistryProtocol, TRepositoryVisibility } from '../../interfaces/auth.interfaces.ts';
export class RepositoryApi {
private permissionService: PermissionService;
@@ -26,7 +26,6 @@ export class RepositoryApi {
const { orgId } = ctx.params;
try {
// Get accessible repositories
const repositories = await this.permissionService.getAccessibleRepositories(
ctx.actor.userId,
orgId
@@ -38,9 +37,9 @@ export class RepositoryApi {
repositories: repositories.map((repo) => ({
id: repo.id,
name: repo.name,
displayName: repo.displayName,
description: repo.description,
protocols: repo.protocols,
protocol: repo.protocol,
visibility: repo.visibility,
isPublic: repo.isPublic,
packageCount: repo.packageCount,
createdAt: repo.createdAt,
@@ -84,11 +83,10 @@ export class RepositoryApi {
id: repo.id,
organizationId: repo.organizationId,
name: repo.name,
displayName: repo.displayName,
description: repo.description,
protocols: repo.protocols,
protocol: repo.protocol,
visibility: repo.visibility,
isPublic: repo.isPublic,
settings: repo.settings,
packageCount: repo.packageCount,
storageBytes: repo.storageBytes,
createdAt: repo.createdAt,
@@ -118,17 +116,22 @@ export class RepositoryApi {
try {
const body = await ctx.request.json();
const { name, displayName, description, protocols, isPublic, settings } = body;
const { name, description, protocol, visibility } = body as {
name: string;
description?: string;
protocol?: TRegistryProtocol;
visibility?: TRepositoryVisibility;
};
if (!name) {
return { status: 400, body: { error: 'Repository name is required' } };
}
// Validate name format
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(name)) {
if (!/^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$/.test(name)) {
return {
status: 400,
body: { error: 'Name must be lowercase alphanumeric with optional hyphens' },
body: { error: 'Name must be lowercase alphanumeric with optional dots, hyphens, or underscores' },
};
}
@@ -138,30 +141,15 @@ export class RepositoryApi {
return { status: 404, body: { error: 'Organization not found' } };
}
// Check if name is taken in this org
const existing = await Repository.findByName(orgId, name);
if (existing) {
return { status: 409, body: { error: 'Repository name already taken in this organization' } };
}
// Create repository
const repo = new Repository();
repo.id = await Repository.getNewId();
repo.organizationId = orgId;
repo.name = name;
repo.displayName = displayName || name;
repo.description = description;
repo.protocols = protocols || ['npm'];
repo.isPublic = isPublic ?? false;
repo.settings = settings || {
allowOverwrite: false,
immutableTags: false,
retentionDays: 0,
};
repo.createdAt = new Date();
repo.createdById = ctx.actor.userId;
await repo.save();
// Create repository using the model's factory method
const repo = await Repository.createRepository({
organizationId: orgId,
name,
description,
protocol: protocol || 'npm',
visibility: visibility || 'private',
createdById: ctx.actor.userId,
});
// Audit log
await AuditService.withContext({
@@ -177,9 +165,9 @@ export class RepositoryApi {
id: repo.id,
organizationId: repo.organizationId,
name: repo.name,
displayName: repo.displayName,
description: repo.description,
protocols: repo.protocols,
protocol: repo.protocol,
visibility: repo.visibility,
isPublic: repo.isPublic,
createdAt: repo.createdAt,
},
@@ -217,13 +205,13 @@ export class RepositoryApi {
}
const body = await ctx.request.json();
const { displayName, description, protocols, isPublic, settings } = body;
const { description, visibility } = body as {
description?: string;
visibility?: TRepositoryVisibility;
};
if (displayName !== undefined) repo.displayName = displayName;
if (description !== undefined) repo.description = description;
if (protocols !== undefined) repo.protocols = protocols;
if (isPublic !== undefined) repo.isPublic = isPublic;
if (settings !== undefined) repo.settings = { ...repo.settings, ...settings };
if (visibility !== undefined) repo.visibility = visibility;
await repo.save();
@@ -232,11 +220,10 @@ export class RepositoryApi {
body: {
id: repo.id,
name: repo.name,
displayName: repo.displayName,
description: repo.description,
protocols: repo.protocols,
protocol: repo.protocol,
visibility: repo.visibility,
isPublic: repo.isPublic,
settings: repo.settings,
},
};
} catch (error) {

View File

@@ -137,8 +137,8 @@ export class UserApi {
user.username = username;
user.passwordHash = passwordHash;
user.displayName = displayName || username;
user.isSystemAdmin = isSystemAdmin || false;
user.isActive = true;
user.isPlatformAdmin = isSystemAdmin || false;
user.status = 'active';
user.createdAt = new Date();
await user.save();
@@ -189,8 +189,8 @@ export class UserApi {
// Only admins can change these
if (ctx.actor.user?.isSystemAdmin) {
if (isActive !== undefined) user.isActive = isActive;
if (isSystemAdmin !== undefined) user.isSystemAdmin = isSystemAdmin;
if (isActive !== undefined) user.status = isActive ? 'active' : 'suspended';
if (isSystemAdmin !== undefined) user.isPlatformAdmin = isSystemAdmin;
}
// Password change
@@ -245,7 +245,7 @@ export class UserApi {
}
// Soft delete - deactivate instead of removing
user.isActive = false;
user.status = 'suspended';
await user.save();
return {