mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
refactor: replace fs with secureFs for improved file handling
This commit updates various modules to utilize the secure file system operations from the secureFs module instead of the native fs module. Key changes include: - Replaced fs imports with secureFs in multiple route handlers and services to enhance security and consistency in file operations. - Added centralized validation for working directories in the sdk-options module to ensure all AI model invocations are secure. These changes aim to improve the security and maintainability of file handling across the application.
This commit is contained in:
@@ -1,28 +1,26 @@
|
||||
import { createRootRoute, Outlet, useLocation, useNavigate } from "@tanstack/react-router";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { Sidebar } from "@/components/layout/sidebar";
|
||||
import { FileBrowserProvider, useFileBrowser, setGlobalFileBrowser } from "@/contexts/file-browser-context";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { useSetupStore } from "@/store/setup-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { Toaster } from "sonner";
|
||||
import { ThemeOption, themeOptions } from "@/config/theme-options";
|
||||
import { createRootRoute, Outlet, useLocation, useNavigate } from '@tanstack/react-router';
|
||||
import { useEffect, useState, useCallback, useDeferredValue } from 'react';
|
||||
import { Sidebar } from '@/components/layout/sidebar';
|
||||
import {
|
||||
FileBrowserProvider,
|
||||
useFileBrowser,
|
||||
setGlobalFileBrowser,
|
||||
} from '@/contexts/file-browser-context';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { Toaster } from 'sonner';
|
||||
import { ThemeOption, themeOptions } from '@/config/theme-options';
|
||||
|
||||
function RootLayoutContent() {
|
||||
const location = useLocation();
|
||||
const {
|
||||
setIpcConnected,
|
||||
theme,
|
||||
currentProject,
|
||||
previewTheme,
|
||||
getEffectiveTheme,
|
||||
} = useAppStore();
|
||||
const { setIpcConnected, theme, currentProject, previewTheme, getEffectiveTheme } = useAppStore();
|
||||
const { setupComplete } = useSetupStore();
|
||||
const navigate = useNavigate();
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const [streamerPanelOpen, setStreamerPanelOpen] = useState(false);
|
||||
const [setupHydrated, setSetupHydrated] = useState(() =>
|
||||
useSetupStore.persist?.hasHydrated?.() ?? false
|
||||
const [setupHydrated, setSetupHydrated] = useState(
|
||||
() => useSetupStore.persist?.hasHydrated?.() ?? false
|
||||
);
|
||||
const { openFileBrowser } = useFileBrowser();
|
||||
|
||||
@@ -31,14 +29,14 @@ function RootLayoutContent() {
|
||||
const activeElement = document.activeElement;
|
||||
if (activeElement) {
|
||||
const tagName = activeElement.tagName.toLowerCase();
|
||||
if (tagName === "input" || tagName === "textarea" || tagName === "select") {
|
||||
if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
|
||||
return;
|
||||
}
|
||||
if (activeElement.getAttribute("contenteditable") === "true") {
|
||||
if (activeElement.getAttribute('contenteditable') === 'true') {
|
||||
return;
|
||||
}
|
||||
const role = activeElement.getAttribute("role");
|
||||
if (role === "textbox" || role === "searchbox" || role === "combobox") {
|
||||
const role = activeElement.getAttribute('role');
|
||||
if (role === 'textbox' || role === 'searchbox' || role === 'combobox') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -47,20 +45,22 @@ function RootLayoutContent() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === "\\") {
|
||||
if (event.key === '\\') {
|
||||
event.preventDefault();
|
||||
setStreamerPanelOpen((prev) => !prev);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleStreamerPanelShortcut);
|
||||
window.addEventListener('keydown', handleStreamerPanelShortcut);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleStreamerPanelShortcut);
|
||||
window.removeEventListener('keydown', handleStreamerPanelShortcut);
|
||||
};
|
||||
}, [handleStreamerPanelShortcut]);
|
||||
|
||||
const effectiveTheme = getEffectiveTheme();
|
||||
// Defer the theme value to keep UI responsive during rapid hover changes
|
||||
const deferredTheme = useDeferredValue(effectiveTheme);
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
@@ -78,7 +78,7 @@ function RootLayoutContent() {
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (typeof unsubscribe === "function") {
|
||||
if (typeof unsubscribe === 'function') {
|
||||
unsubscribe();
|
||||
}
|
||||
};
|
||||
@@ -88,10 +88,10 @@ function RootLayoutContent() {
|
||||
useEffect(() => {
|
||||
if (!setupHydrated) return;
|
||||
|
||||
if (!setupComplete && location.pathname !== "/setup") {
|
||||
navigate({ to: "/setup" });
|
||||
} else if (setupComplete && location.pathname === "/setup") {
|
||||
navigate({ to: "/" });
|
||||
if (!setupComplete && location.pathname !== '/setup') {
|
||||
navigate({ to: '/setup' });
|
||||
} else if (setupComplete && location.pathname === '/setup') {
|
||||
navigate({ to: '/' });
|
||||
}
|
||||
}, [setupComplete, setupHydrated, location.pathname, navigate]);
|
||||
|
||||
@@ -105,9 +105,9 @@ function RootLayoutContent() {
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.ping();
|
||||
setIpcConnected(result === "pong");
|
||||
setIpcConnected(result === 'pong');
|
||||
} catch (error) {
|
||||
console.error("IPC connection failed:", error);
|
||||
console.error('IPC connection failed:', error);
|
||||
setIpcConnected(false);
|
||||
}
|
||||
};
|
||||
@@ -117,34 +117,34 @@ function RootLayoutContent() {
|
||||
|
||||
// Restore to board view if a project was previously open
|
||||
useEffect(() => {
|
||||
if (isMounted && currentProject && location.pathname === "/") {
|
||||
navigate({ to: "/board" });
|
||||
if (isMounted && currentProject && location.pathname === '/') {
|
||||
navigate({ to: '/board' });
|
||||
}
|
||||
}, [isMounted, currentProject, location.pathname, navigate]);
|
||||
|
||||
// Apply theme class to document
|
||||
// Apply theme class to document - use deferred value to avoid blocking UI
|
||||
useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
// Remove all theme classes dynamically from themeOptions
|
||||
const themeClasses = themeOptions
|
||||
.map((option) => option.value)
|
||||
.filter((theme) => theme !== "system" as ThemeOption['value']);
|
||||
.filter((theme) => theme !== ('system' as ThemeOption['value']));
|
||||
root.classList.remove(...themeClasses);
|
||||
|
||||
if (effectiveTheme === "dark") {
|
||||
root.classList.add("dark");
|
||||
} else if (effectiveTheme === "system") {
|
||||
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
root.classList.add(isDark ? "dark" : "light");
|
||||
} else if (effectiveTheme && effectiveTheme !== "light") {
|
||||
root.classList.add(effectiveTheme);
|
||||
if (deferredTheme === 'dark') {
|
||||
root.classList.add('dark');
|
||||
} else if (deferredTheme === 'system') {
|
||||
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
root.classList.add(isDark ? 'dark' : 'light');
|
||||
} else if (deferredTheme && deferredTheme !== 'light') {
|
||||
root.classList.add(deferredTheme);
|
||||
} else {
|
||||
root.classList.add("light");
|
||||
root.classList.add('light');
|
||||
}
|
||||
}, [effectiveTheme, previewTheme, currentProject, theme]);
|
||||
}, [deferredTheme]);
|
||||
|
||||
// Setup view is full-screen without sidebar
|
||||
const isSetupRoute = location.pathname === "/setup";
|
||||
const isSetupRoute = location.pathname === '/setup';
|
||||
|
||||
if (isSetupRoute) {
|
||||
return (
|
||||
@@ -159,7 +159,7 @@ function RootLayoutContent() {
|
||||
<Sidebar />
|
||||
<div
|
||||
className="flex-1 flex flex-col overflow-hidden transition-all duration-300"
|
||||
style={{ marginRight: streamerPanelOpen ? "250px" : "0" }}
|
||||
style={{ marginRight: streamerPanelOpen ? '250px' : '0' }}
|
||||
>
|
||||
<Outlet />
|
||||
</div>
|
||||
@@ -167,7 +167,7 @@ function RootLayoutContent() {
|
||||
{/* Hidden streamer panel - opens with "\" key, pushes content */}
|
||||
<div
|
||||
className={`fixed top-0 right-0 h-full w-[250px] bg-background border-l border-border transition-transform duration-300 ${
|
||||
streamerPanelOpen ? "translate-x-0" : "translate-x-full"
|
||||
streamerPanelOpen ? 'translate-x-0' : 'translate-x-full'
|
||||
}`}
|
||||
/>
|
||||
<Toaster richColors position="bottom-right" />
|
||||
|
||||
Reference in New Issue
Block a user