feat(worker): live box password apply route

This commit is contained in:
Gabriel Brown
2026-07-13 10:07:06 -04:00
parent 58bd101793
commit 78aae034b5
2 changed files with 25 additions and 2 deletions
+23 -1
View File
@@ -8,6 +8,7 @@ import {
listBoxTree,
readBoxFile,
restartBox,
setBoxUserPassword,
startBox,
stopBox,
writeBoxFile,
@@ -66,7 +67,9 @@ const jobRoute = (pathname: string) => {
};
export const boxRoute = (pathname: string) => {
const match = /^\/box\/(status|lifecycle|tree|file)$/.exec(pathname);
const match = /^\/box\/(status|lifecycle|tree|file|set-password)$/.exec(
pathname,
);
return match?.[1] ? { action: match[1] } : null;
};
@@ -136,6 +139,25 @@ export const startWorkerServer = () => {
);
return;
}
if (request.method === 'POST' && box.action === 'set-password') {
const body = await parseJson<{ password?: string | null }>(request);
// Convex owns validation/storage; this route only applies to the
// live container. Re-check bounds anyway so a bad caller can't
// push a malformed chpasswd line.
const password = body.password ?? null;
if (
password !== null &&
(password.length < 8 ||
password.length > 128 ||
// eslint-disable-next-line no-control-regex -- rejecting control chars is the point
/[\u0000-\u001f\u007f]/.test(password))
) {
sendJson(response, 400, { error: 'Invalid password' });
return;
}
sendJson(response, 200, await setBoxUserPassword(user, password));
return;
}
sendJson(response, 404, { error: 'Not found' });
return;
}
@@ -13,12 +13,13 @@ const load = async () => {
describe('boxRoute', () => {
afterEach(() => vi.resetModules());
test('matches the four box actions', async () => {
test('matches the five box actions', async () => {
const { boxRoute } = await load();
expect(boxRoute('/box/status')).toEqual({ action: 'status' });
expect(boxRoute('/box/lifecycle')).toEqual({ action: 'lifecycle' });
expect(boxRoute('/box/tree')).toEqual({ action: 'tree' });
expect(boxRoute('/box/file')).toEqual({ action: 'file' });
expect(boxRoute('/box/set-password')).toEqual({ action: 'set-password' });
});
test('rejects unknown, nested, and trailing paths', async () => {