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

@@ -6,7 +6,11 @@ import { User } from '../../ts/models/user.ts';
import { ApiToken } from '../../ts/models/apitoken.ts';
import { AuthService } from '../../ts/services/auth.service.ts';
import { TokenService } from '../../ts/services/token.service.ts';
import type { TRegistryProtocol, ITokenScope, TUserStatus } from '../../ts/interfaces/auth.interfaces.ts';
import type {
ITokenScope,
TRegistryProtocol,
TUserStatus,
} from '../../ts/interfaces/auth.interfaces.ts';
import { testConfig } from '../test.config.ts';
const TEST_PASSWORD = 'TestPassword123!';
@@ -25,7 +29,7 @@ export interface ICreateTestUserOptions {
* Create a test user with sensible defaults
*/
export async function createTestUser(
overrides: ICreateTestUserOptions = {}
overrides: ICreateTestUserOptions = {},
): Promise<{ user: User; password: string }> {
const uniqueId = crypto.randomUUID().slice(0, 8);
const password = overrides.password || TEST_PASSWORD;
@@ -61,7 +65,7 @@ export async function createAdminUser(): Promise<{ user: User; password: string
*/
export async function loginUser(
email: string,
password: string
password: string,
): Promise<{ accessToken: string; refreshToken: string; sessionId: string }> {
const authService = new AuthService({
jwtSecret: testConfig.jwt.secret,
@@ -96,7 +100,7 @@ export interface ICreateTestApiTokenOptions {
* Create test API token
*/
export async function createTestApiToken(
options: ICreateTestApiTokenOptions
options: ICreateTestApiTokenOptions,
): Promise<{ rawToken: string; token: ApiToken }> {
const tokenService = new TokenService();
@@ -127,7 +131,7 @@ export function createAuthHeader(token: string): { Authorization: string } {
*/
export function createBasicAuthHeader(
username: string,
password: string
password: string,
): { Authorization: string } {
const credentials = btoa(`${username}:${password}`);
return { Authorization: `Basic ${credentials}` };

View File

@@ -15,7 +15,7 @@ let isConnected = false;
// We need to patch the global db export since models reference it
// This is done by re-initializing with the test config
import { initDb, closeDb } from '../../ts/models/db.ts';
import { closeDb, initDb } from '../../ts/models/db.ts';
/**
* Initialize test database with unique name per test run

View File

@@ -11,10 +11,10 @@ import { Package } from '../../ts/models/package.ts';
import { RepositoryPermission } from '../../ts/models/repository.permission.ts';
import type {
TOrganizationRole,
TTeamRole,
TRegistryProtocol,
TRepositoryRole,
TRepositoryVisibility,
TRegistryProtocol,
TTeamRole,
} from '../../ts/interfaces/auth.interfaces.ts';
export interface ICreateTestOrganizationOptions {
@@ -29,7 +29,7 @@ export interface ICreateTestOrganizationOptions {
* Create test organization
*/
export async function createTestOrganization(
options: ICreateTestOrganizationOptions
options: ICreateTestOrganizationOptions,
): Promise<Organization> {
const uniqueId = crypto.randomUUID().slice(0, 8);
@@ -53,7 +53,7 @@ export async function createTestOrganization(
*/
export async function createOrgWithOwner(
ownerId: string,
orgOptions?: Partial<ICreateTestOrganizationOptions>
orgOptions?: Partial<ICreateTestOrganizationOptions>,
): Promise<{
organization: Organization;
membership: OrganizationMember;
@@ -82,7 +82,7 @@ export async function addOrgMember(
organizationId: string,
userId: string,
role: TOrganizationRole = 'member',
invitedBy?: string
invitedBy?: string,
): Promise<OrganizationMember> {
const membership = await OrganizationMember.addMember({
organizationId,
@@ -113,7 +113,7 @@ export interface ICreateTestRepositoryOptions {
* Create test repository
*/
export async function createTestRepository(
options: ICreateTestRepositoryOptions
options: ICreateTestRepositoryOptions,
): Promise<Repository> {
const uniqueId = crypto.randomUUID().slice(0, 8);
@@ -152,7 +152,7 @@ export async function createTestTeam(options: ICreateTestTeamOptions): Promise<T
export async function addTeamMember(
teamId: string,
userId: string,
role: TTeamRole = 'member'
role: TTeamRole = 'member',
): Promise<TeamMember> {
const member = new TeamMember();
member.id = await TeamMember.getNewId();
@@ -176,7 +176,7 @@ export interface IGrantRepoPermissionOptions {
* Grant repository permission
*/
export async function grantRepoPermission(
options: IGrantRepoPermissionOptions
options: IGrantRepoPermissionOptions,
): Promise<RepositoryPermission> {
const perm = new RepositoryPermission();
perm.id = await RepositoryPermission.getNewId();

View File

@@ -75,7 +75,7 @@ export const del = (path: string, headers?: Record<string, string>) =>
export function assertStatus(response: ITestResponse, expected: number): void {
if (response.status !== expected) {
throw new Error(
`Expected status ${expected} but got ${response.status}: ${JSON.stringify(response.body)}`
`Expected status ${expected} but got ${response.status}: ${JSON.stringify(response.body)}`,
);
}
}
@@ -98,7 +98,7 @@ export function assertBodyHas(response: ITestResponse, keys: string[]): void {
export function assertSuccess(response: ITestResponse): void {
if (response.status < 200 || response.status >= 300) {
throw new Error(
`Expected successful response but got ${response.status}: ${JSON.stringify(response.body)}`
`Expected successful response but got ${response.status}: ${JSON.stringify(response.body)}`,
);
}
}

View File

@@ -4,82 +4,82 @@
// Database helpers
export {
setupTestDb,
cleanupTestDb,
teardownTestDb,
clearCollections,
getTestDbName,
getTestDb,
getTestDbName,
setupTestDb,
teardownTestDb,
} from './db.helper.ts';
// Auth helpers
export {
createTestUser,
createAdminUser,
loginUser,
createTestApiToken,
createAuthHeader,
createBasicAuthHeader,
createTestApiToken,
createTestUser,
getTestPassword,
type ICreateTestUserOptions,
type ICreateTestApiTokenOptions,
type ICreateTestUserOptions,
loginUser,
} from './auth.helper.ts';
// Factory helpers
export {
createTestOrganization,
createOrgWithOwner,
addOrgMember,
addTeamMember,
createFullTestScenario,
createOrgWithOwner,
createTestOrganization,
createTestPackage,
createTestRepository,
createTestTeam,
addTeamMember,
grantRepoPermission,
createTestPackage,
createFullTestScenario,
type ICreateTestOrganizationOptions,
type ICreateTestPackageOptions,
type ICreateTestRepositoryOptions,
type ICreateTestTeamOptions,
type IGrantRepoPermissionOptions,
type ICreateTestPackageOptions,
} from './factory.helper.ts';
// HTTP helpers
export {
testRequest,
get,
post,
put,
patch,
del,
assertStatus,
assertBodyHas,
assertSuccess,
assertError,
assertStatus,
assertSuccess,
del,
get,
type ITestRequest,
type ITestResponse,
patch,
post,
put,
testRequest,
} from './http.helper.ts';
// Subprocess helpers
export {
runCommand,
commandExists,
clients,
skipIfMissing,
type ICommandResult,
commandExists,
type ICommandOptions,
type ICommandResult,
runCommand,
skipIfMissing,
} from './subprocess.helper.ts';
// Storage helpers
export {
setupTestStorage,
checkStorageAvailable,
objectExists,
listObjects,
cleanupTestStorage,
deleteObject,
deletePrefix,
cleanupTestStorage,
isStorageAvailable,
listObjects,
objectExists,
setupTestStorage,
} from './storage.helper.ts';
// Re-export test config
export { testConfig, getTestConfig } from '../test.config.ts';
export { getTestConfig, testConfig } from '../test.config.ts';

View File

@@ -22,7 +22,7 @@ export interface ICommandOptions {
*/
export async function runCommand(
cmd: string[],
options: ICommandOptions = {}
options: ICommandOptions = {},
): Promise<ICommandResult> {
const { cwd, env, timeout = 60000, stdin } = options;
@@ -116,7 +116,7 @@ export const clients = {
publish: (dir: string, registry: string, token: string) =>
runCommand(
['cargo', 'publish', '--registry', 'stack-test', '--token', token, '--allow-dirty'],
{ cwd: dir }
{ cwd: dir },
),
yank: (crate: string, version: string, token: string) =>
runCommand([
@@ -164,7 +164,7 @@ export const clients = {
'--repository',
JSON.stringify({ type: 'composer', url: repository }),
],
{ cwd: dir }
{ cwd: dir },
),
},
@@ -190,7 +190,7 @@ export const clients = {
`-Dusername=${username}`,
`-Dpassword=${password}`,
],
{ cwd: dir }
{ cwd: dir },
),
package: (dir: string) => runCommand(['mvn', 'package', '-DskipTests'], { cwd: dir }),
},