diff --git a/data/sessions/LM6PBrGjZYyCLnNhv-LtxLYUZVCJEGqG.json b/data/sessions/LM6PBrGjZYyCLnNhv-LtxLYUZVCJEGqG.json new file mode 100644 index 0000000..a6098fc --- /dev/null +++ b/data/sessions/LM6PBrGjZYyCLnNhv-LtxLYUZVCJEGqG.json @@ -0,0 +1 @@ +{"cookie":{"secure":false,"httpOnly":true,"path":"/"},"userId":1,"username":"admin","requiresTwoFactor":false,"__lastAccess":1745499534213} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 0a66d55..d41c350 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,27 +9,10 @@ services: ports: - "3001:3001" # 将容器的 3001 端口映射到宿主机的 3001 端口 environment: - # 这里需要添加后端运行所需的环境变量 - # 例如: - # NODE_ENV: production - # PORT: 3001 # 确保容器内端口与 EXPOSE 一致 - # DATABASE_PATH: /app/data/database.sqlite # 如果使用 SQLite - # ENCRYPTION_KEY: your_strong_encryption_key # 需要设置强密钥 - # SESSION_SECRET: your_strong_session_secret # 需要设置强密钥 - # JWT_SECRET: your_strong_jwt_secret # 如果使用 JWT - # ... 其他后端需要的环境变量 - # 注意:敏感信息最好通过 .env 文件或 Docker secrets 管理 NODE_ENV: production PORT: 3001 - # 示例:如果数据库文件在容器内的 /app/data 目录下 - DATABASE_PATH: /app/data/database.sqlite - # !! 请务必替换下面的示例密钥为强随机值 !! - ENCRYPTION_KEY: replace_with_strong_random_key_32_bytes - SESSION_SECRET: replace_with_strong_random_secret - JWT_SECRET: replace_with_strong_random_jwt_secret + volumes: - # 如果后端需要持久化数据(例如 SQLite 数据库),则挂载卷 - # 将宿主机上的 ./data 目录映射到容器内的 /app/data 目录 - ./data:/app/data # 确保宿主机上的 ./data 目录存在或 Docker 会创建它 networks: - nexus_network # 将服务连接到自定义网络 diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 9e95335..a2933d0 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -143,7 +143,8 @@ const initializeDatabase = async () => { const startServer = () => { // --- 会话中间件配置 --- const FileStore = sessionFileStore(session); - const sessionsPath = path.join(__dirname, '..', 'sessions'); + // 修改路径以匹配 Docker volume 挂载点 /app/data + const sessionsPath = path.join('/app/data', 'sessions'); if (!fs.existsSync(sessionsPath)) { fs.mkdirSync(sessionsPath, { recursive: true }); } @@ -157,9 +158,12 @@ const startServer = () => { secret: process.env.SESSION_SECRET as string, resave: false, saveUninitialized: false, + proxy: true, // 信任反向代理设置的 X-Forwarded-Proto 头 cookie: { httpOnly: true, - secure: process.env.NODE_ENV === 'production' + // secure: 'auto' in newer versions, relies on proxy: true here + // secure: process.env.NODE_ENV === 'production', // Keep original logic, but proxy:true adjusts behavior + secure: false, // Temporarily force secure to false for debugging proxy issues // maxAge: 默认会话期 } }); diff --git a/packages/backend/src/repositories/terminal-theme.repository.ts b/packages/backend/src/repositories/terminal-theme.repository.ts index 6126e9a..32f299f 100644 --- a/packages/backend/src/repositories/terminal-theme.repository.ts +++ b/packages/backend/src/repositories/terminal-theme.repository.ts @@ -250,15 +250,21 @@ export const deleteTheme = async (id: number): Promise => { */ export const initializePresetThemes = async (db: Database, presets: Array & { name: string }>) => { console.log('[DB Init] 开始检查并初始化预设主题...'); + // 在这里添加日志,显示总共要处理多少个预设主题 + console.log(`[DB Init] 发现 ${presets.length} 个预设主题定义。`); const nowSeconds = Math.floor(Date.now() / 1000); // Use seconds for DB consistency // const db = await getDbInstance(); // Use the passed db instance for (const preset of presets) { + // 在循环开始时添加日志,显示正在处理哪个主题 + console.log(`[DB Init] 正在处理预设主题: "${preset.name}"`); try { // Check using name and theme_type const existing = await getDb<{ id: number }>(db, `SELECT id FROM terminal_themes WHERE name = ? AND theme_type = 'preset'`, [preset.name]); if (!existing) { + // 添加日志,表明正在插入新主题 + console.log(`[DB Init] 主题 "${preset.name}" 不存在,正在插入...`); // Map preset.themeData to individual columns const theme = preset.themeData; const columns = [ @@ -284,7 +290,8 @@ export const initializePresetThemes = async (db: Database, presets: Array { sidebarPanes.value = getDefaultSidebarPanes(); } } + + // --- Final Check: Ensure defaults are applied if loading failed or resulted in null --- + if (!layoutTree.value) { + console.warn('[Layout Store] Layout tree is still null after all loading attempts. Applying default layout.'); + layoutTree.value = getDefaultLayout(); + // Optionally save the default to localStorage now? + // try { localStorage.setItem(LAYOUT_STORAGE_KEY, JSON.stringify(layoutTree.value)); } catch(e) {} + } + // Basic check for sidebarPanes structure validity before potentially applying default + if (!sidebarPanes.value || !Array.isArray(sidebarPanes.value.left) || !Array.isArray(sidebarPanes.value.right)) { + console.warn('[Layout Store] Sidebar panes are null or invalid after all loading attempts. Applying default sidebar panes.'); + sidebarPanes.value = getDefaultSidebarPanes(); + // Optionally save the default to localStorage now? + // try { localStorage.setItem(SIDEBAR_STORAGE_KEY, JSON.stringify(sidebarPanes.value)); } catch(e) {} + } } // --- Helper for debounced persistence ---