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

@@ -3,8 +3,8 @@
*/
import { assertEquals, assertExists } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { setupTestDb, teardownTestDb, cleanupTestDb, createTestUser } from '../../helpers/index.ts';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import { cleanupTestDb, createTestUser, setupTestDb, teardownTestDb } from '../../helpers/index.ts';
import { ApiToken } from '../../../ts/models/apitoken.ts';
describe('ApiToken Model', () => {

View File

@@ -3,8 +3,8 @@
*/
import { assertEquals, assertExists, assertRejects } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { setupTestDb, teardownTestDb, cleanupTestDb, createTestUser } from '../../helpers/index.ts';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import { cleanupTestDb, createTestUser, setupTestDb, teardownTestDb } from '../../helpers/index.ts';
import { Organization } from '../../../ts/models/organization.ts';
describe('Organization Model', () => {
@@ -73,7 +73,7 @@ describe('Organization Model', () => {
});
},
Error,
'lowercase alphanumeric'
'lowercase alphanumeric',
);
});
@@ -87,7 +87,7 @@ describe('Organization Model', () => {
});
},
Error,
'lowercase alphanumeric'
'lowercase alphanumeric',
);
});
@@ -101,7 +101,7 @@ describe('Organization Model', () => {
});
},
Error,
'lowercase alphanumeric'
'lowercase alphanumeric',
);
});
@@ -115,7 +115,7 @@ describe('Organization Model', () => {
});
},
Error,
'lowercase alphanumeric'
'lowercase alphanumeric',
);
});

View File

@@ -3,14 +3,14 @@
*/
import { assertEquals, assertExists } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import {
setupTestDb,
teardownTestDb,
cleanupTestDb,
createTestUser,
createOrgWithOwner,
createTestRepository,
createTestUser,
setupTestDb,
teardownTestDb,
} from '../../helpers/index.ts';
import { Package } from '../../../ts/models/package.ts';
import type { IPackageVersion } from '../../../ts/interfaces/package.interfaces.ts';

View File

@@ -3,13 +3,13 @@
*/
import { assertEquals, assertExists, assertRejects } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import {
cleanupTestDb,
createOrgWithOwner,
createTestUser,
setupTestDb,
teardownTestDb,
cleanupTestDb,
createTestUser,
createOrgWithOwner,
} from '../../helpers/index.ts';
import { Repository } from '../../../ts/models/repository.ts';
@@ -103,7 +103,7 @@ describe('Repository Model', () => {
});
},
Error,
'already exists'
'already exists',
);
});
@@ -137,7 +137,7 @@ describe('Repository Model', () => {
});
},
Error,
'lowercase alphanumeric'
'lowercase alphanumeric',
);
});

View File

@@ -3,8 +3,8 @@
*/
import { assertEquals, assertExists } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { setupTestDb, teardownTestDb, cleanupTestDb, createTestUser } from '../../helpers/index.ts';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import { cleanupTestDb, createTestUser, setupTestDb, teardownTestDb } from '../../helpers/index.ts';
import { Session } from '../../../ts/models/session.ts';
describe('Session Model', () => {
@@ -70,9 +70,21 @@ describe('Session Model', () => {
describe('getUserSessions', () => {
it('should return all valid sessions for user', async () => {
await Session.createSession({ userId: testUserId, userAgent: 'Agent 1', ipAddress: '1.1.1.1' });
await Session.createSession({ userId: testUserId, userAgent: 'Agent 2', ipAddress: '2.2.2.2' });
await Session.createSession({ userId: testUserId, userAgent: 'Agent 3', ipAddress: '3.3.3.3' });
await Session.createSession({
userId: testUserId,
userAgent: 'Agent 1',
ipAddress: '1.1.1.1',
});
await Session.createSession({
userId: testUserId,
userAgent: 'Agent 2',
ipAddress: '2.2.2.2',
});
await Session.createSession({
userId: testUserId,
userAgent: 'Agent 3',
ipAddress: '3.3.3.3',
});
const sessions = await Session.getUserSessions(testUserId);
assertEquals(sessions.length, 3);
@@ -110,9 +122,21 @@ describe('Session Model', () => {
describe('invalidateAllUserSessions', () => {
it('should invalidate all user sessions', async () => {
await Session.createSession({ userId: testUserId, userAgent: 'Agent 1', ipAddress: '1.1.1.1' });
await Session.createSession({ userId: testUserId, userAgent: 'Agent 2', ipAddress: '2.2.2.2' });
await Session.createSession({ userId: testUserId, userAgent: 'Agent 3', ipAddress: '3.3.3.3' });
await Session.createSession({
userId: testUserId,
userAgent: 'Agent 1',
ipAddress: '1.1.1.1',
});
await Session.createSession({
userId: testUserId,
userAgent: 'Agent 2',
ipAddress: '2.2.2.2',
});
await Session.createSession({
userId: testUserId,
userAgent: 'Agent 3',
ipAddress: '3.3.3.3',
});
const count = await Session.invalidateAllUserSessions(testUserId, 'Security logout');
assertEquals(count, 3);

View File

@@ -3,8 +3,8 @@
*/
import { assertEquals, assertExists, assertRejects } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { setupTestDb, teardownTestDb, cleanupTestDb } from '../../helpers/index.ts';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import { cleanupTestDb, setupTestDb, teardownTestDb } from '../../helpers/index.ts';
import { User } from '../../../ts/models/user.ts';
describe('User Model', () => {

View File

@@ -3,8 +3,8 @@
*/
import { assertEquals, assertExists } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { setupTestDb, teardownTestDb, cleanupTestDb, createTestUser } from '../../helpers/index.ts';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import { cleanupTestDb, createTestUser, setupTestDb, teardownTestDb } from '../../helpers/index.ts';
import { AuthService } from '../../../ts/services/auth.service.ts';
import { Session } from '../../../ts/models/session.ts';
import { testConfig } from '../../test.config.ts';

View File

@@ -3,8 +3,8 @@
*/
import { assertEquals, assertExists, assertMatch } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import { setupTestDb, teardownTestDb, cleanupTestDb, createTestUser } from '../../helpers/index.ts';
import { afterAll, beforeAll, beforeEach, describe, it } from 'jsr:@std/testing/bdd';
import { cleanupTestDb, createTestUser, setupTestDb, teardownTestDb } from '../../helpers/index.ts';
import { TokenService } from '../../../ts/services/token.service.ts';
import { ApiToken } from '../../../ts/models/apitoken.ts';
@@ -39,8 +39,8 @@ describe('TokenService', () => {
assertExists(result.rawToken);
assertExists(result.token);
// Check token format: srg_{prefix}_{random}
assertMatch(result.rawToken, /^srg_[a-z0-9]+_[a-z0-9]+$/);
// Check token format: srg_ + 64 hex chars
assertMatch(result.rawToken, /^srg_[a-f0-9]{64}$/);
assertEquals(result.token.name, 'test-token');
assertEquals(result.token.protocols.includes('npm'), true);
assertEquals(result.token.protocols.includes('oci'), true);
@@ -111,13 +111,19 @@ describe('TokenService', () => {
it('should reject invalid token format', async () => {
const validation = await tokenService.validateToken('invalid-format', '127.0.0.1');
assertEquals(validation, null);
assertEquals(validation.valid, false);
assertEquals(validation.errorCode, 'INVALID_TOKEN_FORMAT');
});
it('should reject non-existent token', async () => {
const validation = await tokenService.validateToken('srg_abc123_def456', '127.0.0.1');
// Must match srg_ prefix + 64 hex chars = 68 total
const validation = await tokenService.validateToken(
'srg_0000000000000000000000000000000000000000000000000000000000000000',
'127.0.0.1',
);
assertEquals(validation, null);
assertEquals(validation.valid, false);
assertEquals(validation.errorCode, 'TOKEN_NOT_FOUND');
});
it('should reject revoked token', async () => {
@@ -132,7 +138,9 @@ describe('TokenService', () => {
const validation = await tokenService.validateToken(rawToken, '127.0.0.1');
assertEquals(validation, null);
assertEquals(validation.valid, false);
// findByHash excludes revoked tokens, so the token is not found
assertEquals(validation.errorCode, 'TOKEN_NOT_FOUND');
});
it('should reject expired token', async () => {
@@ -150,7 +158,8 @@ describe('TokenService', () => {
const validation = await tokenService.validateToken(rawToken, '127.0.0.1');
assertEquals(validation, null);
assertEquals(validation.valid, false);
assertEquals(validation.errorCode, 'TOKEN_EXPIRED');
});
it('should record usage on validation', async () => {