Spaces:
Sleeping
Sleeping
| import { existsSync } from 'fs'; | |
| import { promises as fs } from 'fs'; | |
| import path from 'path'; | |
| import crypto from 'crypto'; | |
| // Token存储到本地文件中 | |
| const TOKEN_STORE_FILE = path.join(process.cwd(), 'configs', 'token-store.json'); | |
| /** | |
| * 默认密码(当pwd文件不存在时使用) | |
| */ | |
| const DEFAULT_PASSWORD = 'admin123'; | |
| /** | |
| * 读取密码文件内容 | |
| * 如果文件不存在或读取失败,返回默认密码 | |
| */ | |
| export async function readPasswordFile() { | |
| const pwdFilePath = path.join(process.cwd(), 'configs', 'pwd'); | |
| try { | |
| // 使用异步方式检查文件是否存在并读取,避免竞态条件 | |
| const password = await fs.readFile(pwdFilePath, 'utf8'); | |
| const trimmedPassword = password.trim(); | |
| // 如果密码文件为空,使用默认密码 | |
| if (!trimmedPassword) { | |
| console.log('[Auth] Password file is empty, using default password: ' + DEFAULT_PASSWORD); | |
| return DEFAULT_PASSWORD; | |
| } | |
| console.log('[Auth] Successfully read password file'); | |
| return trimmedPassword; | |
| } catch (error) { | |
| // ENOENT means file does not exist, which is normal | |
| if (error.code === 'ENOENT') { | |
| console.log('[Auth] Password file does not exist, using default password: ' + DEFAULT_PASSWORD); | |
| } else { | |
| console.error('[Auth] Failed to read password file:', error.code || error.message); | |
| console.log('[Auth] Using default password: ' + DEFAULT_PASSWORD); | |
| } | |
| return DEFAULT_PASSWORD; | |
| } | |
| } | |
| /** | |
| * 验证登录凭据 | |
| */ | |
| export async function validateCredentials(password) { | |
| const storedPassword = await readPasswordFile(); | |
| console.log('[Auth] Validating password, stored password length:', storedPassword ? storedPassword.length : 0, ', input password length:', password ? password.length : 0); | |
| const isValid = storedPassword && password === storedPassword; | |
| console.log('[Auth] Password validation result:', isValid); | |
| return isValid; | |
| } | |
| /** | |
| * 解析请求体JSON | |
| */ | |
| function parseRequestBody(req) { | |
| return new Promise((resolve, reject) => { | |
| let body = ''; | |
| req.on('data', chunk => { | |
| body += chunk.toString(); | |
| }); | |
| req.on('end', () => { | |
| try { | |
| if (!body.trim()) { | |
| resolve({}); | |
| } else { | |
| resolve(JSON.parse(body)); | |
| } | |
| } catch (error) { | |
| reject(new Error('Invalid JSON format')); | |
| } | |
| }); | |
| req.on('error', reject); | |
| }); | |
| } | |
| /** | |
| * 生成简单的token | |
| */ | |
| function generateToken() { | |
| return crypto.randomBytes(32).toString('hex'); | |
| } | |
| /** | |
| * 生成token过期时间 | |
| */ | |
| function getExpiryTime() { | |
| const now = Date.now(); | |
| const expiry = 60 * 60 * 1000; // 1小时 | |
| return now + expiry; | |
| } | |
| /** | |
| * 读取token存储文件 | |
| */ | |
| async function readTokenStore() { | |
| try { | |
| if (existsSync(TOKEN_STORE_FILE)) { | |
| const content = await fs.readFile(TOKEN_STORE_FILE, 'utf8'); | |
| return JSON.parse(content); | |
| } else { | |
| // 如果文件不存在,创建一个默认的token store | |
| await writeTokenStore({ tokens: {} }); | |
| return { tokens: {} }; | |
| } | |
| } catch (error) { | |
| console.error('[Token Store] Failed to read token store file:', error); | |
| return { tokens: {} }; | |
| } | |
| } | |
| /** | |
| * 写入token存储文件 | |
| */ | |
| async function writeTokenStore(tokenStore) { | |
| try { | |
| await fs.writeFile(TOKEN_STORE_FILE, JSON.stringify(tokenStore, null, 2), 'utf8'); | |
| } catch (error) { | |
| console.error('[Token Store] Failed to write token store file:', error); | |
| } | |
| } | |
| /** | |
| * 验证简单token | |
| */ | |
| export async function verifyToken(token) { | |
| const tokenStore = await readTokenStore(); | |
| const tokenInfo = tokenStore.tokens[token]; | |
| if (!tokenInfo) { | |
| return null; | |
| } | |
| // 检查是否过期 | |
| if (Date.now() > tokenInfo.expiryTime) { | |
| await deleteToken(token); | |
| return null; | |
| } | |
| return tokenInfo; | |
| } | |
| /** | |
| * 保存token到本地文件 | |
| */ | |
| async function saveToken(token, tokenInfo) { | |
| const tokenStore = await readTokenStore(); | |
| tokenStore.tokens[token] = tokenInfo; | |
| await writeTokenStore(tokenStore); | |
| } | |
| /** | |
| * 删除token | |
| */ | |
| async function deleteToken(token) { | |
| const tokenStore = await readTokenStore(); | |
| if (tokenStore.tokens[token]) { | |
| delete tokenStore.tokens[token]; | |
| await writeTokenStore(tokenStore); | |
| } | |
| } | |
| /** | |
| * 清理过期的token | |
| */ | |
| export async function cleanupExpiredTokens() { | |
| const tokenStore = await readTokenStore(); | |
| const now = Date.now(); | |
| let hasChanges = false; | |
| for (const token in tokenStore.tokens) { | |
| if (now > tokenStore.tokens[token].expiryTime) { | |
| delete tokenStore.tokens[token]; | |
| hasChanges = true; | |
| } | |
| } | |
| if (hasChanges) { | |
| await writeTokenStore(tokenStore); | |
| } | |
| } | |
| /** | |
| * 检查token验证 | |
| */ | |
| export async function checkAuth(req) { | |
| const authHeader = req.headers.authorization; | |
| if (!authHeader || !authHeader.startsWith('Bearer ')) { | |
| return false; | |
| } | |
| const token = authHeader.substring(7); | |
| const tokenInfo = await verifyToken(token); | |
| return tokenInfo !== null; | |
| } | |
| /** | |
| * 处理登录请求 | |
| */ | |
| export async function handleLoginRequest(req, res) { | |
| if (req.method !== 'POST') { | |
| res.writeHead(405, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify({ success: false, message: 'Only POST requests are supported' })); | |
| return true; | |
| } | |
| try { | |
| const requestData = await parseRequestBody(req); | |
| const { password } = requestData; | |
| if (!password) { | |
| res.writeHead(400, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify({ success: false, message: 'Password cannot be empty' })); | |
| return true; | |
| } | |
| const isValid = await validateCredentials(password); | |
| if (isValid) { | |
| // Generate simple token | |
| const token = generateToken(); | |
| const expiryTime = getExpiryTime(); | |
| // Store token info to local file | |
| await saveToken(token, { | |
| username: 'admin', | |
| loginTime: Date.now(), | |
| expiryTime | |
| }); | |
| res.writeHead(200, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify({ | |
| success: true, | |
| message: 'Login successful', | |
| token, | |
| expiresIn: '1 hour' | |
| })); | |
| } else { | |
| res.writeHead(401, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify({ | |
| success: false, | |
| message: 'Incorrect password, please try again' | |
| })); | |
| } | |
| } catch (error) { | |
| console.error('[Auth] Login processing error:', error); | |
| res.writeHead(500, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify({ | |
| success: false, | |
| message: error.message || 'Server error' | |
| })); | |
| } | |
| return true; | |
| } | |
| // 定时清理过期token | |
| setInterval(cleanupExpiredTokens, 5 * 60 * 1000); // 每5分钟清理一次 |