Skip to content

Commit 39b2183

Browse files
feat: validate maxSessions input in settings update handler
- Added validation to ensure maxSessions is an integer before processing the request. - Responds with a 400 status and an error message if the input is not a valid integer.
1 parent 0e944e2 commit 39b2183

2 files changed

Lines changed: 33 additions & 6 deletions

File tree

apps/server/src/routes/terminal/routes/settings.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ export function createSettingsUpdateHandler() {
2626
const { maxSessions } = req.body;
2727

2828
if (typeof maxSessions === "number") {
29+
if (!Number.isInteger(maxSessions)) {
30+
res.status(400).json({
31+
success: false,
32+
error: "maxSessions must be an integer",
33+
});
34+
return;
35+
}
2936
if (maxSessions < MIN_MAX_SESSIONS || maxSessions > MAX_MAX_SESSIONS) {
3037
res.status(400).json({
3138
success: false,

apps/ui/src/components/views/terminal-view.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ export function TerminalView() {
245245
const lastCreateTimeRef = useRef<number>(0);
246246
const isCreatingRef = useRef<boolean>(false);
247247
const prevProjectPathRef = useRef<string | null>(null);
248-
const isRestoringLayoutRef = useRef<boolean>(false);
248+
const restoringProjectPathRef = useRef<string | null>(null);
249249
const [newSessionIds, setNewSessionIds] = useState<Set<string>>(new Set());
250250
const [serverSessionInfo, setServerSessionInfo] = useState<{ current: number; max: number } | null>(null);
251251
const hasShownHighRamWarningRef = useRef<boolean>(false);
@@ -438,11 +438,14 @@ export function TerminalView() {
438438
const currentPath = currentProject?.path || null;
439439
const prevPath = prevProjectPathRef.current;
440440

441-
// Skip if no change or if we're restoring layout
442-
if (currentPath === prevPath || isRestoringLayoutRef.current) {
441+
// Skip if no change
442+
if (currentPath === prevPath) {
443443
return;
444444
}
445445

446+
// If we're restoring a different project, that restore will be stale - let it finish but ignore results
447+
// The path check in restoreLayout will handle this
448+
446449
// Save layout for previous project (if there was one and has terminals)
447450
if (prevPath && terminalState.tabs.length > 0) {
448451
saveTerminalLayout(prevPath);
@@ -462,13 +465,20 @@ export function TerminalView() {
462465

463466
if (savedLayout && savedLayout.tabs.length > 0) {
464467
// Restore the saved layout - try to reconnect to existing sessions
465-
isRestoringLayoutRef.current = true;
468+
// Track which project we're restoring to detect stale restores
469+
restoringProjectPathRef.current = currentPath;
466470

467471
// Clear existing terminals first (only client state, sessions stay on server)
468472
clearTerminalState();
469473

470474
// Create terminals and build layout - try to reconnect or create new
471475
const restoreLayout = async () => {
476+
// Check if we're still restoring the same project (user may have switched)
477+
if (restoringProjectPathRef.current !== currentPath) {
478+
console.log("[Terminal] Restore cancelled - project changed");
479+
return;
480+
}
481+
472482
let failedSessions = 0;
473483
let totalSessions = 0;
474484
let reconnectedSessions = 0;
@@ -578,6 +588,12 @@ export function TerminalView() {
578588

579589
// For each saved tab, rebuild the layout
580590
for (let tabIndex = 0; tabIndex < savedLayout.tabs.length; tabIndex++) {
591+
// Check if project changed during restore - bail out early
592+
if (restoringProjectPathRef.current !== currentPath) {
593+
console.log("[Terminal] Restore cancelled mid-loop - project changed");
594+
return;
595+
}
596+
581597
const savedTab = savedLayout.tabs[tabIndex];
582598

583599
// Create the tab first
@@ -619,7 +635,10 @@ export function TerminalView() {
619635
duration: 5000,
620636
});
621637
} finally {
622-
isRestoringLayoutRef.current = false;
638+
// Only clear if we're still the active restore
639+
if (restoringProjectPathRef.current === currentPath) {
640+
restoringProjectPathRef.current = null;
641+
}
623642
}
624643
};
625644

@@ -631,7 +650,8 @@ export function TerminalView() {
631650
// Also save when tabs become empty so closed terminals stay closed on refresh
632651
const saveLayoutTimeoutRef = useRef<NodeJS.Timeout | null>(null);
633652
useEffect(() => {
634-
if (currentProject?.path && !isRestoringLayoutRef.current) {
653+
// Don't save while restoring this project's layout
654+
if (currentProject?.path && restoringProjectPathRef.current !== currentProject.path) {
635655
// Debounce saves to prevent excessive localStorage writes during rapid changes
636656
if (saveLayoutTimeoutRef.current) {
637657
clearTimeout(saveLayoutTimeoutRef.current);

0 commit comments

Comments
 (0)