Spaces:
Running
Running
File size: 7,389 Bytes
ceb3821 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | 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分钟清理一次 |