Jaasomn
Initial deployment
ceb3821
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分钟清理一次