BREAKING CHANGE(config): convert configuration management to read-only; remove updateConfiguration endpoint and client-side editing

This commit is contained in:
2026-02-03 23:26:51 +00:00
parent 5de3344905
commit 9e0e77737b
25 changed files with 2129 additions and 269 deletions

View File

@@ -10,7 +10,7 @@ tap.test('should start DCRouter with OpsServer', async () => {
testDcRouter = new DcRouter({
// Minimal config for testing
});
await testDcRouter.start();
expect(testDcRouter.opsServer).toBeInstanceOf(Object);
});
@@ -20,49 +20,40 @@ tap.test('should login as admin', async () => {
'http://localhost:3000/typedrequest',
'adminLoginWithUsernameAndPassword'
);
const response = await loginRequest.fire({
username: 'admin',
password: 'admin'
});
expect(response).toHaveProperty('identity');
adminIdentity = response.identity;
console.log('Admin logged in with JWT');
});
tap.test('should allow admin to update configuration', async () => {
const updateRequest = new TypedRequest<interfaces.requests.IReq_UpdateConfiguration>(
tap.test('should allow admin to verify identity', async () => {
const verifyRequest = new TypedRequest<interfaces.requests.IReq_VerifyIdentity>(
'http://localhost:3000/typedrequest',
'updateConfiguration'
'verifyIdentity'
);
const response = await updateRequest.fire({
const response = await verifyRequest.fire({
identity: adminIdentity,
section: 'security',
config: {
rateLimit: true,
spamDetection: true
}
});
expect(response).toHaveProperty('updated');
expect(response.updated).toBeTrue();
expect(response).toHaveProperty('valid');
expect(response.valid).toBeTrue();
console.log('Admin identity verified successfully');
});
tap.test('should reject configuration update without identity', async () => {
const updateRequest = new TypedRequest<interfaces.requests.IReq_UpdateConfiguration>(
tap.test('should reject verify identity without identity', async () => {
const verifyRequest = new TypedRequest<interfaces.requests.IReq_VerifyIdentity>(
'http://localhost:3000/typedrequest',
'updateConfiguration'
'verifyIdentity'
);
try {
await updateRequest.fire({
section: 'security',
config: {
rateLimit: false
}
});
await verifyRequest.fire({} as any);
expect(true).toBeFalse(); // Should not reach here
} catch (error) {
expect(error).toBeTruthy();
@@ -70,22 +61,18 @@ tap.test('should reject configuration update without identity', async () => {
}
});
tap.test('should reject configuration update with invalid JWT', async () => {
const updateRequest = new TypedRequest<interfaces.requests.IReq_UpdateConfiguration>(
tap.test('should reject verify identity with invalid JWT', async () => {
const verifyRequest = new TypedRequest<interfaces.requests.IReq_VerifyIdentity>(
'http://localhost:3000/typedrequest',
'updateConfiguration'
'verifyIdentity'
);
try {
await updateRequest.fire({
await verifyRequest.fire({
identity: {
...adminIdentity,
jwt: 'invalid.jwt.token'
},
section: 'security',
config: {
rateLimit: false
}
});
expect(true).toBeFalse(); // Should not reach here
} catch (error) {
@@ -99,17 +86,34 @@ tap.test('should allow access to public endpoints without auth', async () => {
'http://localhost:3000/typedrequest',
'getHealthStatus'
);
// No identity provided
const response = await healthRequest.fire({});
expect(response).toHaveProperty('health');
expect(response.health.healthy).toBeTrue();
console.log('Public endpoint accessible without auth');
});
tap.test('should allow read-only config access', async () => {
const configRequest = new TypedRequest<interfaces.requests.IReq_GetConfiguration>(
'http://localhost:3000/typedrequest',
'getConfiguration'
);
// Config is read-only and doesn't require auth
const response = await configRequest.fire({});
expect(response).toHaveProperty('config');
expect(response.config).toHaveProperty('email');
expect(response.config).toHaveProperty('dns');
expect(response.config).toHaveProperty('proxy');
expect(response.config).toHaveProperty('security');
console.log('Configuration read successfully');
});
tap.test('should stop DCRouter', async () => {
await testDcRouter.stop();
});
export default tap.start();
export default tap.start();