From 03e876dae9b731523a8686ae85dc99ccbcc7c1b8 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Mon, 22 Sep 2025 01:31:54 +0800 Subject: [PATCH] Clean Chinese comments from backend codebase Replace all Chinese comments with English equivalents while preserving: - Technical meaning and Linus-style direct tone - Code structure and functionality - User-facing text in UI components Backend files cleaned: - All utils/ TypeScript files - Database routes and operations - System architecture comments - Field encryption documentation All backend code now uses consistent English comments. --- src/backend/database/database.ts | 18 +++--- src/backend/database/routes/users.ts | 18 +++--- src/backend/starter.ts | 10 ++-- src/backend/utils/auth-manager.ts | 34 +++++------ src/backend/utils/data-crypto.ts | 26 ++++----- src/backend/utils/field-crypto.ts | 20 +++---- src/backend/utils/simple-db-ops.ts | 58 +++++++++--------- src/backend/utils/system-crypto.ts | 68 +++++++++++----------- src/backend/utils/user-crypto.ts | 84 +++++++++++++-------------- src/backend/utils/user-data-export.ts | 36 ++++++------ src/backend/utils/user-data-import.ts | 60 +++++++++---------- 11 files changed, 216 insertions(+), 216 deletions(-) diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts index 0c62801b..9fe42563 100644 --- a/src/backend/database/database.ts +++ b/src/backend/database/database.ts @@ -412,7 +412,7 @@ app.post("/database/export", async (req, res) => { const userId = payload.userId; const { format = 'encrypted', scope = 'user_data', includeCredentials = true, password } = req.body; - // 对于明文导出,需要解锁用户数据 + // For plaintext export, need to unlock user data if (format === 'plaintext') { if (!password) { return res.status(400).json({ @@ -441,7 +441,7 @@ app.post("/database/export", async (req, res) => { includeCredentials, }); - // 生成导出文件名 + // Generate export filename const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); const filename = `termix-export-${exportData.username}-${timestamp}.json`; @@ -507,10 +507,10 @@ app.post("/database/import", upload.single("file"), async (req, res) => { dryRun, }); - // 读取上传的文件 + // Read uploaded file const fileContent = fs.readFileSync(req.file.path, 'utf8'); - // 清理上传的临时文件 + // Clean up uploaded temporary file try { fs.unlinkSync(req.file.path); } catch (cleanupError) { @@ -520,7 +520,7 @@ app.post("/database/import", upload.single("file"), async (req, res) => { }); } - // 解析导入数据 + // Parse import data let importData; try { importData = JSON.parse(fileContent); @@ -528,7 +528,7 @@ app.post("/database/import", upload.single("file"), async (req, res) => { return res.status(400).json({ error: "Invalid JSON format in uploaded file" }); } - // 如果导入数据是加密的,需要解锁用户数据 + // If import data is encrypted, need to unlock user data if (importData.metadata?.encrypted) { if (!password) { return res.status(400).json({ @@ -543,7 +543,7 @@ app.post("/database/import", upload.single("file"), async (req, res) => { } } - // 执行导入 + // Execute import const result = await UserDataImport.importUserData(userId, importData, { replaceExisting: replaceExisting === 'true' || replaceExisting === true, skipCredentials: skipCredentials === 'true' || skipCredentials === true, @@ -619,9 +619,9 @@ app.post("/database/export/preview", async (req, res) => { includeCredentials, }); - // 生成导出数据但不解密敏感字段 + // Generate export data but don't decrypt sensitive fields const exportData = await UserDataExport.exportUserData(userId, { - format: 'encrypted', // 始终加密预览 + format: 'encrypted', // Always encrypt preview scope, includeCredentials, }); diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index db76fa20..1c3655c1 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -336,10 +336,10 @@ router.post("/oidc-config", authenticateJWT, async (req, res) => { scopes: scopes || "openid email profile", }; - // 对敏感配置进行加密存储 + // Encrypt sensitive configuration for storage let encryptedConfig; try { - // 使用管理员的数据密钥加密OIDC配置 + // Use admin's data key to encrypt OIDC configuration const adminDataKey = DataCrypto.getUserDataKey(userId); if (adminDataKey) { encryptedConfig = DataCrypto.encryptRecord("settings", config, userId, adminDataKey); @@ -348,10 +348,10 @@ router.post("/oidc-config", authenticateJWT, async (req, res) => { userId, }); } else { - // 如果管理员数据未解锁,只加密client_secret + // If admin data not unlocked, only encrypt client_secret encryptedConfig = { ...config, - client_secret: `encrypted:${Buffer.from(client_secret).toString('base64')}`, // 简单的base64编码 + client_secret: `encrypted:${Buffer.from(client_secret).toString('base64')}`, // Simple base64 encoding }; authLogger.warn("OIDC configuration stored with basic encoding - admin should re-save with password", { operation: "oidc_config_basic_encoding", @@ -422,10 +422,10 @@ router.get("/oidc-config", async (req, res) => { let config = JSON.parse((row as any).value); - // 解密或解码client_secret用于显示 + // Decrypt or decode client_secret for display if (config.client_secret) { if (config.client_secret.startsWith('encrypted:')) { - // 需要管理员权限解密 + // Requires admin permission to decrypt const authHeader = req.headers["authorization"]; if (authHeader?.startsWith("Bearer ")) { const token = authHeader.split(" ")[1]; @@ -442,7 +442,7 @@ router.get("/oidc-config", async (req, res) => { if (adminDataKey) { config = DataCrypto.decryptRecord("settings", config, userId, adminDataKey); } else { - // 管理员数据未解锁,隐藏client_secret + // Admin data not unlocked, hide client_secret config.client_secret = "[ENCRYPTED - PASSWORD REQUIRED]"; } } catch (decryptError) { @@ -462,7 +462,7 @@ router.get("/oidc-config", async (req, res) => { config.client_secret = "[ENCRYPTED - AUTH REQUIRED]"; } } else if (config.client_secret.startsWith('encoded:')) { - // base64解码 + // base64 decode try { const decoded = Buffer.from(config.client_secret.substring(8), 'base64').toString('utf8'); config.client_secret = decoded; @@ -470,7 +470,7 @@ router.get("/oidc-config", async (req, res) => { config.client_secret = "[ENCODING ERROR]"; } } - // 否则是明文,直接返回 + // Otherwise plaintext, return directly } res.json(config); diff --git a/src/backend/starter.ts b/src/backend/starter.ts index 39c9c790..8169f923 100644 --- a/src/backend/starter.ts +++ b/src/backend/starter.ts @@ -15,7 +15,7 @@ import "dotenv/config"; version: version, }); - // 生产环境安全检查 + // Production environment security checks if (process.env.NODE_ENV === 'production') { systemLogger.info("Running production environment security checks...", { operation: "security_checks", @@ -23,19 +23,19 @@ import "dotenv/config"; const securityIssues: string[] = []; - // 检查系统主密钥 + // Check system master key if (!process.env.SYSTEM_MASTER_KEY) { securityIssues.push("SYSTEM_MASTER_KEY environment variable is required in production"); } else if (process.env.SYSTEM_MASTER_KEY.length < 64) { securityIssues.push("SYSTEM_MASTER_KEY should be at least 64 characters in production"); } - // 检查数据库文件加密 + // Check database file encryption if (process.env.DB_FILE_ENCRYPTION === 'false') { securityIssues.push("Database file encryption should be enabled in production"); } - // 检查JWT移密 + // Check JWT secret if (!process.env.JWT_SECRET) { systemLogger.info("JWT_SECRET not set - will use encrypted storage", { operation: "security_checks", @@ -43,7 +43,7 @@ import "dotenv/config"; }); } - // 检查CORS配置警告 + // Check CORS configuration warning systemLogger.warn("Production deployment detected - ensure CORS is properly configured", { operation: "security_checks", warning: "Verify frontend domain whitelist" diff --git a/src/backend/utils/auth-manager.ts b/src/backend/utils/auth-manager.ts index d5ffafba..cbb0c995 100644 --- a/src/backend/utils/auth-manager.ts +++ b/src/backend/utils/auth-manager.ts @@ -23,14 +23,14 @@ interface JWTPayload { } /** - * AuthManager - 简化的认证管理器 + * AuthManager - Simplified authentication manager * - * 职责: - * - JWT生成和验证 - * - 认证中间件 - * - 用户登录登出 + * Responsibilities: + * - JWT generation and validation + * - Authentication middleware + * - User login/logout * - * 不再有两层session - 直接使用UserKeyManager + * No more two-layer sessions - use UserKeyManager directly */ class AuthManager { private static instance: AuthManager; @@ -50,7 +50,7 @@ class AuthManager { } /** - * 初始化认证系统 + * Initialize authentication system */ async initialize(): Promise { await this.systemCrypto.initializeJWTSecret(); @@ -60,21 +60,21 @@ class AuthManager { } /** - * 用户注册 + * User registration */ async registerUser(userId: string, password: string): Promise { await this.userCrypto.setupUserEncryption(userId, password); } /** - * 用户登录 - 使用UserCrypto + * User login - use UserCrypto */ async authenticateUser(userId: string, password: string): Promise { return await this.userCrypto.authenticateUser(userId, password); } /** - * 生成JWT Token + * Generate JWT Token */ async generateJWTToken( userId: string, @@ -93,7 +93,7 @@ class AuthManager { } /** - * 验证JWT Token + * Verify JWT Token */ async verifyJWTToken(token: string): Promise { try { @@ -105,7 +105,7 @@ class AuthManager { } /** - * 认证中间件 + * Authentication middleware */ createAuthMiddleware() { return async (req: Request, res: Response, next: NextFunction) => { @@ -128,7 +128,7 @@ class AuthManager { } /** - * 数据访问中间件 - 要求用户已解锁数据 + * Data access middleware - requires user to have unlocked data */ createDataAccessMiddleware() { return async (req: Request, res: Response, next: NextFunction) => { @@ -151,28 +151,28 @@ class AuthManager { } /** - * 用户登出 + * User logout */ logoutUser(userId: string): void { this.userCrypto.logoutUser(userId); } /** - * 获取用户数据密钥 + * Get user data key */ getUserDataKey(userId: string): Buffer | null { return this.userCrypto.getUserDataKey(userId); } /** - * 检查用户是否已解锁 + * Check if user is unlocked */ isUserUnlocked(userId: string): boolean { return this.userCrypto.isUserUnlocked(userId); } /** - * 修改用户密码 + * Change user password */ async changeUserPassword(userId: string, oldPassword: string, newPassword: string): Promise { return await this.userCrypto.changeUserPassword(userId, oldPassword, newPassword); diff --git a/src/backend/utils/data-crypto.ts b/src/backend/utils/data-crypto.ts index 153ff47c..0ad495ef 100644 --- a/src/backend/utils/data-crypto.ts +++ b/src/backend/utils/data-crypto.ts @@ -3,13 +3,13 @@ import { UserCrypto } from "./user-crypto.js"; import { databaseLogger } from "./logger.js"; /** - * DataCrypto - 简化的数据库加密 + * DataCrypto - Simplified database encryption * - * Linus原则: - * - 删除所有"向后兼容"垃圾 - * - 删除所有特殊情况处理 - * - 数据要么正确加密,要么操作失败 - * - 没有legacy data概念 + * Linus principles: + * - Remove all "backward compatibility" garbage + * - Remove all special case handling + * - Data is either properly encrypted or operation fails + * - No legacy data concept */ class DataCrypto { private static userCrypto: UserCrypto; @@ -43,12 +43,12 @@ class DataCrypto { } /** - * 解密记录 - 要么成功,要么失败 + * Decrypt record - either succeeds or fails * - * 删除了所有的: - * - isEncrypted()检查 - * - legacy data处理 - * - "向后兼容"逻辑 + * Removed all: + * - isEncrypted() checks + * - legacy data handling + * - "backward compatibility" logic * - migration on access */ static decryptRecord(tableName: string, record: any, userId: string, userDataKey: Buffer): any { @@ -59,8 +59,8 @@ class DataCrypto { for (const [fieldName, value] of Object.entries(record)) { if (FieldCrypto.shouldEncryptField(tableName, fieldName) && value) { - // 简单规则:敏感字段必须是加密的JSON格式 - // 如果不是,就是数据损坏,直接失败 + // Simple rule: sensitive fields must be encrypted JSON format + // If not, it's data corruption, fail directly decryptedRecord[fieldName] = FieldCrypto.decryptField( value as string, userDataKey, diff --git a/src/backend/utils/field-crypto.ts b/src/backend/utils/field-crypto.ts index baa58694..f6c956d3 100644 --- a/src/backend/utils/field-crypto.ts +++ b/src/backend/utils/field-crypto.ts @@ -8,13 +8,13 @@ interface EncryptedData { } /** - * FieldCrypto - 简单直接的字段加密 + * FieldCrypto - Simple direct field encryption * - * Linus原则: - * - 没有特殊情况 - * - 没有兼容性检查 - * - 数据要么加密,要么失败 - * - 不存在"legacy data"概念 + * Linus principles: + * - No special cases + * - No compatibility checks + * - Data is either encrypted or fails + * - No "legacy data" concept */ class FieldCrypto { private static readonly ALGORITHM = "aes-256-gcm"; @@ -22,7 +22,7 @@ class FieldCrypto { private static readonly IV_LENGTH = 16; private static readonly SALT_LENGTH = 32; - // 需要加密的字段 - 简单的映射,没有复杂逻辑 + // Fields requiring encryption - simple mapping, no complex logic private static readonly ENCRYPTED_FIELDS = { users: new Set(["password_hash", "client_secret", "totp_secret", "totp_backup_codes", "oidc_identifier"]), ssh_data: new Set(["password", "key", "keyPassword"]), @@ -30,7 +30,7 @@ class FieldCrypto { }; /** - * 加密字段 - 没有特殊情况 + * Encrypt field - no special cases */ static encryptField(plaintext: string, masterKey: Buffer, recordId: string, fieldName: string): string { if (!plaintext) return ""; @@ -57,7 +57,7 @@ class FieldCrypto { } /** - * 解密字段 - 要么成功,要么失败,没有第三种情况 + * Decrypt field - either succeeds or fails, no third option */ static decryptField(encryptedValue: string, masterKey: Buffer, recordId: string, fieldName: string): string { if (!encryptedValue) return ""; @@ -77,7 +77,7 @@ class FieldCrypto { } /** - * 检查字段是否需要加密 - 简单查表,没有复杂逻辑 + * Check if field needs encryption - simple table lookup, no complex logic */ static shouldEncryptField(tableName: string, fieldName: string): boolean { const fields = this.ENCRYPTED_FIELDS[tableName as keyof typeof this.ENCRYPTED_FIELDS]; diff --git a/src/backend/utils/simple-db-ops.ts b/src/backend/utils/simple-db-ops.ts index 3c8a9561..91f784ff 100644 --- a/src/backend/utils/simple-db-ops.ts +++ b/src/backend/utils/simple-db-ops.ts @@ -6,17 +6,17 @@ import type { SQLiteTable } from "drizzle-orm/sqlite-core"; type TableName = "users" | "ssh_data" | "ssh_credentials"; /** - * SimpleDBOps - 简化的加密数据库操作 + * SimpleDBOps - Simplified encrypted database operations * - * Linus式简化: - * - 删除所有复杂的抽象层 - * - 直接的CRUD操作 - * - 自动加密/解密 - * - 没有特殊情况处理 + * Linus-style simplification: + * - Remove all complex abstraction layers + * - Direct CRUD operations + * - Automatic encryption/decryption + * - No special case handling */ class SimpleDBOps { /** - * 插入加密记录 + * Insert encrypted record */ static async insert>( table: SQLiteTable, @@ -24,18 +24,18 @@ class SimpleDBOps { data: T, userId: string, ): Promise { - // 验证用户访问权限 + // Verify user access permissions if (!DataCrypto.canUserAccessData(userId)) { throw new Error(`User ${userId} data not unlocked`); } - // 加密数据 + // Encrypt data const encryptedData = DataCrypto.encryptRecordForUser(tableName, data, userId); - // 插入数据库 + // Insert into database const result = await db.insert(table).values(encryptedData).returning(); - // 解密返回结果 + // Decrypt return result const decryptedResult = DataCrypto.decryptRecordForUser( tableName, result[0], @@ -53,22 +53,22 @@ class SimpleDBOps { } /** - * 查询多条记录 + * Query multiple records */ static async select>( query: any, tableName: TableName, userId: string, ): Promise { - // 验证用户访问权限 + // Verify user access permissions if (!DataCrypto.canUserAccessData(userId)) { throw new Error(`User ${userId} data not unlocked`); } - // 执行查询 + // Execute query const results = await query; - // 解密结果 + // Decrypt results const decryptedResults = DataCrypto.decryptRecordsForUser( tableName, results, @@ -86,23 +86,23 @@ class SimpleDBOps { } /** - * 查询单条记录 + * Query single record */ static async selectOne>( query: any, tableName: TableName, userId: string, ): Promise { - // 验证用户访问权限 + // Verify user access permissions if (!DataCrypto.canUserAccessData(userId)) { throw new Error(`User ${userId} data not unlocked`); } - // 执行查询 + // Execute query const result = await query; if (!result) return undefined; - // 解密结果 + // Decrypt results const decryptedResult = DataCrypto.decryptRecordForUser( tableName, result, @@ -120,7 +120,7 @@ class SimpleDBOps { } /** - * 更新记录 + * Update record */ static async update>( table: SQLiteTable, @@ -129,22 +129,22 @@ class SimpleDBOps { data: Partial, userId: string, ): Promise { - // 验证用户访问权限 + // Verify user access permissions if (!DataCrypto.canUserAccessData(userId)) { throw new Error(`User ${userId} data not unlocked`); } - // 加密更新数据 + // Encrypt update data const encryptedData = DataCrypto.encryptRecordForUser(tableName, data, userId); - // 执行更新 + // Execute update const result = await db .update(table) .set(encryptedData) .where(where) .returning(); - // 解密返回数据 + // Decrypt return data const decryptedResults = DataCrypto.decryptRecordsForUser( tableName, result, @@ -162,7 +162,7 @@ class SimpleDBOps { } /** - * 删除记录 + * Delete record */ static async delete( table: SQLiteTable, @@ -183,18 +183,18 @@ class SimpleDBOps { } /** - * 健康检查 + * Health check */ static async healthCheck(userId: string): Promise { return DataCrypto.canUserAccessData(userId); } /** - * 特殊方法:返回加密数据(用于自动启动等场景) - * 不解密,直接返回加密状态的数据 + * Special method: return encrypted data (for auto-start scenarios) + * No decryption, return data in encrypted state directly */ static async selectEncrypted(query: any, tableName: TableName): Promise { - // 直接执行查询,不进行解密 + // Execute query directly, no decryption const results = await query; databaseLogger.debug(`Selected ${results.length} encrypted records from ${tableName}`, { diff --git a/src/backend/utils/system-crypto.ts b/src/backend/utils/system-crypto.ts index 41b00988..54ed6720 100644 --- a/src/backend/utils/system-crypto.ts +++ b/src/backend/utils/system-crypto.ts @@ -7,19 +7,19 @@ import { eq } from "drizzle-orm"; import { databaseLogger } from "./logger.js"; /** - * SystemCrypto - 开源友好的JWT密钥管理 + * SystemCrypto - Open source friendly JWT key management * - * Linus原则: - * - 删除复杂的"系统主密钥"层 - 不解决真实威胁 - * - 删除硬编码默认密钥 - 开源软件的安全灾难 - * - 首次启动自动生成 - 每个实例独立安全 - * - 简单直接,专注真正的安全边界 + * Linus principles: + * - Remove complex "system master key" layer - doesn't solve real threats + * - Remove hardcoded default keys - security disaster for open source software + * - Auto-generate on first startup - each instance independently secure + * - Simple and direct, focus on real security boundaries */ class SystemCrypto { private static instance: SystemCrypto; private jwtSecret: string | null = null; - // 存储路径配置 + // Storage path configuration private static readonly JWT_SECRET_FILE = path.join(process.cwd(), '.termix', 'jwt.key'); private static readonly JWT_SECRET_DB_KEY = 'system_jwt_secret'; @@ -33,7 +33,7 @@ class SystemCrypto { } /** - * 初始化JWT密钥 - 开源友好的方式 + * Initialize JWT secret - open source friendly way */ async initializeJWTSecret(): Promise { try { @@ -41,7 +41,7 @@ class SystemCrypto { operation: "jwt_init", }); - // 1. 环境变量优先(生产环境最佳实践) + // 1. Environment variable priority (production best practice) const envSecret = process.env.JWT_SECRET; if (envSecret && envSecret.length >= 64) { this.jwtSecret = envSecret; @@ -52,7 +52,7 @@ class SystemCrypto { return; } - // 2. 检查文件系统存储 + // 2. Check filesystem storage const fileSecret = await this.loadSecretFromFile(); if (fileSecret) { this.jwtSecret = fileSecret; @@ -63,7 +63,7 @@ class SystemCrypto { return; } - // 3. 检查数据库存储 + // 3. Check database storage const dbSecret = await this.loadSecretFromDB(); if (dbSecret) { this.jwtSecret = dbSecret; @@ -74,7 +74,7 @@ class SystemCrypto { return; } - // 4. 生成新密钥并持久化 + // 4. Generate new key and persist await this.generateAndStoreSecret(); } catch (error) { @@ -86,7 +86,7 @@ class SystemCrypto { } /** - * 获取JWT密钥 + * Get JWT secret */ async getJWTSecret(): Promise { if (!this.jwtSecret) { @@ -96,7 +96,7 @@ class SystemCrypto { } /** - * 生成新密钥并持久化存储 + * Generate new key and persist storage */ private async generateAndStoreSecret(): Promise { const newSecret = crypto.randomBytes(32).toString('hex'); @@ -107,7 +107,7 @@ class SystemCrypto { instanceId }); - // 尝试文件存储(优先,因为更快且不依赖数据库) + // Try file storage (priority, faster and doesn't depend on database) try { await this.saveSecretToFile(newSecret); databaseLogger.info("✅ JWT secret saved to file", { @@ -120,7 +120,7 @@ class SystemCrypto { error: fileError instanceof Error ? fileError.message : "Unknown error" }); - // 文件存储失败,使用数据库 + // File storage failed, use database await this.saveSecretToDB(newSecret, instanceId); databaseLogger.info("✅ JWT secret saved to database", { operation: "jwt_db_saved" @@ -136,21 +136,21 @@ class SystemCrypto { }); } - // ===== 文件存储方法 ===== + // ===== File storage methods ===== /** - * 保存密钥到文件 + * Save key to file */ private async saveSecretToFile(secret: string): Promise { const dir = path.dirname(SystemCrypto.JWT_SECRET_FILE); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(SystemCrypto.JWT_SECRET_FILE, secret, { - mode: 0o600 // 只有owner可读写 + mode: 0o600 // Only owner can read/write }); } /** - * 从文件加载密钥 + * Load key from file */ private async loadSecretFromFile(): Promise { try { @@ -163,15 +163,15 @@ class SystemCrypto { length: secret.length }); } catch (error) { - // 文件不存在或无法读取,这是正常的 + // File doesn't exist or can't be read, this is normal } return null; } - // ===== 数据库存储方法 ===== + // ===== Database storage methods ===== /** - * 保存密钥到数据库(明文存储,不假装加密有用) + * Save key to database (plaintext storage, don't pretend encryption helps) */ private async saveSecretToDB(secret: string, instanceId: string): Promise { const secretData = { @@ -202,7 +202,7 @@ class SystemCrypto { } /** - * 从数据库加载密钥 + * Load key from database */ private async loadSecretFromDB(): Promise { try { @@ -217,7 +217,7 @@ class SystemCrypto { const secretData = JSON.parse(result[0].value); - // 检查密钥有效性 + // Check key validity if (!secretData.secret || secretData.secret.length < 64) { databaseLogger.warn("Invalid JWT secret in database", { operation: "jwt_db_invalid", @@ -238,7 +238,7 @@ class SystemCrypto { } /** - * 重新生成JWT密钥(管理功能) + * Regenerate JWT secret (admin function) */ async regenerateJWTSecret(): Promise { databaseLogger.warn("🔄 Regenerating JWT secret - ALL TOKENS WILL BE INVALIDATED", { @@ -256,7 +256,7 @@ class SystemCrypto { } /** - * 验证JWT密钥系统 + * Validate JWT secret system */ async validateJWTSecret(): Promise { try { @@ -265,7 +265,7 @@ class SystemCrypto { return false; } - // 测试JWT操作 + // Test JWT operations const jwt = await import("jsonwebtoken"); const testPayload = { test: true, timestamp: Date.now() }; const token = jwt.default.sign(testPayload, secret, { expiresIn: "1s" }); @@ -281,22 +281,22 @@ class SystemCrypto { } /** - * 获取JWT密钥状态(简化版本) + * Get JWT key status (simplified version) */ async getSystemKeyStatus() { const isValid = await this.validateJWTSecret(); const hasSecret = this.jwtSecret !== null; - // 检查文件存储 + // Check file storage let hasFileStorage = false; try { await fs.access(SystemCrypto.JWT_SECRET_FILE); hasFileStorage = true; } catch { - // 文件不存在 + // File doesn't exist } - // 检查数据库存储 + // Check database storage let hasDBStorage = false; let dbInfo = null; try { @@ -315,10 +315,10 @@ class SystemCrypto { }; } } catch (error) { - // 数据库读取失败 + // Database read failed } - // 检查环境变量 + // Check environment variable const hasEnvVar = !!(process.env.JWT_SECRET && process.env.JWT_SECRET.length >= 64); return { diff --git a/src/backend/utils/user-crypto.ts b/src/backend/utils/user-crypto.ts index 8a193e14..237eba9a 100644 --- a/src/backend/utils/user-crypto.ts +++ b/src/backend/utils/user-crypto.ts @@ -20,36 +20,36 @@ interface EncryptedDEK { } interface UserSession { - dataKey: Buffer; // 直接存储DEK,删除just-in-time幻想 + dataKey: Buffer; // Store DEK directly, delete just-in-time fantasy lastActivity: number; expiresAt: number; } /** - * UserCrypto - 简单直接的用户加密 + * UserCrypto - Simple direct user encryption * - * Linus原则: - * - 删除just-in-time幻想,直接缓存DEK - * - 合理的2小时超时,不是5分钟的用户体验灾难 - * - 简单可工作的实现,不是理论上完美的垃圾 - * - 服务器重启后session失效(这是合理的) + * Linus principles: + * - Delete just-in-time fantasy, cache DEK directly + * - Reasonable 2-hour timeout, not 5-minute user experience disaster + * - Simple working implementation, not theoretically perfect garbage + * - Server restart invalidates sessions (this is reasonable) */ class UserCrypto { private static instance: UserCrypto; private userSessions: Map = new Map(); - // 配置常量 - 合理的超时设置 + // Configuration constants - reasonable timeout settings private static readonly PBKDF2_ITERATIONS = 100000; private static readonly KEK_LENGTH = 32; private static readonly DEK_LENGTH = 32; - private static readonly SESSION_DURATION = 2 * 60 * 60 * 1000; // 2小时,合理的用户体验 - private static readonly MAX_INACTIVITY = 30 * 60 * 1000; // 30分钟,不是1分钟的灾难 + private static readonly SESSION_DURATION = 2 * 60 * 60 * 1000; // 2 hours, reasonable user experience + private static readonly MAX_INACTIVITY = 30 * 60 * 1000; // 30 minutes, not 1-minute disaster private constructor() { - // 合理的清理间隔 + // Reasonable cleanup interval setInterval(() => { this.cleanupExpiredSessions(); - }, 5 * 60 * 1000); // 每5分钟清理一次,不是30秒 + }, 5 * 60 * 1000); // Clean every 5 minutes, not 30 seconds } static getInstance(): UserCrypto { @@ -60,7 +60,7 @@ class UserCrypto { } /** - * 用户注册:生成KEK salt和DEK + * User registration: generate KEK salt and DEK */ async setupUserEncryption(userId: string, password: string): Promise { const kekSalt = await this.generateKEKSalt(); @@ -71,7 +71,7 @@ class UserCrypto { const encryptedDEK = this.encryptDEK(DEK, KEK); await this.storeEncryptedDEK(userId, encryptedDEK); - // 立即清理临时密钥 + // Immediately clean temporary keys KEK.fill(0); DEK.fill(0); @@ -82,12 +82,12 @@ class UserCrypto { } /** - * 用户认证:验证密码并缓存DEK - * 删除了just-in-time幻想,直接工作 + * User authentication: validate password and cache DEK + * Deleted just-in-time fantasy, works directly */ async authenticateUser(userId: string, password: string): Promise { try { - // 验证密码并解密DEK + // Validate password and decrypt DEK const kekSalt = await this.getKEKSalt(userId); if (!kekSalt) return false; @@ -99,24 +99,24 @@ class UserCrypto { } const DEK = this.decryptDEK(encryptedDEK, KEK); - KEK.fill(0); // 立即清理KEK + KEK.fill(0); // Immediately clean KEK - // 创建用户会话,直接缓存DEK + // Create user session, cache DEK directly const now = Date.now(); - // 清理旧会话 + // Clean old session const oldSession = this.userSessions.get(userId); if (oldSession) { oldSession.dataKey.fill(0); } this.userSessions.set(userId, { - dataKey: Buffer.from(DEK), // 复制DEK + dataKey: Buffer.from(DEK), // Copy DEK lastActivity: now, expiresAt: now + UserCrypto.SESSION_DURATION, }); - DEK.fill(0); // 清理临时DEK + DEK.fill(0); // Clean temporary DEK databaseLogger.success("User authenticated and DEK cached", { operation: "user_crypto_auth", @@ -136,8 +136,8 @@ class UserCrypto { } /** - * 获取用户数据密钥 - 简单直接从缓存返回 - * 删除了just-in-time推导垃圾 + * Get user data key - simple direct return from cache + * Deleted just-in-time derivation garbage */ getUserDataKey(userId: string): Buffer | null { const session = this.userSessions.get(userId); @@ -147,7 +147,7 @@ class UserCrypto { const now = Date.now(); - // 检查会话是否过期 + // Check if session has expired if (now > session.expiresAt) { this.userSessions.delete(userId); session.dataKey.fill(0); @@ -158,7 +158,7 @@ class UserCrypto { return null; } - // 检查是否超过最大不活跃时间 + // Check if max inactivity time exceeded if (now - session.lastActivity > UserCrypto.MAX_INACTIVITY) { this.userSessions.delete(userId); session.dataKey.fill(0); @@ -169,19 +169,19 @@ class UserCrypto { return null; } - // 更新最后活动时间 + // Update last activity time session.lastActivity = now; return session.dataKey; } /** - * 用户登出:清理会话 + * User logout: clear session */ logoutUser(userId: string): void { const session = this.userSessions.get(userId); if (session) { - session.dataKey.fill(0); // 安全清理密钥 + session.dataKey.fill(0); // Securely clear key this.userSessions.delete(userId); } databaseLogger.info("User logged out", { @@ -191,22 +191,22 @@ class UserCrypto { } /** - * 检查用户是否已解锁 + * Check if user is unlocked */ isUserUnlocked(userId: string): boolean { return this.getUserDataKey(userId) !== null; } /** - * 修改用户密码 + * Change user password */ async changeUserPassword(userId: string, oldPassword: string, newPassword: string): Promise { try { - // 验证旧密码 + // Validate old password const isValid = await this.validatePassword(userId, oldPassword); if (!isValid) return false; - // 获取当前DEK + // Get current DEK const kekSalt = await this.getKEKSalt(userId); if (!kekSalt) return false; @@ -216,21 +216,21 @@ class UserCrypto { const DEK = this.decryptDEK(encryptedDEK, oldKEK); - // 生成新的KEK salt和加密DEK + // Generate new KEK salt and encrypt DEK const newKekSalt = await this.generateKEKSalt(); const newKEK = this.deriveKEK(newPassword, newKekSalt); const newEncryptedDEK = this.encryptDEK(DEK, newKEK); - // 存储新的salt和encrypted DEK + // Store new salt and encrypted DEK await this.storeKEKSalt(userId, newKekSalt); await this.storeEncryptedDEK(userId, newEncryptedDEK); - // 清理所有临时密钥 + // Clean all temporary keys oldKEK.fill(0); newKEK.fill(0); DEK.fill(0); - // 清理用户会话,要求重新登录 + // Clean user session, require re-login this.logoutUser(userId); return true; @@ -239,7 +239,7 @@ class UserCrypto { } } - // ===== 私有方法 ===== + // ===== Private methods ===== private async validatePassword(userId: string, password: string): Promise { try { @@ -252,7 +252,7 @@ class UserCrypto { const DEK = this.decryptDEK(encryptedDEK, KEK); - // 清理临时密钥 + // Clean temporary keys KEK.fill(0); DEK.fill(0); @@ -268,7 +268,7 @@ class UserCrypto { for (const [userId, session] of this.userSessions.entries()) { if (now > session.expiresAt || now - session.lastActivity > UserCrypto.MAX_INACTIVITY) { - session.dataKey.fill(0); // 安全清理密钥 + session.dataKey.fill(0); // Securely clear key expiredUsers.push(userId); } } @@ -285,7 +285,7 @@ class UserCrypto { } } - // ===== 数据库操作和加密方法(简化版本) ===== + // ===== Database operations and encryption methods (simplified version) ===== private async generateKEKSalt(): Promise { return { @@ -337,7 +337,7 @@ class UserCrypto { return decrypted; } - // 数据库操作方法 + // Database operation methods private async storeKEKSalt(userId: string, kekSalt: KEKSalt): Promise { const key = `user_kek_salt_${userId}`; const value = JSON.stringify(kekSalt); diff --git a/src/backend/utils/user-data-export.ts b/src/backend/utils/user-data-export.ts index 8edba1c7..2f7fffc0 100644 --- a/src/backend/utils/user-data-export.ts +++ b/src/backend/utils/user-data-export.ts @@ -28,19 +28,19 @@ interface UserExportData { } /** - * UserDataExport - 用户级数据导入导出 + * UserDataExport - User-level data import/export * - * Linus原则: - * - 用户拥有自己的数据,应该能自由导出 - * - 简单直接,没有复杂的权限检查 - * - 支持加密和明文两种格式 - * - 不破坏现有系统架构 + * Linus principles: + * - Users own their data and should be able to export freely + * - Simple and direct, no complex permission checks + * - Support both encrypted and plaintext formats + * - Don't break existing system architecture */ class UserDataExport { private static readonly EXPORT_VERSION = "v2.0"; /** - * 导出用户数据 + * Export user data */ static async exportUserData( userId: string, @@ -61,7 +61,7 @@ class UserDataExport { includeCredentials, }); - // 验证用户存在 + // Verify user exists const user = await db.select().from(users).where(eq(users.id, userId)); if (!user || user.length === 0) { throw new Error(`User not found: ${userId}`); @@ -69,7 +69,7 @@ class UserDataExport { const userRecord = user[0]; - // 获取用户数据密钥(如果需要解密) + // Get user data key (if decryption needed) let userDataKey: Buffer | null = null; if (format === 'plaintext') { userDataKey = DataCrypto.getUserDataKey(userId); @@ -78,13 +78,13 @@ class UserDataExport { } } - // 导出SSH主机配置 + // Export SSH host configurations const sshHosts = await db.select().from(sshData).where(eq(sshData.userId, userId)); const processedSshHosts = format === 'plaintext' && userDataKey ? sshHosts.map(host => DataCrypto.decryptRecord("ssh_data", host, userId, userDataKey!)) : sshHosts; - // 导出SSH凭据(如果包含) + // Export SSH credentials (if included) let sshCredentialsData: any[] = []; if (includeCredentials) { const credentials = await db.select().from(sshCredentials).where(eq(sshCredentials.userId, userId)); @@ -93,17 +93,17 @@ class UserDataExport { : credentials; } - // 导出文件管理器数据 + // Export file manager data const [recentFiles, pinnedFiles, shortcuts] = await Promise.all([ db.select().from(fileManagerRecent).where(eq(fileManagerRecent.userId, userId)), db.select().from(fileManagerPinned).where(eq(fileManagerPinned.userId, userId)), db.select().from(fileManagerShortcuts).where(eq(fileManagerShortcuts.userId, userId)), ]); - // 导出已忽略的警告 + // Export dismissed alerts const alerts = await db.select().from(dismissedAlerts).where(eq(dismissedAlerts.userId, userId)); - // 构建导出数据 + // Build export data const exportData: UserExportData = { version: this.EXPORT_VERSION, exportedAt: new Date().toISOString(), @@ -148,7 +148,7 @@ class UserDataExport { } /** - * 导出为JSON字符串 + * Export as JSON string */ static async exportUserDataToJSON( userId: string, @@ -165,7 +165,7 @@ class UserDataExport { } /** - * 验证导出数据格式 + * Validate export data format */ static validateExportData(data: any): { valid: boolean; errors: string[] } { const errors: string[] = []; @@ -191,7 +191,7 @@ class UserDataExport { errors.push("Missing or invalid metadata field"); } - // 检查必需的数据字段 + // Check required data fields if (data.userData) { const requiredFields = ['sshHosts', 'sshCredentials', 'fileManagerData', 'dismissedAlerts']; for (const field of requiredFields) { @@ -214,7 +214,7 @@ class UserDataExport { } /** - * 获取导出数据统计信息 + * Get export data statistics */ static getExportStats(data: UserExportData): { version: string; diff --git a/src/backend/utils/user-data-import.ts b/src/backend/utils/user-data-import.ts index 1ae6c74e..ba66e911 100644 --- a/src/backend/utils/user-data-import.ts +++ b/src/backend/utils/user-data-import.ts @@ -27,18 +27,18 @@ interface ImportResult { } /** - * UserDataImport - 用户数据导入 + * UserDataImport - User data import * - * Linus原则: - * - 导入不应该破坏现有数据(除非明确要求) - * - 支持dry-run模式验证 - * - 处理ID冲突的简单策略:重新生成 - * - 错误处理要明确,不能静默失败 + * Linus principles: + * - Import should not break existing data (unless explicitly requested) + * - Support dry-run mode for validation + * - Simple strategy for ID conflicts: regenerate + * - Error handling must be explicit, no silent failures */ class UserDataImport { /** - * 导入用户数据 + * Import user data */ static async importUserData( targetUserId: string, @@ -64,19 +64,19 @@ class UserDataImport { skipFileManagerData, }); - // 验证目标用户存在 + // Verify target user exists const targetUser = await db.select().from(users).where(eq(users.id, targetUserId)); if (!targetUser || targetUser.length === 0) { throw new Error(`Target user not found: ${targetUserId}`); } - // 验证导出数据格式 + // Validate export data format const validation = UserDataExport.validateExportData(exportData); if (!validation.valid) { throw new Error(`Invalid export data: ${validation.errors.join(', ')}`); } - // 验证用户数据已解锁(如果数据是加密的) + // Verify user data is unlocked (if data is encrypted) let userDataKey: Buffer | null = null; if (exportData.metadata.encrypted) { userDataKey = DataCrypto.getUserDataKey(targetUserId); @@ -98,7 +98,7 @@ class UserDataImport { dryRun, }; - // 导入SSH主机配置 + // Import SSH host configurations if (exportData.userData.sshHosts && exportData.userData.sshHosts.length > 0) { const importStats = await this.importSshHosts( targetUserId, @@ -110,7 +110,7 @@ class UserDataImport { result.summary.errors.push(...importStats.errors); } - // 导入SSH凭据 + // Import SSH credentials if (!skipCredentials && exportData.userData.sshCredentials && exportData.userData.sshCredentials.length > 0) { const importStats = await this.importSshCredentials( targetUserId, @@ -122,7 +122,7 @@ class UserDataImport { result.summary.errors.push(...importStats.errors); } - // 导入文件管理器数据 + // Import file manager data if (!skipFileManagerData && exportData.userData.fileManagerData) { const importStats = await this.importFileManagerData( targetUserId, @@ -134,7 +134,7 @@ class UserDataImport { result.summary.errors.push(...importStats.errors); } - // 导入忽略的警告 + // Import dismissed alerts if (exportData.userData.dismissedAlerts && exportData.userData.dismissedAlerts.length > 0) { const importStats = await this.importDismissedAlerts( targetUserId, @@ -167,7 +167,7 @@ class UserDataImport { } /** - * 导入SSH主机配置 + * Import SSH host configurations */ private static async importSshHosts( targetUserId: string, @@ -185,16 +185,16 @@ class UserDataImport { continue; } - // 重新生成ID避免冲突 + // Regenerate ID to avoid conflicts const newHostData = { ...host, - id: undefined, // 让数据库自动生成 + id: undefined, // Let database auto-generate userId: targetUserId, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; - // 如果数据需要重新加密 + // If data needs re-encryption let processedHostData = newHostData; if (options.userDataKey) { processedHostData = DataCrypto.encryptRecord("ssh_data", newHostData, targetUserId, options.userDataKey); @@ -212,7 +212,7 @@ class UserDataImport { } /** - * 导入SSH凭据 + * Import SSH credentials */ private static async importSshCredentials( targetUserId: string, @@ -230,18 +230,18 @@ class UserDataImport { continue; } - // 重新生成ID避免冲突 + // Regenerate ID to avoid conflicts const newCredentialData = { ...credential, - id: undefined, // 让数据库自动生成 + id: undefined, // Let database auto-generate userId: targetUserId, - usageCount: 0, // 重置使用计数 + usageCount: 0, // Reset usage count lastUsed: null, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; - // 如果数据需要重新加密 + // If data needs re-encryption let processedCredentialData = newCredentialData; if (options.userDataKey) { processedCredentialData = DataCrypto.encryptRecord("ssh_credentials", newCredentialData, targetUserId, options.userDataKey); @@ -259,7 +259,7 @@ class UserDataImport { } /** - * 导入文件管理器数据 + * Import file manager data */ private static async importFileManagerData( targetUserId: string, @@ -271,7 +271,7 @@ class UserDataImport { const errors: string[] = []; try { - // 导入最近文件 + // Import recent files if (fileManagerData.recent && Array.isArray(fileManagerData.recent)) { for (const item of fileManagerData.recent) { try { @@ -292,7 +292,7 @@ class UserDataImport { } } - // 导入固定文件 + // Import pinned files if (fileManagerData.pinned && Array.isArray(fileManagerData.pinned)) { for (const item of fileManagerData.pinned) { try { @@ -313,7 +313,7 @@ class UserDataImport { } } - // 导入快捷方式 + // Import shortcuts if (fileManagerData.shortcuts && Array.isArray(fileManagerData.shortcuts)) { for (const item of fileManagerData.shortcuts) { try { @@ -341,7 +341,7 @@ class UserDataImport { } /** - * 导入忽略的警告 + * Import dismissed alerts */ private static async importDismissedAlerts( targetUserId: string, @@ -359,7 +359,7 @@ class UserDataImport { continue; } - // 检查是否已存在相同的警告 + // Check if alert already exists const existing = await db .select() .from(dismissedAlerts) @@ -402,7 +402,7 @@ class UserDataImport { } /** - * 从JSON字符串导入 + * Import from JSON string */ static async importUserDataFromJSON( targetUserId: string,