tons of server side fixes

This commit is contained in:
KingKDot
2026-04-14 10:23:59 -05:00
parent 59da653142
commit 303b7a43ba
9 changed files with 227 additions and 11 deletions
@@ -15,6 +15,7 @@ import {
hashTokenForSession,
persistRevokedTokenHash,
getSessionById,
deleteInactiveSessions,
} from "../../db";
import { logger } from "../../logger";
import {
@@ -187,6 +188,27 @@ export async function handleAuthRoutes(
});
}
if (req.method === "DELETE" && url.pathname === "/api/sessions/inactive") {
const user = await authenticateRequest(req);
if (!user) {
return Response.json({ error: "Not authenticated" }, { status: 401 });
}
const removed = deleteInactiveSessions(user.userId);
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.LOGOUT,
details: `Removed ${removed} inactive session(s)`,
success: true,
});
return Response.json({ ok: true, removed });
}
if (req.method === "GET" && url.pathname.match(/^\/api\/users\/\d+\/sessions$/)) {
const user = await authenticateRequest(req);
if (!user) {
@@ -4,7 +4,7 @@ import { authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import * as buildManager from "../../build/buildManager";
import * as clientManager from "../../clientManager";
import { deleteBuild, getAllBuilds, getBuild } from "../../db";
import { deleteBuild, getAllBuilds, getBuild, countBuildsForUser, getOldestBuildForUser } from "../../db";
import { encodeMessage } from "../../protocol";
import { metrics } from "../../metrics";
import { requirePermission } from "../../rbac";
@@ -180,6 +180,30 @@ export async function handleBuildRoutes(
const buildId = uuidv4();
const ip = server.requestIP(req)?.address || "unknown";
// Enforce max 10 builds per user — auto-delete oldest when limit exceeded
const MAX_BUILDS_PER_USER = 10;
const currentCount = countBuildsForUser(user.userId);
if (currentCount >= MAX_BUILDS_PER_USER) {
const oldest = getOldestBuildForUser(user.userId);
if (oldest) {
if (oldest.files) {
const rootDir = resolveRuntimeRoot();
const outDir = path.join(rootDir, "dist-clients");
for (const file of oldest.files) {
try {
const fp = path.join(outDir, file.filename);
if (fs.existsSync(fp)) {
fs.unlinkSync(fp);
}
} catch {}
}
}
buildManager.deleteBuildStream(oldest.id);
deleteBuild(oldest.id);
logger.info(`[build:limit] Auto-deleted oldest build ${oldest.id.substring(0, 8)} for user ${user.username} (limit: ${MAX_BUILDS_PER_USER})`);
}
}
logAudit({
timestamp: Date.now(),
username: user.username,
@@ -324,7 +348,10 @@ export async function handleBuildRoutes(
if (req.method === "GET" && url.pathname === "/api/build/list") {
requirePermission(user, "clients:build");
const builds = getAllBuilds(user.userId, user.role);
const showAll = url.searchParams.get("all") === "true" && user.role === "admin";
const builds = showAll
? getAllBuilds(undefined, "admin")
: getAllBuilds(user.userId, user.role === "admin" ? "operator" : user.role);
return Response.json({ builds });
}
@@ -16,6 +16,18 @@ import { logAudit, AuditAction } from "../../auditLog";
import * as clientManager from "../../clientManager";
import { setOnlineState } from "../../db";
import { requirePermission } from "../../rbac";
import { getUserClientAccessScope, setUserClientAccessScope, setUserClientAccessRule } from "../../users";
function grantClientAccess(userId: number, role: string, clientId: string): void {
if (role === "admin") return;
const currentScope = getUserClientAccessScope(userId);
if (currentScope === "none") {
setUserClientAccessScope(userId, "allowlist");
}
if (currentScope === "none" || currentScope === "allowlist") {
setUserClientAccessRule(userId, clientId, "allow");
}
}
function getClientScopeFilters(userId: number, role: string): {
allowedClientIds?: string[];
@@ -118,6 +130,8 @@ export async function handleEnrollmentRoutes(
setClientEnrollmentStatus(clientId, "approved", user.username);
grantClientAccess(user.userId, user.role, clientId);
_postApproveHook?.(clientId);
logAudit({
@@ -261,6 +275,7 @@ export async function handleEnrollmentRoutes(
if (ok) {
updated++;
if (action === "approve") {
grantClientAccess(user.userId, user.role, id);
_postApproveHook?.(id);
}
}
@@ -1,7 +1,7 @@
import { authenticateRequest } from "../../auth";
import { AuditAction, getAuditLogs, logAudit } from "../../auditLog";
import { getConfig, updateSecurityConfig, updateTlsConfig, updateAppearanceConfig, getExportableConfig, importFullConfig } from "../../config";
import { getClientMetricsSummary } from "../../db";
import { getClientMetricsSummary, getClientMetricsSummaryForUser } from "../../db";
import { metrics } from "../../metrics";
import { requirePermission } from "../../rbac";
import { getUserTelegramChatId, setUserTelegramChatId, getUserClientAccessScope, listUserClientRuleIdsByAccess } from "../../users";
@@ -40,7 +40,9 @@ export async function handleMiscRoutes(
const snapshot = metrics.getSnapshot();
const clientSummary = getClientMetricsSummary();
const clientSummary = user.role === "admin"
? getClientMetricsSummary()
: getClientMetricsSummaryForUser(user.userId);
snapshot.clients.total = clientSummary.total;
snapshot.clients.online = clientSummary.online;
snapshot.clients.offline = clientSummary.total - clientSummary.online;