Add expiring panel auth sessions

This commit is contained in:
2026-04-02 00:45:27 +03:00
parent e342693211
commit 69c97ea387
11 changed files with 514 additions and 93 deletions

View File

@@ -1,7 +1,12 @@
import express, { type Request, type Response } from 'express';
import fs from 'node:fs/promises';
import path from 'node:path';
import type { ControlPlaneState, CreateUserInput, UpdateSystemInput } from '../src/shared/contracts';
import type {
ControlPlaneState,
CreateUserInput,
PanelLoginInput,
UpdateSystemInput,
} from '../src/shared/contracts';
import { validateCreateUserInput, validateSystemInput } from '../src/shared/validation';
import {
buildRuntimePaths,
@@ -10,6 +15,7 @@ import {
render3proxyConfig,
type RuntimePaths,
} from './lib/config';
import { AuthService } from './lib/auth';
import type { RuntimeController } from './lib/runtime';
import { StateStore } from './lib/store';
@@ -17,9 +23,10 @@ export interface AppServices {
store: StateStore;
runtime: RuntimeController;
runtimeRootDir: string;
auth: AuthService;
}
export function createApp({ store, runtime, runtimeRootDir }: AppServices) {
export function createApp({ store, runtime, runtimeRootDir, auth }: AppServices) {
const app = express();
const runtimePaths = buildRuntimePaths(runtimeRootDir);
const distDir = path.resolve('dist');
@@ -27,6 +34,40 @@ export function createApp({ store, runtime, runtimeRootDir }: AppServices) {
app.use(express.json());
app.use(express.static(distDir));
app.post('/api/auth/login', (request, response) => {
const input = request.body as Partial<PanelLoginInput>;
try {
const payload = auth.login(input.login?.trim() ?? '', input.password ?? '');
response.json(payload);
} catch (error) {
response.status(401).json({
error: error instanceof Error ? error.message : 'Wrong panel credentials.',
});
}
});
app.use('/api', (request, response, next) => {
if (request.path === '/auth/login' || request.path === '/health') {
next();
return;
}
const token = auth.extractBearerToken(request);
if (!token) {
response.status(401).json(auth.unauthorizedError());
return;
}
if (!auth.verify(token)) {
response.status(401).json(auth.invalidTokenError());
return;
}
next();
});
app.get('/api/health', async (_request, response) => {
const state = await store.read();
const previewConfig = render3proxyConfig(state, runtimePaths);