fix(auth): 微信登录按钮放大+主题色 & /api/auth/aggregate 500 诊断日志\n\nUI 改动(微信登录按钮 + 个人页绑定卡片):\n- OAuthButtons.vue:aggregate 微信按钮 48x48 → 56x56,SVG 图标 22px→28px;默认背景微信绿 #07C160,hover #06ad56,active #059a4c;正常态加绿色阴影,纯 #fff 文字;不再和相邻 Google/GitHub 保持原尺寸不变\n- OAuthBindingCard.vue 个人页卡片:aggregate(aggregate):微信图标框 40x40→48x48(h-10→h-12),图标 22px→26px,背景/边框/文字用微信绿 #07C160;未绑定微信色 立即绑定微信 按钮 字号从 xs→sm,py 1.5→2.5,文案 立即绑定→立即绑定微信\n\n500 排错日志(/api/auth/aggregate):\n- loadAggregateSettings:先取 env fallback 前置,DB select 包 try/catch 不抛;并打印 [AggregateOAuth] enabled/appId/appKey 长度/loginType/endpoint 状态(DB=? 配置 / ENV=? 配置 / dbEnabled回退逻辑\n- auxxStrategy.getAuthorizeUrl 全链路结构化日志:\n 1) 参数打印(enabled/endpoint/safe_ok/redirect_uri/state_len\n 2) 配置缺失时逐条枚举原因(APP_ID 空 / APP_KEY 空 / ENDPOINT 空 / enabled=false)\n 3) endpoint 不合法时单独打印 [AggregateOAuth][auxx][getAuthorizeUrl] endpoint 不合法 + JSON(endpoint)\n 4) 请求 auxx.cn/connect.php?act=login 前打印 GET URL(appkey 替换为 ***,日志绝不输出真实密钥)\n 5) $fetch 成功打印 code/msg/has_url/url_prefix\n 6) $fetch 异常打印 err/status/body (response._data 或 data 前 400 字)\n 7) $fetch 加 15000ms 超时,避免 Runner 到 auxx 不通卡死一直挂 30s\n 以上;30s+ 才被 Nitro 强制 500
构建并部署 BYW VoiceHub / Preflight 语法与构建检查 (push) Successful in 2m13s
构建并部署 BYW VoiceHub / 构建并推送容器镜像 (push) Successful in 10m1s
构建并部署 BYW VoiceHub / 部署到生产服务器 (push) Successful in 6s

This commit is contained in:
2026-09-13 01:05:38 +08:00
parent 9fd8a9ccbd
commit a481e0ae4c
3 changed files with 116 additions and 32 deletions
+89 -22
View File
@@ -259,23 +259,34 @@ const strategies: Record<string, OAuthStrategy> = {
}
async function loadAggregateSettings() {
const row = await db
.select({
aggregateOAuthEnabled: systemSettings.aggregateOAuthEnabled,
aggregateOAuthAppId: systemSettings.aggregateOAuthAppId,
aggregateOAuthAppKey: systemSettings.aggregateOAuthAppKey,
aggregateOAuthLoginType: systemSettings.aggregateOAuthLoginType,
aggregateOAuthEndpoint: systemSettings.aggregateOAuthEndpoint
})
.from(systemSettings)
.orderBy(asc(systemSettings.id))
.limit(1)
const base = row[0] ?? null
const envAppId = process.env.AGGREGATE_OAUTH_APP_ID
const envAppKey = process.env.AGGREGATE_OAUTH_APP_KEY
const envLoginType = process.env.AGGREGATE_OAUTH_LOGIN_TYPE
const envEndpoint = process.env.AGGREGATE_OAUTH_ENDPOINT
const hasEnvConfig = !!(envAppId && envAppKey && envEndpoint)
let base: any = null
try {
const row = await db
.select({
aggregateOAuthEnabled: systemSettings.aggregateOAuthEnabled,
aggregateOAuthAppId: systemSettings.aggregateOAuthAppId,
aggregateOAuthAppKey: systemSettings.aggregateOAuthAppKey,
aggregateOAuthLoginType: systemSettings.aggregateOAuthLoginType,
aggregateOAuthEndpoint: systemSettings.aggregateOAuthEndpoint
})
.from(systemSettings)
.orderBy(asc(systemSettings.id))
.limit(1)
base = row[0] ?? null
} catch (e: any) {
console.warn(
'[AggregateOAuth][WARN] loadAggregateSettings 查询 DB 失败,回退只用 process.env 配置。' +
` DB 错误:${e?.message || String(e)}。 envAppId=${envAppId ? '已配置(len=' + String(envAppId).length + ')' : '空'} ` +
` envEndpoint=${envEndpoint ? '已配置(' + envEndpoint + ')' : '空'}`
)
}
const dbEnabled = base?.aggregateOAuthEnabled
const dbHasConfig = !!(
base?.aggregateOAuthAppId &&
@@ -288,7 +299,7 @@ async function loadAggregateSettings() {
const loginType = ((base?.aggregateOAuthLoginType as string) || envLoginType || '') as string
const endpoint = ((base?.aggregateOAuthEndpoint as string) || envEndpoint || '') as string
return {
const result: any = {
aggregateOAuthEnabled: enabled && !!(appId && appKey && endpoint),
aggregateOAuthAppId: appId,
aggregateOAuthAppKey: appKey,
@@ -296,7 +307,19 @@ async function loadAggregateSettings() {
aggregateOAuthEndpoint: endpoint,
_dbHasConfig: dbHasConfig,
_hasEnvConfig: hasEnvConfig
} as any
}
console.log(
'[AggregateOAuth] loadAggregateSettings -> ' +
` enabled=${result.aggregateOAuthEnabled}` +
` appId=${appId ? 'len=' + String(appId).length : 'EMPTY'}` +
` appKey=${appKey ? 'len=' + String(appKey).length : 'EMPTY'}` +
` loginType=${loginType || 'EMPTY'}` +
` endpoint=${endpoint || 'EMPTY'}` +
` (DB=${dbHasConfig ? '有' : '无'}配置, ENV=${hasEnvConfig ? '有' : '无'}配置, dbEnabled=${dbEnabled ?? '未设置->回退ENV'})`
)
return result
}
function normalizeEndpoint(endpoint: string): string {
@@ -321,34 +344,78 @@ function isAuxxEndpoint(endpoint: string): boolean {
const auxxStrategy: OAuthStrategy = {
async getAuthorizeUrl(redirectUri: string, state: string) {
const settings = await loadAggregateSettings()
console.log(
'[AggregateOAuth][auxx][getAuthorizeUrl] ' +
`enabled=${settings?.aggregateOAuthEnabled}` +
` endpoint=${settings?.aggregateOAuthEndpoint || 'EMPTY'}` +
` endpoint_safe_ok=${isSafeAggregateOAuthUrl(settings?.aggregateOAuthEndpoint || '')}` +
` redirect_uri=${redirectUri}` +
` state_len=${String(state || '').length}`
)
if (!settings?.aggregateOAuthEnabled || !settings.aggregateOAuthAppId || !settings.aggregateOAuthAppKey || !settings.aggregateOAuthEndpoint) {
throw createError({ statusCode: 500, message: '聚合登录未配置' })
const hint = []
if (!settings?.aggregateOAuthEnabled) hint.push('aggregateOAuthEnabled=false')
if (!settings?.aggregateOAuthAppId) hint.push('APP_ID 空')
if (!settings?.aggregateOAuthAppKey) hint.push('APP_KEY 空')
if (!settings?.aggregateOAuthEndpoint) hint.push('ENDPOINT 空')
console.error('[AggregateOAuth][auxx][getAuthorizeUrl] 配置缺失:', hint.join(' | '))
throw createError({ statusCode: 500, message: '聚合登录未配置:' + hint.join(' / ') })
}
if (!isSafeAggregateOAuthUrl(settings.aggregateOAuthEndpoint)) {
throw createError({ statusCode: 500, message: '聚合登录端点不安全或格式错误' })
console.error(
'[AggregateOAuth][auxx][getAuthorizeUrl] endpoint 不合法,' +
'isSafeAggregateOAuthUrl 返回 false。endpoint=' +
JSON.stringify(settings.aggregateOAuthEndpoint)
)
throw createError({
statusCode: 500,
message:
'聚合登录端点不安全或格式错误:endpoint 必须是 https://xxx 或内网 http://localhost,当前值=' +
settings.aggregateOAuthEndpoint
})
}
const loginTypes = getAggregateOAuthLoginTypesOrDefault(settings.aggregateOAuthLoginType)
const loginType = loginTypes[0] || 'qq'
const endpoint = normalizeEndpoint(settings.aggregateOAuthEndpoint)
const loginQuery = new URLSearchParams({
const loginQueryObj: Record<string, string> = {
act: 'login',
appid: settings.aggregateOAuthAppId,
appkey: settings.aggregateOAuthAppKey,
type: loginType,
redirect_uri: redirectUri
}).toString()
}
const loginQuery = new URLSearchParams(loginQueryObj).toString()
const debugQueryNoKey = new URLSearchParams({ ...loginQueryObj, appkey: '***' }).toString()
console.log(
`[AggregateOAuth][auxx][getAuthorizeUrl] 请求 auxx Step1:GET ${endpoint}/connect.php?${debugQueryNoKey}`
)
try {
const resp: any = await $fetch(`${endpoint}/connect.php?${loginQuery}`, {
headers: { Accept: 'application/json' }
headers: { Accept: 'application/json' },
timeout: 15000
})
console.log(
'[AggregateOAuth][auxx][getAuthorizeUrl] auxx Step1 返回:' +
JSON.stringify({
code: resp?.code,
msg: resp?.msg,
has_url: !!resp?.url,
url_prefix: resp?.url ? String(resp.url).slice(0, 80) + '...' : null
})
)
if (resp.code !== 0 || !resp.url) {
throw new Error(resp.msg || '获取登录跳转地址失败')
throw new Error(resp.msg || `获取登录跳转地址失败(auxx code=${resp?.code ?? 'N/A'})`)
}
const sep = resp.url.includes('?') ? '&' : '?'
return `${resp.url}${sep}state=${encodeURIComponent(state)}`
} catch (e: any) {
console.error('Auxx OAuth authorize URL failed', e)
throw new Error(e.message || '获取登录跳转地址失败')
const status = e?.response?.status || e?.status || 'N/A'
const body = e?.response?._data ? JSON.stringify(e.response._data).slice(0, 400) : e?.data ? JSON.stringify(e.data).slice(0, 400) : null
console.error(
'[AggregateOAuth][auxx][getAuthorizeUrl] fetch 异常:' +
` err=${e?.message || String(e)} status=${status} body=${body}`
)
throw new Error(e.message || '获取登录跳转地址失败(auxx 不可达或凭据错误)')
}
},
async exchangeToken(code: string, redirectUri: string) {