Files
workspace/code/g3soft-erp/scripts/dev-login.mjs
T
2026-10-14 17:25:47 +08:00

104 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
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.
#!/usr/bin/env node
/**
* 开发期取 token —— 给 AI / 脚本 / 命令行用,不必开浏览器点登录。
*
* 用法(在仓库根目录执行):
* node scripts/dev-login.mjs # 用 web/.env.development 里的账号
* node scripts/dev-login.mjs G3HD g3soft # 指定机构码 + 账号
* node scripts/dev-login.mjs G3HD g3soft pwd # 再指定密码
* node scripts/dev-login.mjs --token-only # 只输出 token 一行,便于 $(...) 取值
* node scripts/dev-login.mjs --target http://127.0.0.1:8088
*
* 直接打后端(默认 127.0.0.1:8088),不经过 Vite 代理。
*
* ⚠️ 同一账号重复登录会互踢(后端 ActiveSessionRegistry,业务码 40101):
* 这个脚本每跑一次就会把该账号上一个会话踢下线。别在别人正用同一账号时跑。
*/
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
const ROOT = fileURLToPath(new URL('..', import.meta.url))
const ENV_FILE = path.join(ROOT, 'web', '.env.development')
/* ---------------- 读取 web/.env.development 作为默认值 ---------------- */
function readEnvValue(key) {
try {
for (const line of readFileSync(ENV_FILE, 'utf8').split(/\r?\n/)) {
const match = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line)
if (match && match[1] === key) {
return match[2].trim().replace(/^(['"])(.*)\1$/, '$2')
}
}
} catch {
// 文件不存在:回落到内置默认值,下面还会打印提示
}
return ''
}
/* ---------------- 参数 ---------------- */
const positional = []
let target = ''
let tokenOnly = false
const args = process.argv.slice(2)
for (let i = 0; i < args.length; i += 1) {
if (args[i] === '--target') {
target = args[i + 1] ?? ''
i += 1
} else if (args[i] === '--token-only') {
tokenOnly = true
} else {
positional.push(args[i])
}
}
const base = (target || process.env.VITE_PROXY_TARGET || 'http://127.0.0.1:8088').replace(/\/+$/, '')
const orgId = positional[0] || readEnvValue('VITE_DEV_ORG') || 'G3HD'
const account = positional[1] || readEnvValue('VITE_DEV_ACCOUNT') || 'g3soft'
const password = positional[2] ?? readEnvValue('VITE_DEV_PASSWORD')
/* ---------------- 登录 ---------------- */
let body
try {
const response = await fetch(`${base}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orgid: orgId, userid: account, password }),
})
body = await response.json()
} catch (error) {
console.error(`连不上后端 ${base}:${error.message}`)
console.error('先在 server/ 下起后端(legacy TLS 用这条):')
console.error(' mvn "-Dspring-boot.run.jvmArguments=-Djava.security.properties=./config/legacy-tls.security" spring-boot:run')
process.exit(1)
}
/* 后端鉴权失败返回 HTTP 200 + body.code=401(不是 HTTP 401),两个都要判。 */
if (body?.code !== 0 || !body?.data?.token) {
console.error(`登录失败(code=${body?.code}):${body?.message || '响应里没有 token'}`)
console.error(`用的账号:机构码 ${orgId} / 账号 ${account}${password ? ' / 密码已填' : ' / 密码为空'}`)
console.error('账号密码来自 web/.env.development 的 VITE_DEV_*,也可命令行覆盖。')
console.error('超管 g3soft 的密码在库里是空串;若你的库不是,传第三个参数指定密码。')
process.exit(1)
}
const { token, user } = body.data
/* ---------------- 输出 ---------------- */
if (tokenOnly) {
process.stdout.write(`${token}\n`)
} else {
console.log(`已登录:${orgId}/${account}(${user?.name || '未命名'})`)
console.log('')
console.log('token:')
console.log(token)
console.log('')
console.log('直接调业务接口(浏览器里不用这个,开发期页面已自动登录):')
console.log(` curl -H "Authorization: Bearer ${token}" ${base}/api/data/loaddata`)
}