fix: 统一管理员密码管理机制,以init.json为唯一数据源

- app.js: 每次启动强制从init.json加载管理员凭据到Redis,确保数据一致性
- web.js: 修改密码时先更新init.json,成功后再更新Redis缓存
- cli/index.js: CLI创建管理员时同时更新init.json和Redis
- setup.js: 优化提示信息,明确重置密码需要重启服务
- admin.js: 修复Claude账户专属绑定功能的验证逻辑

解决了之前存在的双重存储同步问题,现在init.json是唯一真实数据源。

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-07-17 20:57:31 +08:00
parent ee9bd4aea4
commit 11873ed78b
6 changed files with 188 additions and 74 deletions

View File

@@ -183,40 +183,37 @@ class Application {
}
}
// 🔧 初始化管理员凭据
// 🔧 初始化管理员凭据(总是从 init.json 加载,确保数据一致性)
async initializeAdmin() {
try {
// 检查Redis中是否已存在管理员凭据
const existingAdmin = await redis.getSession('admin_credentials');
const initFilePath = path.join(__dirname, '..', 'data', 'init.json');
if (!existingAdmin || Object.keys(existingAdmin).length === 0) {
// 尝试从初始化文件读取
const initFilePath = path.join(__dirname, '..', 'data', 'init.json');
if (fs.existsSync(initFilePath)) {
const initData = JSON.parse(fs.readFileSync(initFilePath, 'utf8'));
// 将明文密码哈希化
const saltRounds = 10;
const passwordHash = await bcrypt.hash(initData.adminPassword, saltRounds);
// 存储到Redis
const adminCredentials = {
username: initData.adminUsername,
passwordHash: passwordHash,
createdAt: new Date().toISOString(),
lastLogin: null
};
await redis.setSession('admin_credentials', adminCredentials);
logger.success('✅ Admin credentials initialized from setup data');
} else {
logger.warn('⚠️ No admin credentials found. Please run npm run setup first.');
}
} else {
logger.info(' Admin credentials already exist in Redis');
if (!fs.existsSync(initFilePath)) {
logger.warn('⚠️ No admin credentials found. Please run npm run setup first.');
return;
}
// 从 init.json 读取管理员凭据(作为唯一真实数据源)
const initData = JSON.parse(fs.readFileSync(initFilePath, 'utf8'));
// 将明文密码哈希化
const saltRounds = 10;
const passwordHash = await bcrypt.hash(initData.adminPassword, saltRounds);
// 存储到Redis每次启动都覆盖确保与 init.json 同步)
const adminCredentials = {
username: initData.adminUsername,
passwordHash: passwordHash,
createdAt: initData.initializedAt || new Date().toISOString(),
lastLogin: null,
updatedAt: initData.updatedAt || null
};
await redis.setSession('admin_credentials', adminCredentials);
logger.success('✅ Admin credentials loaded from init.json (single source of truth)');
logger.info(`📋 Admin username: ${adminCredentials.username}`);
} catch (error) {
logger.error('❌ Failed to initialize admin credentials:', { error: error.message, stack: error.stack });
throw error;