Files
claude-relay-service/scripts/setup.js
shaw 11873ed78b 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>
2025-07-17 20:57:31 +08:00

111 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const bcrypt = require('bcryptjs');
const chalk = require('chalk');
const ora = require('ora');
const config = require('../config/config');
async function setup() {
console.log(chalk.blue.bold('\n🚀 Claude Relay Service 初始化设置\n'));
const spinner = ora('正在进行初始化设置...').start();
try {
// 1. 创建必要目录
const directories = [
'logs',
'data',
'temp'
];
directories.forEach(dir => {
const dirPath = path.join(__dirname, '..', dir);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
});
// 2. 生成环境配置文件
if (!fs.existsSync(path.join(__dirname, '..', '.env'))) {
const envTemplate = fs.readFileSync(path.join(__dirname, '..', '.env.example'), 'utf8');
// 生成随机密钥
const jwtSecret = crypto.randomBytes(64).toString('hex');
const encryptionKey = crypto.randomBytes(32).toString('hex');
const envContent = envTemplate
.replace('your-jwt-secret-here', jwtSecret)
.replace('your-encryption-key-here', encryptionKey);
fs.writeFileSync(path.join(__dirname, '..', '.env'), envContent);
}
// 3. 生成随机管理员凭据
const adminUsername = `cr_admin_${crypto.randomBytes(4).toString('hex')}`;
const adminPassword = crypto.randomBytes(16).toString('base64').replace(/[^a-zA-Z0-9]/g, '').substring(0, 16);
// 4. 创建初始化完成标记文件
const initData = {
initializedAt: new Date().toISOString(),
adminUsername,
adminPassword,
version: '1.0.0'
};
fs.writeFileSync(
path.join(__dirname, '..', 'data', 'init.json'),
JSON.stringify(initData, null, 2)
);
spinner.succeed('初始化设置完成');
console.log(chalk.green('\n✅ 设置完成!\n'));
console.log(chalk.yellow('📋 重要信息:\n'));
console.log(` 管理员用户名: ${chalk.cyan(adminUsername)}`);
console.log(` 管理员密码: ${chalk.cyan(adminPassword)}`);
console.log(chalk.red('\n⚠ 请立即保存这些凭据!首次登录后建议修改密码。\n'));
console.log(chalk.blue('🚀 启动服务:\n'));
console.log(' npm start - 启动生产服务');
console.log(' npm run dev - 启动开发服务');
console.log(' npm run cli admin - 管理员CLI工具\n');
console.log(chalk.blue('🌐 访问地址:\n'));
console.log(` Web管理界面: http://localhost:${config.server.port}/web`);
console.log(` API端点: http://localhost:${config.server.port}/api/v1/messages`);
console.log(` 健康检查: http://localhost:${config.server.port}/health\n`);
} catch (error) {
spinner.fail('初始化设置失败');
console.error(chalk.red('❌ 错误:'), error.message);
process.exit(1);
}
}
// 检查是否已初始化
function checkInitialized() {
const initFile = path.join(__dirname, '..', 'data', 'init.json');
if (fs.existsSync(initFile)) {
const initData = JSON.parse(fs.readFileSync(initFile, 'utf8'));
console.log(chalk.yellow('⚠️ 服务已经初始化过了!'));
console.log(` 初始化时间: ${new Date(initData.initializedAt).toLocaleString()}`);
console.log(` 管理员用户名: ${initData.adminUsername}`);
console.log('\n如需重新初始化请删除 data/init.json 文件后再运行此命令。');
console.log(chalk.red('\n⚠ 重要提示:'));
console.log(' 1. 删除 init.json 文件后运行 npm run setup');
console.log(' 2. 生成新的账号密码后,需要重启服务才能生效');
console.log(' 3. 使用 npm run service:restart 重启服务\n');
return true;
}
return false;
}
if (require.main === module) {
if (!checkInitialized()) {
setup();
}
}
module.exports = { setup, checkInitialized };