UNPKG

mira-app-server

Version:

Mira Server - standalone server application using mira-app-core

294 lines 11.7 kB
"use strict"; /** * 认证与凭证命令 * * - login: 交互式登录,凭证持久化到本地 profile * - logout: 清除当前 profile 的 token * - whoami: 显示当前登录用户 * - auth use / list / add / remove: profile 管理 */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.registerAuth = registerAuth; const readline = __importStar(require("readline/promises")); const credentials_1 = require("../credentials"); const client_1 = require("../client"); const format_1 = require("../format"); /** * 交互式提示输入。 * @param question 提示语 * @param hidden 是否隐藏输入(密码场景)。隐藏通过在原始模式下逐字符读取、 * 输出星号实现,回车结束。 */ async function prompt(question, hidden = false) { if (!hidden) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); try { return (await rl.question(question)).trim(); } finally { rl.close(); } } // 隐藏输入模式:直接操作 stdin/stdout,原始模式逐字符读取 return new Promise(resolve => { const stdin = process.stdin; const stdout = process.stdout; stdout.write(question); let value = ''; const onData = (buffer) => { for (const byte of buffer) { // \r (13) 或 \n (10) 表示回车结束 if (byte === 0x0d || byte === 0x0a) { stdout.write('\n'); stdin.removeListener('data', onData); stdin.setRawMode(false); stdin.pause(); resolve(value.trim()); return; } // Backspace (127 / 8) if (byte === 0x7f || byte === 0x08) { if (value.length > 0) { value = value.slice(0, -1); stdout.write('\b \b'); } continue; } // Ctrl+C (3) if (byte === 0x03) { stdout.write('\n'); stdin.removeListener('data', onData); stdin.setRawMode(false); process.exit(0); } const ch = String.fromCharCode(byte); value += ch; stdout.write('*'); } }; stdin.setRawMode(true); stdin.resume(); stdin.on('data', onData); }); } function registerAuth(program) { // ============ login ============ program .command('login [server]') .description('登录到 Mira 服务器,凭证会保存到本地 profile') .option('-u, --username <username>', '用户名(不填则交互式输入)') .option('-p, --password <password>', '密码(不填则交互式输入)') .option('--profile <name>', '保存到的 profile 名', credentials_1.DEFAULT_PROFILE_NAME) .action(async (serverArg, options) => { try { // server: 位置参数 > 全局 --server > 默认 const server = serverArg || program.opts().server || client_1.DEFAULT_SERVER; const { client } = (0, client_1.getAnonymousClient)({ server }); let username = options.username; let password = options.password; let authRequired = true; // 显式凭据直接登录,避免不同 SDK 版本的 health 解析阻断认证请求。 if (!username && !password) { const health = await client.system().getHealth(); authRequired = health.authRequired !== false; } if (authRequired) { if (!username) { username = await prompt('用户名: '); } if (!password) { password = await prompt('密码: ', true); } if (!username || !password) { throw new Error('用户名和密码不能为空'); } await client.auth().login(username, password); (0, format_1.success)(`登录成功 (${server})`); } else { (0, format_1.success)(`服务器 ${server} 无需鉴权`); } // 拉取用户信息 const userInfo = authRequired ? await client.user().getInfo() : { username: username || 'anonymous' }; // 持久化 profile const profile = { server, token: client.getConfig().token || '', username: userInfo.username || username, }; (0, credentials_1.saveProfile)(options.profile, profile); (0, format_1.output)({ profile: options.profile, server, user: userInfo }, () => `当前 profile: ${options.profile}\n` + `服务器: ${server}\n` + `用户信息:\n${(0, format_1.formatKeyValue)(userInfo)}`); } catch (error) { (0, format_1.fatal)(error); } }); // ============ logout ============ program .command('logout') .description('登出当前 profile(清除本地保存的 token)') .option('--profile <name>', '指定登出的 profile') .action(async (options) => { try { // 尝试通知服务器登出(失败不阻塞) try { const { client } = (0, client_1.getClient)(false, { profile: options.profile }); if (client.getConfig().token) { await client.auth().logout(); } } catch { // 忽略服务器端登出错误 } const name = options.profile || (0, credentials_1.getCurrentProfile)()?.name; if (!name) { throw new Error('没有可登出的 profile'); } // 仅清除当前指定 profile 的 token if (options.profile) { const profile = (0, credentials_1.getProfile)(options.profile); if (profile) { (0, credentials_1.saveProfile)(options.profile, { ...profile, token: '' }); } } else { (0, credentials_1.clearCurrentToken)(); } (0, format_1.success)(`已登出 profile: ${name}`); } catch (error) { (0, format_1.fatal)(error); } }); // ============ whoami ============ program .command('whoami') .description('显示当前登录用户信息') .action(async () => { try { const { client, connection } = (0, client_1.getClient)(); const info = await client.user().getInfo(); (0, format_1.output)({ server: connection.server, profile: connection.profile, user: info }, () => `服务器: ${connection.server}\n` + `用户信息:\n${(0, format_1.formatKeyValue)(info)}`); } catch (error) { (0, format_1.fatal)(error); } }); // ============ auth (子命令组) ============ const auth = program.command('auth').description('凭证 profile 管理'); auth.command('list') .description('列出所有已保存的 profile') .action(() => { try { const current = (0, credentials_1.getCurrentProfile)()?.name; const names = (0, credentials_1.listProfiles)(); if (names.length === 0) { (0, format_1.output)({ profiles: [] }, () => '暂无保存的 profile'); return; } const rows = names.map(name => { const p = (0, credentials_1.getProfile)(name); return { profile: name, current: name === current ? '*' : '', server: p.server, username: p.username || '', updated: p.updatedAt || '', }; }); (0, format_1.output)({ profiles: rows }, () => (0, format_1.formatTable)(rows)); } catch (error) { (0, format_1.fatal)(error); } }); auth.command('use <name>') .description('切换当前激活的 profile') .action((name) => { try { if (!(0, credentials_1.setCurrent)(name)) { throw new Error(`profile "${name}" 不存在`); } (0, format_1.success)(`已切换到 profile: ${name}`); } catch (error) { (0, format_1.fatal)(error); } }); auth.command('add <name>') .description('手动添加一个 profile(server/token 不填则交互式输入,或用全局 -s/--token)') .option('-u, --username <username>', '用户名(可选)') .action(async (name, options) => { try { // server/token 优先取全局 --server / --token,其次交互式输入 const global = program.opts(); const server = global.server || (await prompt('服务器地址: ')); const token = global.token || (await prompt('访问令牌: ')); const username = options.username; if (!server || !token) { throw new Error('服务器地址和访问令牌不能为空'); } (0, credentials_1.saveProfile)(name, { server, token, username }); (0, format_1.success)(`已添加 profile: ${name} (${server})`); } catch (error) { (0, format_1.fatal)(error); } }); auth.command('remove <name>') .alias('rm') .description('删除一个 profile') .action((name) => { try { if (!(0, credentials_1.removeProfile)(name)) { throw new Error(`profile "${name}" 不存在`); } (0, format_1.success)(`已删除 profile: ${name}`); } catch (error) { (0, format_1.fatal)(error); } }); } //# sourceMappingURL=auth.js.map