feat(opsserver,web): replace the Angular UI and REST management layer with a TypedRequest-based ops server and bundled web frontend

This commit is contained in:
2026-03-20 16:43:44 +00:00
parent 0fc74ff995
commit d4f758ce0f
159 changed files with 12465 additions and 14861 deletions

View File

@@ -7,7 +7,8 @@ import type { IApiToken, ITokenScope, TRegistryProtocol } from '../interfaces/au
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class ApiToken extends plugins.smartdata.SmartDataDbDoc<ApiToken, ApiToken> implements IApiToken {
export class ApiToken extends plugins.smartdata.SmartDataDbDoc<ApiToken, ApiToken>
implements IApiToken {
@plugins.smartdata.unI()
public id: string = '';
@@ -150,7 +151,7 @@ export class ApiToken extends plugins.smartdata.SmartDataDbDoc<ApiToken, ApiToke
protocol: TRegistryProtocol,
organizationId?: string,
repositoryId?: string,
action?: string
action?: string,
): boolean {
for (const scope of this.scopes) {
// Check protocol
@@ -163,7 +164,9 @@ export class ApiToken extends plugins.smartdata.SmartDataDbDoc<ApiToken, ApiToke
if (scope.repositoryId && scope.repositoryId !== repositoryId) continue;
// Check action
if (action && !scope.actions.includes('*') && !scope.actions.includes(action as never)) continue;
if (action && !scope.actions.includes('*') && !scope.actions.includes(action as never)) {
continue;
}
return true;
}

View File

@@ -3,11 +3,16 @@
*/
import * as plugins from '../plugins.ts';
import type { IAuditLog, TAuditAction, TAuditResourceType } from '../interfaces/audit.interfaces.ts';
import type {
IAuditLog,
TAuditAction,
TAuditResourceType,
} from '../interfaces/audit.interfaces.ts';
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class AuditLog extends plugins.smartdata.SmartDataDbDoc<AuditLog, AuditLog> implements IAuditLog {
export class AuditLog extends plugins.smartdata.SmartDataDbDoc<AuditLog, AuditLog>
implements IAuditLog {
@plugins.smartdata.unI()
public id: string = '';

View File

@@ -5,13 +5,13 @@
import * as plugins from '../plugins.ts';
import type {
IAuthProvider,
TAuthProviderType,
TAuthProviderStatus,
IOAuthConfig,
ILdapConfig,
IAttributeMapping,
IAuthProvider,
ILdapConfig,
IOAuthConfig,
IProvisioningSettings,
TAuthProviderStatus,
TAuthProviderType,
} from '../interfaces/auth.interfaces.ts';
import { db } from './db.ts';
@@ -27,10 +27,8 @@ const DEFAULT_PROVISIONING: IProvisioningSettings = {
};
@plugins.smartdata.Collection(() => db)
export class AuthProvider
extends plugins.smartdata.SmartDataDbDoc<AuthProvider, AuthProvider>
implements IAuthProvider
{
export class AuthProvider extends plugins.smartdata.SmartDataDbDoc<AuthProvider, AuthProvider>
implements IAuthProvider {
@plugins.smartdata.unI()
public id: string = '';

View File

@@ -20,7 +20,7 @@ let isInitialized = false;
*/
export async function initDb(
mongoDbUrl: string,
mongoDbName?: string
mongoDbName?: string,
): Promise<plugins.smartdata.SmartdataDb> {
if (isInitialized && db) {
return db;

View File

@@ -10,8 +10,7 @@ import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class ExternalIdentity
extends plugins.smartdata.SmartDataDbDoc<ExternalIdentity, ExternalIdentity>
implements IExternalIdentity
{
implements IExternalIdentity {
@plugins.smartdata.unI()
public id: string = '';
@@ -55,7 +54,7 @@ export class ExternalIdentity
*/
public static async findByExternalId(
providerId: string,
externalId: string
externalId: string,
): Promise<ExternalIdentity | null> {
return await ExternalIdentity.getInstance({ providerId, externalId });
}
@@ -72,7 +71,7 @@ export class ExternalIdentity
*/
public static async findByUserAndProvider(
userId: string,
providerId: string
providerId: string,
): Promise<ExternalIdentity | null> {
return await ExternalIdentity.getInstance({ userId, providerId });
}

View File

@@ -2,7 +2,7 @@
* Model exports
*/
export { initDb, getDb, closeDb, isDbConnected } from './db.ts';
export { closeDb, getDb, initDb, isDbConnected } from './db.ts';
export { User } from './user.ts';
export { Organization } from './organization.ts';
export { OrganizationMember } from './organization.member.ts';

View File

@@ -7,7 +7,9 @@ import type { IOrganizationMember, TOrganizationRole } from '../interfaces/auth.
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class OrganizationMember extends plugins.smartdata.SmartDataDbDoc<OrganizationMember, OrganizationMember> implements IOrganizationMember {
export class OrganizationMember
extends plugins.smartdata.SmartDataDbDoc<OrganizationMember, OrganizationMember>
implements IOrganizationMember {
@plugins.smartdata.unI()
public id: string = '';
@@ -69,7 +71,7 @@ export class OrganizationMember extends plugins.smartdata.SmartDataDbDoc<Organiz
*/
public static async findMembership(
organizationId: string,
userId: string
userId: string,
): Promise<OrganizationMember | null> {
return await OrganizationMember.getInstance({
organizationId,

View File

@@ -18,7 +18,8 @@ const DEFAULT_SETTINGS: IOrganizationSettings = {
};
@plugins.smartdata.Collection(() => db)
export class Organization extends plugins.smartdata.SmartDataDbDoc<Organization, Organization> implements IOrganization {
export class Organization extends plugins.smartdata.SmartDataDbDoc<Organization, Organization>
implements IOrganization {
@plugins.smartdata.unI()
public id: string = '';
@@ -92,7 +93,7 @@ export class Organization extends plugins.smartdata.SmartDataDbDoc<Organization,
const nameRegex = /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/;
if (!nameRegex.test(data.name)) {
throw new Error(
'Organization name must be lowercase alphanumeric with optional hyphens and dots'
'Organization name must be lowercase alphanumeric with optional hyphens and dots',
);
}

View File

@@ -12,7 +12,8 @@ import type { TRegistryProtocol } from '../interfaces/auth.interfaces.ts';
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class Package extends plugins.smartdata.SmartDataDbDoc<Package, Package> implements IPackage {
export class Package extends plugins.smartdata.SmartDataDbDoc<Package, Package>
implements IPackage {
@plugins.smartdata.unI()
public id: string = ''; // {protocol}:{org}:{name}
@@ -94,7 +95,7 @@ export class Package extends plugins.smartdata.SmartDataDbDoc<Package, Package>
public static async findByName(
protocol: TRegistryProtocol,
orgName: string,
name: string
name: string,
): Promise<Package | null> {
const id = Package.generateId(protocol, orgName, name);
return await Package.findById(id);
@@ -118,7 +119,7 @@ export class Package extends plugins.smartdata.SmartDataDbDoc<Package, Package>
isPrivate?: boolean;
limit?: number;
offset?: number;
}
},
): Promise<Package[]> {
const filter: Record<string, unknown> = {};
if (options?.protocol) filter.protocol = options.protocol;
@@ -133,7 +134,7 @@ export class Package extends plugins.smartdata.SmartDataDbDoc<Package, Package>
const filtered = allPackages.filter(
(pkg) =>
pkg.name.toLowerCase().includes(lowerQuery) ||
pkg.description?.toLowerCase().includes(lowerQuery)
pkg.description?.toLowerCase().includes(lowerQuery),
);
// Apply pagination

View File

@@ -4,7 +4,7 @@
*/
import * as plugins from '../plugins.ts';
import type { IPlatformSettings, IPlatformAuthSettings } from '../interfaces/auth.interfaces.ts';
import type { IPlatformAuthSettings, IPlatformSettings } from '../interfaces/auth.interfaces.ts';
import { db } from './db.ts';
const DEFAULT_AUTH_SETTINGS: IPlatformAuthSettings = {
@@ -16,8 +16,7 @@ const DEFAULT_AUTH_SETTINGS: IPlatformAuthSettings = {
@plugins.smartdata.Collection(() => db)
export class PlatformSettings
extends plugins.smartdata.SmartDataDbDoc<PlatformSettings, PlatformSettings>
implements IPlatformSettings
{
implements IPlatformSettings {
@plugins.smartdata.unI()
public id: string = 'singleton';
@@ -51,7 +50,7 @@ export class PlatformSettings
*/
public async updateAuthSettings(
settings: Partial<IPlatformAuthSettings>,
updatedById?: string
updatedById?: string,
): Promise<void> {
this.auth = { ...this.auth, ...settings };
this.updatedAt = new Date();

View File

@@ -7,7 +7,9 @@ import type { IRepositoryPermission, TRepositoryRole } from '../interfaces/auth.
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class RepositoryPermission extends plugins.smartdata.SmartDataDbDoc<RepositoryPermission, RepositoryPermission> implements IRepositoryPermission {
export class RepositoryPermission
extends plugins.smartdata.SmartDataDbDoc<RepositoryPermission, RepositoryPermission>
implements IRepositoryPermission {
@plugins.smartdata.unI()
public id: string = '';
@@ -104,7 +106,7 @@ export class RepositoryPermission extends plugins.smartdata.SmartDataDbDoc<Repos
*/
public static async findPermission(
repositoryId: string,
userId: string
userId: string,
): Promise<RepositoryPermission | null> {
return await RepositoryPermission.getUserPermission(repositoryId, userId);
}
@@ -114,7 +116,7 @@ export class RepositoryPermission extends plugins.smartdata.SmartDataDbDoc<Repos
*/
public static async getUserPermission(
repositoryId: string,
userId: string
userId: string,
): Promise<RepositoryPermission | null> {
return await RepositoryPermission.getInstance({
repositoryId,
@@ -127,7 +129,7 @@ export class RepositoryPermission extends plugins.smartdata.SmartDataDbDoc<Repos
*/
public static async getTeamPermission(
repositoryId: string,
teamId: string
teamId: string,
): Promise<RepositoryPermission | null> {
return await RepositoryPermission.getInstance({
repositoryId,
@@ -149,7 +151,7 @@ export class RepositoryPermission extends plugins.smartdata.SmartDataDbDoc<Repos
*/
public static async getTeamPermissionsForRepo(
repositoryId: string,
teamIds: string[]
teamIds: string[],
): Promise<RepositoryPermission[]> {
if (teamIds.length === 0) return [];
return await RepositoryPermission.getInstances({

View File

@@ -3,11 +3,16 @@
*/
import * as plugins from '../plugins.ts';
import type { IRepository, TRepositoryVisibility, TRegistryProtocol } from '../interfaces/auth.interfaces.ts';
import type {
IRepository,
TRegistryProtocol,
TRepositoryVisibility,
} from '../interfaces/auth.interfaces.ts';
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class Repository extends plugins.smartdata.SmartDataDbDoc<Repository, Repository> implements IRepository {
export class Repository extends plugins.smartdata.SmartDataDbDoc<Repository, Repository>
implements IRepository {
@plugins.smartdata.unI()
public id: string = '';
@@ -70,7 +75,9 @@ export class Repository extends plugins.smartdata.SmartDataDbDoc<Repository, Rep
// Validate name
const nameRegex = /^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$/;
if (!nameRegex.test(data.name.toLowerCase())) {
throw new Error('Repository name must be lowercase alphanumeric with optional dots, hyphens, or underscores');
throw new Error(
'Repository name must be lowercase alphanumeric with optional dots, hyphens, or underscores',
);
}
// Check for duplicate name in org + protocol
@@ -105,7 +112,7 @@ export class Repository extends plugins.smartdata.SmartDataDbDoc<Repository, Rep
public static async findByName(
organizationId: string,
name: string,
protocol: TRegistryProtocol
protocol: TRegistryProtocol,
): Promise<Repository | null> {
return await Repository.getInstance({
organizationId,

View File

@@ -7,7 +7,8 @@ import type { ISession } from '../interfaces/auth.interfaces.ts';
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class Session extends plugins.smartdata.SmartDataDbDoc<Session, Session> implements ISession {
export class Session extends plugins.smartdata.SmartDataDbDoc<Session, Session>
implements ISession {
@plugins.smartdata.unI()
public id: string = '';
@@ -94,7 +95,7 @@ export class Session extends plugins.smartdata.SmartDataDbDoc<Session, Session>
*/
public static async invalidateAllUserSessions(
userId: string,
reason: string = 'logout_all'
reason: string = 'logout_all',
): Promise<number> {
const sessions = await Session.getUserSessions(userId);
for (const session of sessions) {

View File

@@ -7,7 +7,8 @@ import type { ITeamMember, TTeamRole } from '../interfaces/auth.interfaces.ts';
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class TeamMember extends plugins.smartdata.SmartDataDbDoc<TeamMember, TeamMember> implements ITeamMember {
export class TeamMember extends plugins.smartdata.SmartDataDbDoc<TeamMember, TeamMember>
implements ITeamMember {
@plugins.smartdata.unI()
public id: string = '';