feat: remove old vanilla JS/CSS frontend, make React SPA the sole entry for ccw view

Remove the entire old template-based frontend (~106K lines) and make the React
SPA the only way to access the ccw dashboard via `ccw view`.

Key changes:
- Delete all old frontend files: dashboard-css/ (37 CSS), dashboard-js/ (59 JS),
  assets/, dashboard.html, and legacy HTML templates
- Delete dashboard-generator.ts and dashboard-generator-patch.ts
- Simplify server.ts: remove ~234 lines of old frontend code (template constants,
  MODULE_CSS_FILES/MODULE_FILES arrays, generateServerDashboard(), /assets/* serving)
- Rebase React frontend from /react/ to root / (vite.config.ts, react-frontend.ts)
- Add /react/* -> /* 301 redirect for backward compatibility
- Remove --frontend and --new CLI flags from view and serve commands
- Remove generateDashboard export from public API (index.ts)
- Simplify serve.ts and view.ts to always use React without conditional branching
- Update all affected tests (unit, e2e) for React-only architecture

BREAKING CHANGE: --frontend and --new CLI flags removed; generateDashboard export
removed from ccw package; /react/ base path changed to /
This commit is contained in:
catlog22
2026-02-13 17:26:03 +08:00
parent 31f37751fc
commit bcb736709f
136 changed files with 204 additions and 115952 deletions

View File

@@ -14,14 +14,14 @@ import { constants } from 'fs';
// Environment variable configuration
const ENV_PROJECT_ROOT = 'CCW_PROJECT_ROOT';
const ENV_ALLOWED_DIRS = 'CCW_ALLOWED_DIRS';
const ENV_DISABLE_SANDBOX = 'CCW_DISABLE_SANDBOX';
const ENV_ENABLE_SANDBOX = 'CCW_ENABLE_SANDBOX';
/**
* Check if sandbox mode is disabled
* When disabled, path validation allows access to any directory
* Check if sandbox mode is enabled
* When enabled, path validation restricts access to allowed directories only
*/
export function isSandboxDisabled(): boolean {
const value = process.env[ENV_DISABLE_SANDBOX];
export function isSandboxEnabled(): boolean {
const value = process.env[ENV_ENABLE_SANDBOX];
return value === '1' || value?.toLowerCase() === 'true';
}
@@ -110,7 +110,7 @@ export async function validatePath(
mustExist?: boolean;
} = {}
): Promise<string> {
const sandboxDisabled = isSandboxDisabled();
const sandboxEnabled = isSandboxEnabled();
const allowedDirs = options.allowedDirectories || getAllowedDirectories();
// 1. Resolve to absolute path
@@ -119,8 +119,8 @@ export async function validatePath(
: resolve(getProjectRoot(), filePath);
const normalizedPath = normalizePath(absolutePath);
// 2. Initial sandbox check (skip if sandbox is disabled)
if (!sandboxDisabled && !isPathWithinAllowedDirectories(normalizedPath, allowedDirs)) {
// 2. Initial sandbox check (only if sandbox is enabled)
if (sandboxEnabled && !isPathWithinAllowedDirectories(normalizedPath, allowedDirs)) {
throw new Error(
`Access denied: path "${normalizedPath}" is outside allowed directories. ` +
`Allowed: [${allowedDirs.join(', ')}]`
@@ -132,8 +132,8 @@ export async function validatePath(
const realPath = await realpath(absolutePath);
const normalizedReal = normalizePath(realPath);
// Skip sandbox check for symlink target if sandbox is disabled
if (!sandboxDisabled && !isPathWithinAllowedDirectories(normalizedReal, allowedDirs)) {
// Only check symlink target if sandbox is enabled
if (sandboxEnabled && !isPathWithinAllowedDirectories(normalizedReal, allowedDirs)) {
throw new Error(
`Access denied: symlink target "${normalizedReal}" is outside allowed directories`
);
@@ -147,13 +147,13 @@ export async function validatePath(
throw new Error(`File not found: ${absolutePath}`);
}
// Validate parent directory's real path (skip if sandbox is disabled)
// Validate parent directory's real path (only if sandbox is enabled)
const parentDir = resolve(absolutePath, '..');
try {
const realParent = await realpath(parentDir);
const normalizedParent = normalizePath(realParent);
if (!sandboxDisabled && !isPathWithinAllowedDirectories(normalizedParent, allowedDirs)) {
if (sandboxEnabled && !isPathWithinAllowedDirectories(normalizedParent, allowedDirs)) {
throw new Error(
`Access denied: parent directory "${normalizedParent}" is outside allowed directories`
);

View File

@@ -78,7 +78,7 @@ export async function startReactFrontend(port: number): Promise<void> {
env: {
...process.env,
BROWSER: 'none', // Prevent React from auto-opening browser
VITE_BASE_URL: '/react/', // Set base URL for React frontend
VITE_BASE_URL: '/', // Set base URL for React frontend (root path)
}
});