remove HomeDirSelector, use HOME_DIR for super admin file browser root
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,6 @@ import { createRouter } from '@@/create-router';
|
|||||||
import { resolve, dirname, join, parse as parsePath } from 'node:path';
|
import { resolve, dirname, join, parse as parsePath } from 'node:path';
|
||||||
import { readdir, stat, mkdir, rm, rename, readFile, cp, unlink } from 'node:fs/promises';
|
import { readdir, stat, mkdir, rm, rename, readFile, cp, unlink } from 'node:fs/promises';
|
||||||
import { existsSync } from 'node:fs';
|
import { existsSync } from 'node:fs';
|
||||||
import { homedir } from 'node:os';
|
|
||||||
import { getHomeDir, DATA_PATH, getUserSettingsFile } from '@@/data-path';
|
import { getHomeDir, DATA_PATH, getUserSettingsFile } from '@@/data-path';
|
||||||
import * as errors from '@@/custom-errors';
|
import * as errors from '@@/custom-errors';
|
||||||
import { readTtsConfig } from '@@/api/server-settings/tts';
|
import { readTtsConfig } from '@@/api/server-settings/tts';
|
||||||
@@ -64,11 +63,11 @@ function getUserDataDir(email: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRootDir(user: UserCtx, root?: string): string {
|
function getRootDir(user: UserCtx, root?: string): string {
|
||||||
if (!root || root === 'home') return getHomeDir(user.email);
|
if (!root || root === 'home') {
|
||||||
|
if (user.role === 'Super Admin' && process.env.HOME_DIR) return process.env.HOME_DIR;
|
||||||
|
return getHomeDir(user.email);
|
||||||
|
}
|
||||||
if (root === 'user-data') return getUserDataDir(user.email);
|
if (root === 'user-data') return getUserDataDir(user.email);
|
||||||
if (user.role !== 'Super Admin') throw errors.FORBIDDEN('Only Super Admin can access this root');
|
|
||||||
if (root === '~') return homedir();
|
|
||||||
if (root === 'officer.dev') return resolve(process.cwd(), '..');
|
|
||||||
throw errors.BAD_REQUEST(`Invalid root: ${root}`);
|
throw errors.BAD_REQUEST(`Invalid root: ${root}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +373,7 @@ router.post('/save-result', async (ctx) => {
|
|||||||
const srcAbs = resolve(userDataDir, cachedPath);
|
const srcAbs = resolve(userDataDir, cachedPath);
|
||||||
if (!existsSync(srcAbs)) throw errors.BAD_REQUEST('Cached file not found');
|
if (!existsSync(srcAbs)) throw errors.BAD_REQUEST('Cached file not found');
|
||||||
|
|
||||||
const homeDir = getHomeDir(user.email);
|
const homeDir = getRootDir(user, 'home');
|
||||||
const destAbs = resolve(homeDir, relativePath);
|
const destAbs = resolve(homeDir, relativePath);
|
||||||
if (!destAbs.startsWith(homeDir)) throw errors.FORBIDDEN('Path outside home directory');
|
if (!destAbs.startsWith(homeDir)) throw errors.FORBIDDEN('Path outside home directory');
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Breadcrumb } from './components/Breadcrumb';
|
import { Breadcrumb } from './components/Breadcrumb';
|
||||||
import { Toolbar } from './components/Toolbar';
|
import { Toolbar } from './components/Toolbar';
|
||||||
import { HomeDirSelector } from './components/HomeDirSelector';
|
|
||||||
import { UploadProgress } from './components/UploadProgress';
|
import { UploadProgress } from './components/UploadProgress';
|
||||||
import { FileViewContainer } from './components/FileViewContainer';
|
import { FileViewContainer } from './components/FileViewContainer';
|
||||||
import { TaskRunnerDialog } from './components/TaskRunnerDialog';
|
import { TaskRunnerDialog } from './components/TaskRunnerDialog';
|
||||||
@@ -26,7 +25,6 @@ export const FileBrowserApp = ({ basePath = '/', rootOverride, initialPath, defa
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full overflow-hidden">
|
<div className="flex flex-col h-full overflow-hidden">
|
||||||
<Toolbar fileBrowserManager={fileBrowserManager} />
|
<Toolbar fileBrowserManager={fileBrowserManager} />
|
||||||
<HomeDirSelector fileBrowserManager={fileBrowserManager} basePath={basePath} />
|
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
path={fileBrowserManager.currentPath}
|
path={fileBrowserManager.currentPath}
|
||||||
onNavigate={handleNavigate} basePath={basePath} />
|
onNavigate={handleNavigate} basePath={basePath} />
|
||||||
|
|||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
import { Home, FolderRoot, Code } from 'lucide-react';
|
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
|
||||||
import type { UseFileBrowserAppType } from '../useFileBrowserApp';
|
|
||||||
|
|
||||||
type HomeDirSelectorProps = {
|
|
||||||
fileBrowserManager: UseFileBrowserAppType;
|
|
||||||
basePath: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const HomeDirSelector = ({ fileBrowserManager, basePath }: HomeDirSelectorProps) => {
|
|
||||||
const { user, homeRoot, setHomeRoot, currentPath, setCurrentPath } = fileBrowserManager;
|
|
||||||
|
|
||||||
if (basePath !== '/' || user?.role !== 'Super Admin') return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="shrink-0 border-b border-duck-dark/10 px-4 py-2">
|
|
||||||
<RadioGroup
|
|
||||||
value={homeRoot}
|
|
||||||
onValueChange={(v) => {
|
|
||||||
setHomeRoot(v as 'home' | '~' | 'officer.dev');
|
|
||||||
if (currentPath !== '/') setCurrentPath('/');
|
|
||||||
}}
|
|
||||||
className="flex items-center gap-4"
|
|
||||||
>
|
|
||||||
<label className="flex items-center gap-1.5 cursor-pointer text-sm text-duck-dark/70">
|
|
||||||
<RadioGroupItem value="home" />
|
|
||||||
<Home className="h-3.5 w-3.5" />
|
|
||||||
Home dir
|
|
||||||
</label>
|
|
||||||
<label className="flex items-center gap-1.5 cursor-pointer text-sm text-duck-dark/70">
|
|
||||||
<RadioGroupItem value="~" />
|
|
||||||
<FolderRoot className="h-3.5 w-3.5" />~
|
|
||||||
</label>
|
|
||||||
<label className="flex items-center gap-1.5 cursor-pointer text-sm text-duck-dark/70">
|
|
||||||
<RadioGroupItem value="officer.dev" />
|
|
||||||
<Code className="h-3.5 w-3.5" />
|
|
||||||
officer.dev
|
|
||||||
</label>
|
|
||||||
</RadioGroup>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+2
-2
@@ -6,7 +6,7 @@ type TaskRunnerDialogProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const TaskRunnerDialog = ({ fileBrowserManager }: TaskRunnerDialogProps) => {
|
export const TaskRunnerDialog = ({ fileBrowserManager }: TaskRunnerDialogProps) => {
|
||||||
const { runningTask, setRunningTask, refresh, homeRoot, currentPath, getEntryAbsPath } = fileBrowserManager;
|
const { runningTask, setRunningTask, refresh, currentPath, getEntryAbsPath } = fileBrowserManager;
|
||||||
|
|
||||||
if (!runningTask) return null;
|
if (!runningTask) return null;
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export const TaskRunnerDialog = ({ fileBrowserManager }: TaskRunnerDialogProps)
|
|||||||
entryName={runningTask.entry.name}
|
entryName={runningTask.entry.name}
|
||||||
entryFullPath={getEntryAbsPath(runningTask.entry.name)}
|
entryFullPath={getEntryAbsPath(runningTask.entry.name)}
|
||||||
entryType={runningTask.entry.type}
|
entryType={runningTask.entry.type}
|
||||||
cwd={{ root: homeRoot, path: currentPath.replace(/^\//, '') }}
|
cwd={{ root: 'home', path: currentPath.replace(/^\//, '') }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const useFileBrowserApp = (basePath: string, rootOverride?: string, initi
|
|||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const [homeRoot, setHomeRoot] = useUserState<HomeRoot>('files/homeRoot', 'home');
|
const homeRoot = 'home';
|
||||||
const [globalPath, setGlobalPath] = useUserState<string>('files/currentPath', '/');
|
const [globalPath, setGlobalPath] = useUserState<string>('files/currentPath', '/');
|
||||||
const [localPath, setLocalPath] = useState(initialPath ?? basePath);
|
const [localPath, setLocalPath] = useState(initialPath ?? basePath);
|
||||||
const scoped = basePath !== '/';
|
const scoped = basePath !== '/';
|
||||||
@@ -655,7 +655,7 @@ export const useFileBrowserApp = (basePath: string, rootOverride?: string, initi
|
|||||||
// Auth
|
// Auth
|
||||||
user,
|
user,
|
||||||
// Navigation
|
// Navigation
|
||||||
homeRoot, setHomeRoot,
|
homeRoot,
|
||||||
currentPath, setCurrentPath,
|
currentPath, setCurrentPath,
|
||||||
// Directory listing
|
// Directory listing
|
||||||
visibleEntries, loading, refresh,
|
visibleEntries, loading, refresh,
|
||||||
@@ -731,4 +731,3 @@ export type UseFileBrowserAppType = ReturnType<typeof useFileBrowserApp>;
|
|||||||
|
|
||||||
type ClipboardState = { paths: string[]; mode: 'copy' | 'cut' } | null;
|
type ClipboardState = { paths: string[]; mode: 'copy' | 'cut' } | null;
|
||||||
|
|
||||||
type HomeRoot = 'home' | '~' | 'officer.dev';
|
|
||||||
|
|||||||
Reference in New Issue
Block a user