mira-app-server
Version:
Mira Server - standalone server application using mira-app-core
294 lines • 11.7 kB
JavaScript
;
/**
* 认证与凭证命令
*
* - 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