#!/usr/bin/env node import Database from 'better-sqlite3' import bcrypt from 'bcryptjs' import { v4 as uuidv4 } from 'uuid' import { join, dirname } from 'path' import { fileURLToPath } from 'url' import fs from 'fs' import readline from 'readline' const __dirname = dirname(fileURLToPath(import.meta.url)) const projectRoot = join(__dirname, '..') const dataDir = join(projectRoot, 'data') const dbPath = join(dataDir, 'sports.db') if (!fs.existsSync(dbPath)) { console.error('❌ 数据库文件不存在,请先运行项目') process.exit(1) } const db = new Database(dbPath) const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) function prompt(question) { return new Promise((resolve) => rl.question(question, resolve)) } function validatePassword(password) { if (password.length < 6) { return { valid: false, message: '密码长度至少6位' } } if (/^\d+$/.test(password)) { return { valid: false, message: '密码不能为纯数字' } } return { valid: true } } function initRoles() { const existingRoles = db.prepare('SELECT COUNT(*) as count FROM roles').get() if (existingRoles.count === 0) { console.log('📝 初始化默认角色...') const defaultRoles = [ { name: 'admin', description: '系统管理员', permissions: JSON.stringify([ 'user:create', 'user:read', 'user:update', 'user:delete', 'event:create', 'event:read', 'event:update', 'event:delete', 'result:create', 'result:read', 'result:update', 'result:delete', 'team:create', 'team:read', 'team:update', 'team:delete', 'admin:access', 'role:manage', 'oauth:client:create', 'oauth:client:read', 'oauth:client:update', 'oauth:client:delete' ]), is_system: 1 }, { name: 'user', description: '普通用户', permissions: JSON.stringify([ 'event:read', 'result:read', 'team:read' ]), is_system: 1 }, { name: 'guest', description: '访客', permissions: JSON.stringify(['event:read']), is_system: 1 } ] const stmt = db.prepare('INSERT INTO roles (name, description, permissions, is_system) VALUES (?, ?, ?, ?)') for (const role of defaultRoles) { stmt.run(role.name, role.description, role.permissions, role.is_system) } console.log('✅ 默认角色初始化完成') } } async function listUsers() { console.log('\n📋 用户列表:\n') console.log('ID | 用户名 | 邮箱 | 角色 | 状态 | 创建时间') console.log('-'.repeat(70)) const users = db.prepare(` SELECT u.*, r.name as role_name FROM users u LEFT JOIN roles r ON u.role_id = r.id ORDER BY u.created_at DESC `).all() if (users.length === 0) { console.log('暂无用户') return } for (const user of users) { console.log( `${user.id} | ${user.username} | ${user.email || '-'} | ${user.role_name} | ${user.status} | ${user.created_at}` ) } } async function createUser() { const username = await prompt('用户名: ') if (!username || username.trim().length < 2) { console.log('❌ 用户名至少2个字符') return } const existing = db.prepare('SELECT id FROM users WHERE username = ?').get(username) if (existing) { console.log('❌ 用户名已存在') return } let password while (true) { password = await prompt('密码: ') const validation = validatePassword(password) if (validation.valid) break console.log(`❌ ${validation.message}`) } const email = await prompt('邮箱 (可选): ') let roleId = 2 const roles = db.prepare('SELECT id, name FROM roles').all() if (roles.length > 0) { console.log('\n可选角色:') for (const role of roles) { console.log(` ${role.id}. ${role.name}`) } const roleInput = await prompt('角色 ID (默认: 2): ') if (roleInput && roles.find(r => r.id === parseInt(roleInput))) { roleId = parseInt(roleInput) } } const passwordHash = bcrypt.hashSync(password, 10) const stmt = db.prepare( 'INSERT INTO users (username, password_hash, email, role_id) VALUES (?, ?, ?, ?)' ) const result = stmt.run(username, passwordHash, email || null, roleId) console.log(`\n✅ 用户创建成功 (ID: ${result.lastInsertRowid})`) } async function deleteUser() { const username = await prompt('要删除的用户名: ') const user = db.prepare('SELECT id, username, role_id FROM users WHERE username = ?').get(username) if (!user) { console.log('❌ 用户不存在') return } const role = db.prepare('SELECT is_system FROM roles WHERE id = ?').get(user.role_id) if (role?.is_system) { console.log('❌ 不能删除系统内置用户') return } const confirm = await prompt(`确认删除用户 "${username}" ?(y/N): `) if (confirm.toLowerCase() !== 'y') { console.log('已取消') return } db.prepare('DELETE FROM users WHERE id = ?').run(user.id) console.log('✅ 用户已删除') } async function resetPassword() { const username = await prompt('用户名: ') const user = db.prepare('SELECT id FROM users WHERE username = ?').get(username) if (!user) { console.log('❌ 用户不存在') return } let password while (true) { password = await prompt('新密码: ') const validation = validatePassword(password) if (validation.valid) break console.log(`❌ ${validation.message}`) } const passwordHash = bcrypt.hashSync(password, 10) db.prepare('UPDATE users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(passwordHash, user.id) console.log('✅ 密码重置成功') } async function setRole() { const username = await prompt('用户名: ') const user = db.prepare('SELECT id, role_id FROM users WHERE username = ?').get(username) if (!user) { console.log('❌ 用户不存在') return } const roles = db.prepare('SELECT id, name FROM roles').all() console.log('\n可选角色:') for (const role of roles) { console.log(` ${role.id}. ${role.name}`) } const roleInput = await prompt('新角色 ID: ') const newRole = roles.find(r => r.id === parseInt(roleInput)) if (!newRole) { console.log('❌ 无效的角色 ID') return } db.prepare('UPDATE users SET role_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(newRole.id, user.id) console.log(`✅ 已将 ${username} 的角色设置为 ${newRole.name}`) } async function setStatus() { const username = await prompt('用户名: ') const user = db.prepare('SELECT id, status FROM users WHERE username = ?').get(username) if (!user) { console.log('❌ 用户不存在') return } console.log('\n可选状态:') console.log(' 1. active (正常)') console.log(' 2. inactive (停用)') console.log(' 3. banned (封禁)') const statusMap = { '1': 'active', '2': 'inactive', '3': 'banned' } const input = await prompt('新状态: ') const newStatus = statusMap[input] if (!newStatus) { console.log('❌ 无效的状态') return } db.prepare('UPDATE users SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(newStatus, user.id) console.log(`✅ 用户状态已更新为 ${newStatus}`) } async function createOAuthClient() { const clientName = await prompt('应用名称: ') if (!clientName) { console.log('❌ 应用名称不能为空') return } const redirectUrisInput = await prompt('允许的重定向 URI (多个用逗号分隔): ') const redirectUris = redirectUrisInput.split(',').map(uri => uri.trim()).filter(uri => uri) console.log('\n支持的授权模式:') console.log(' 1. authorization_code (授权码模式)') console.log(' 2. password (密码模式)') console.log(' 3. client_credentials (客户端凭据模式)') console.log(' 4. refresh_token (刷新令牌)') const grantTypeMap = { '1': 'authorization_code', '2': 'password', '3': 'client_credentials', '4': 'refresh_token' } const grantInput = await prompt('授权模式 (多个用逗号分隔,默认1): ') const selectedGrants = grantInput ? grantInput.split(',').map(g => grantTypeMap[g.trim()]).filter(g => g) : ['authorization_code'] console.log('\n支持的平台:') console.log(' 1. web (网页应用)') console.log(' 2. mobile (移动应用)') console.log(' 3. desktop (桌面应用)') console.log(' 4. other (其他)') const platformMap = { '1': 'web', '2': 'mobile', '3': 'desktop', '4': 'other' } const platformInput = await prompt('平台 (默认1): ') const platform = platformMap[platformInput] || 'web' const clientId = uuidv4().replace(/-/g, '') const clientSecret = uuidv4() const clientSecretHash = bcrypt.hashSync(clientSecret, 10) const stmt = db.prepare(` INSERT INTO oauth_clients (client_id, client_secret_hash, client_name, redirect_uris, allowed_scopes, grant_types, platform) VALUES (?, ?, ?, ?, ?, ?, ?) `) stmt.run( clientId, clientSecretHash, clientName, JSON.stringify(redirectUris), JSON.stringify(['read', 'write']), JSON.stringify(selectedGrants), platform ) console.log('\n✅ OAuth 客户端创建成功!\n') console.log('='.repeat(50)) console.log('Client ID:') console.log(clientId) console.log('\nClient Secret:') console.log(clientSecret) console.log('='.repeat(50)) console.log('\n⚠️ 请妥善保存 Client Secret,它不会再次显示!\n') } async function showHelp() { console.log(` 🔧 运动会管理系统 - 用户管理工具 用法: node scripts/user-cli.js 命令: list 列出所有用户 create 创建新用户 delete 删除用户 reset-password 重置用户密码 set-role 设置用户角色 set-status 设置用户状态 create-client 创建 OAuth 客户端 init 初始化默认角色 help 显示帮助信息 示例: node scripts/user-cli.js list node scripts/user-cli.js create node scripts/user-cli.js reset-password `) } async function main() { const args = process.argv.slice(2) const command = args[0] || 'help' console.log('🏃 运动会管理系统 - 用户管理 CLI\n') switch (command) { case 'list': await listUsers() break case 'create': await createUser() break case 'delete': await deleteUser() break case 'reset-password': await resetPassword() break case 'set-role': await setRole() break case 'set-status': await setStatus() break case 'create-client': await createOAuthClient() break case 'init': initRoles() break case 'help': default: await showHelp() } rl.close() } main().catch(console.error)