This commit is contained in:
Baobhan Sith
2025-04-24 20:59:05 +08:00
parent e3139457ff
commit 2ca01ffd04
6 changed files with 33 additions and 23 deletions
@@ -0,0 +1 @@
{"cookie":{"secure":false,"httpOnly":true,"path":"/"},"userId":1,"username":"admin","requiresTwoFactor":false,"__lastAccess":1745499534213}
+1 -18
View File
@@ -9,27 +9,10 @@ services:
ports: ports:
- "3001:3001" # 将容器的 3001 端口映射到宿主机的 3001 端口 - "3001:3001" # 将容器的 3001 端口映射到宿主机的 3001 端口
environment: 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 NODE_ENV: production
PORT: 3001 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: volumes:
# 如果后端需要持久化数据(例如 SQLite 数据库),则挂载卷
# 将宿主机上的 ./data 目录映射到容器内的 /app/data 目录
- ./data:/app/data # 确保宿主机上的 ./data 目录存在或 Docker 会创建它 - ./data:/app/data # 确保宿主机上的 ./data 目录存在或 Docker 会创建它
networks: networks:
- nexus_network # 将服务连接到自定义网络 - nexus_network # 将服务连接到自定义网络
+6 -2
View File
@@ -143,7 +143,8 @@ const initializeDatabase = async () => {
const startServer = () => { const startServer = () => {
// --- 会话中间件配置 --- // --- 会话中间件配置 ---
const FileStore = sessionFileStore(session); 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)) { if (!fs.existsSync(sessionsPath)) {
fs.mkdirSync(sessionsPath, { recursive: true }); fs.mkdirSync(sessionsPath, { recursive: true });
} }
@@ -157,9 +158,12 @@ const startServer = () => {
secret: process.env.SESSION_SECRET as string, secret: process.env.SESSION_SECRET as string,
resave: false, resave: false,
saveUninitialized: false, saveUninitialized: false,
proxy: true, // 信任反向代理设置的 X-Forwarded-Proto 头
cookie: { cookie: {
httpOnly: true, 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: 默认会话期 // maxAge: 默认会话期
} }
}); });
@@ -250,15 +250,21 @@ export const deleteTheme = async (id: number): Promise<boolean> => {
*/ */
export const initializePresetThemes = async (db: Database, presets: Array<Omit<TerminalTheme, '_id' | 'createdAt' | 'updatedAt' | 'isSystemDefault'> & { name: string }>) => { export const initializePresetThemes = async (db: Database, presets: Array<Omit<TerminalTheme, '_id' | 'createdAt' | 'updatedAt' | 'isSystemDefault'> & { name: string }>) => {
console.log('[DB Init] 开始检查并初始化预设主题...'); console.log('[DB Init] 开始检查并初始化预设主题...');
// 在这里添加日志,显示总共要处理多少个预设主题
console.log(`[DB Init] 发现 ${presets.length} 个预设主题定义。`);
const nowSeconds = Math.floor(Date.now() / 1000); // Use seconds for DB consistency const nowSeconds = Math.floor(Date.now() / 1000); // Use seconds for DB consistency
// const db = await getDbInstance(); // Use the passed db instance // const db = await getDbInstance(); // Use the passed db instance
for (const preset of presets) { for (const preset of presets) {
// 在循环开始时添加日志,显示正在处理哪个主题
console.log(`[DB Init] 正在处理预设主题: "${preset.name}"`);
try { try {
// Check using name and theme_type // 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]); const existing = await getDb<{ id: number }>(db, `SELECT id FROM terminal_themes WHERE name = ? AND theme_type = 'preset'`, [preset.name]);
if (!existing) { if (!existing) {
// 添加日志,表明正在插入新主题
console.log(`[DB Init] 主题 "${preset.name}" 不存在,正在插入...`);
// Map preset.themeData to individual columns // Map preset.themeData to individual columns
const theme = preset.themeData; const theme = preset.themeData;
const columns = [ const columns = [
@@ -284,7 +290,8 @@ export const initializePresetThemes = async (db: Database, presets: Array<Omit<T
await runDb(db, insertSql, values); await runDb(db, insertSql, values);
console.log(`[DB Init] 预设主题 "${preset.name}" 已初始化到数据库。`); console.log(`[DB Init] 预设主题 "${preset.name}" 已初始化到数据库。`);
} else { } else {
// console.log(`[DB Init] 预设主题 "${preset.name}" 已存在,跳过初始化。`); // 取消注释并添加日志,表明主题已存在
console.log(`[DB Init] 预设主题 "${preset.name}" 已存在,跳过初始化。`);
} }
} catch (err: any) { // Add type annotation for err } catch (err: any) { // Add type annotation for err
// Remove reference to non-existent preset_key // Remove reference to non-existent preset_key
+2 -2
View File
@@ -13,8 +13,8 @@ server {
# Proxy API requests to the backend service # Proxy API requests to the backend service
location /api/ { location /api/ {
rewrite ^/api(/.*)$ $1 break; # Remove /api prefix before proxying # rewrite ^/api(/.*)$ $1 break; # Remove /api prefix before proxying - REMOVED to match backend routes
proxy_pass http://backend:3001; # Proxy to backend root proxy_pass http://backend:3001; # Proxy to backend root, backend expects /api/v1/...
# Standard proxy headers # Standard proxy headers
proxy_set_header Host $host; proxy_set_header Host $host;
@@ -255,6 +255,21 @@ export const useLayoutStore = defineStore('layout', () => {
sidebarPanes.value = getDefaultSidebarPanes(); 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 --- // --- Helper for debounced persistence ---