Add editable system configuration flow

This commit is contained in:
2026-04-02 00:25:14 +03:00
parent 25f6beedd8
commit 1f73a29137
11 changed files with 756 additions and 152 deletions

View File

@@ -7,6 +7,7 @@ import type {
ProxyServiceRecord,
ProxyUserRecord,
} from '../../src/shared/contracts';
import { quotaMbToBytes } from '../../src/shared/validation';
import type { ServiceState } from '../../src/lib/3proxy';
const MB = 1024 * 1024;
@@ -42,39 +43,6 @@ export function buildRuntimePaths(rootDir: string): RuntimePaths {
};
}
export function validateCreateUserInput(
input: Partial<CreateUserInput>,
services: ProxyServiceRecord[],
): CreateUserInput {
const username = input.username?.trim() ?? '';
const password = input.password?.trim() ?? '';
const serviceId = input.serviceId?.trim() ?? '';
const quotaMb = input.quotaMb ?? null;
assertSafeToken(username, 'Username');
assertSafeToken(password, 'Password');
if (!serviceId) {
throw new Error('Service is required.');
}
const service = services.find((entry) => entry.id === serviceId);
if (!service || !service.enabled || !service.assignable) {
throw new Error('Service must reference an enabled assignable entry.');
}
if (quotaMb !== null && (!Number.isFinite(quotaMb) || quotaMb <= 0 || !Number.isInteger(quotaMb))) {
throw new Error('Quota must be a positive integer number of megabytes.');
}
return {
username,
password,
serviceId,
quotaMb,
};
}
export function createUserRecord(state: ControlPlaneState, input: CreateUserInput): ProxyUserRecord {
if (state.userRecords.some((user) => user.username === input.username)) {
throw new Error('Username already exists.');
@@ -88,7 +56,7 @@ export function createUserRecord(state: ControlPlaneState, input: CreateUserInpu
status: 'idle',
paused: false,
usedBytes: 0,
quotaBytes: input.quotaMb === null ? null : input.quotaMb * MB,
quotaBytes: quotaMbToBytes(input.quotaMb),
};
}
@@ -241,16 +209,6 @@ function renderServiceCommand(service: ProxyServiceRecord): string {
return `proxy -p${service.port}`;
}
function assertSafeToken(value: string, label: string): void {
if (!value) {
throw new Error(`${label} is required.`);
}
if (!/^[A-Za-z0-9._~!@#$%^&*+=:/-]+$/.test(value)) {
throw new Error(`${label} contains unsupported characters.`);
}
}
function normalizePath(value: string): string {
return value.replace(/\\/g, '/');
}