/** * 登录并保存会话,供页面渲染验证脚本复用。 * * 为什么不 stub 接口:MEDISIGN 后端对无效 token 返回 401, * utils/request.ts 的响应拦截器收到 401 会 clearAuthStorage() + 跳登录页, * 伪造会话很容易被踢出去。走真实登录最省事。 * * 验证码是强制的(不传会报「验证码错误或已过期」)。用法: * * node scripts/login.cjs # 第一步:抓验证码图片 * (用看图工具认出图片里的数字) * node scripts/login.cjs 5274 # 第二步:带验证码登录,写 .session.json * * 结果写到 scripts/.session.json(已在 .gitignore 里,不会提交 token)。 * * 文件用 .cjs 后缀:package.json 里的 "type": "module" 会把 .js 当 ES 模块, * 而这里用 require 更简单,所以显式声明成 CommonJS。 * * 依赖:Node 18+(内置 fetch)。无需额外安装依赖。 */ const fs = require('fs') const path = require('path') const BASE_URL = process.env.VERIFY_BASE_URL || 'http://127.0.0.1:5173' const USERNAME = process.env.VERIFY_USERNAME || 'admin' const PASSWORD = process.env.VERIFY_PASSWORD || 'admin' const OUT_DIR = __dirname const CAPTCHA_JSON = path.join(OUT_DIR, '.captcha.json') const CAPTCHA_PNG = path.join(OUT_DIR, '.captcha.png') const SESSION_FILE = path.join(OUT_DIR, '.session.json') async function fetchCaptcha() { const res = await fetch(`${BASE_URL}/api/v1/auth/captcha`) const json = await res.json() if (json.code !== '0' && json.code !== 0) { throw new Error('获取验证码失败: ' + json.message) } const { captchaId, imageBase64 } = json.data fs.writeFileSync(CAPTCHA_JSON, JSON.stringify(json)) fs.writeFileSync( CAPTCHA_PNG, Buffer.from(String(imageBase64).replace(/^data:image\/\w+;base64,/, ''), 'base64'), ) console.log('验证码已保存,请打开图片认出里面的数字:') console.log(' ' + CAPTCHA_PNG) console.log('captchaId = ' + captchaId) console.log('') console.log('然后执行: node scripts/login.cjs <识别出的数字>') } async function login(captchaCode) { if (!fs.existsSync(CAPTCHA_JSON)) { throw new Error('没有找到验证码,请先执行 node scripts/login.cjs 抓取验证码') } const { data } = JSON.parse(fs.readFileSync(CAPTCHA_JSON, 'utf8')) const res = await fetch(`${BASE_URL}/api/v1/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: USERNAME, password: PASSWORD, captchaId: data.captchaId, captchaCode, }), }) const json = await res.json() if (json.code !== '0' && json.code !== 0) { throw new Error( '登录失败: ' + json.message + '(验证码可能已过期,重新执行 node scripts/login.cjs)', ) } fs.writeFileSync(SESSION_FILE, JSON.stringify(json.data, null, 2)) console.log('登录成功') console.log( ' 用户 = ' + json.data.user.displayName + ' (' + json.data.user.username + ')', ) console.log(' campusId = ' + json.data.user.campusId) // 登录响应里 dataScope / permissions 可能缺失:权限以 /v1/authorization/me 为准。 console.log(' dataScope = ' + (json.data.user.dataScope ?? '(未返回)')) console.log(' permissions = ' + (json.data.user.permissions?.length ?? 0) + ' 项') console.log(' 会话已写入 ' + SESSION_FILE) } async function main() { const captchaCode = process.argv[2] if (captchaCode) { await login(captchaCode.trim()) } else { await fetchCaptcha() } } main().catch((err) => { console.error(err.message) process.exit(1) })