Files
Rogee ea6811b295
Build and push backend image / backend-image (push) Successful in 45s
fix: use verified WeChat login and recover sessions after cache clearing
2026-09-26 18:47:38 +08:00

185 lines
6.0 KiB
Plaintext

import { apiBase, post } from './api.uts'
import { User } from './types.uts'
const USER_KEY = 'sf_user'
let loginPending: Promise<any> | null = null
let sessionReady = false
export function loginUser(): Promise<any> {
if (loginPending != null) return loginPending as Promise<any>
sessionReady = false
uni.removeStorageSync(USER_KEY)
loginPending = new Promise<any>((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (res) => {
const code = (res as any).code as string
if (code == null || code.length == 0) { reject(new Error('微信登录未返回 code')); return }
post('/api/auth/wechat/session', { code }).then((session) => {
const user = (session as any).user as User
openidFromUser(user)
setUser(user)
sessionReady = true
resolve(user)
}).catch(reject)
},
fail: (err) => reject(new Error(err.errMsg))
})
}).catch((err) => {
console.error('[login] failed', err)
return Promise.reject(err)
}).finally(() => { loginPending = null })
return loginPending as Promise<any>
}
export function ensureLogin(): Promise<any> {
const user = currentUser()
if (user != null) return Promise.resolve(user)
if (sessionReady) {
console.warn('[login] cached user missing; reauthenticating')
sessionReady = false
}
return loginUser()
}
export function currentUser(): User | null {
if (!sessionReady) return null
const raw = uni.getStorageSync(USER_KEY)
if (raw == null || raw == '') return null
return raw as User
}
export function phoneFromUser(user: any | null): string {
if (user == null) return ''
const phone = (user as any).phone
if (phone == null) return ''
if (typeof phone == 'string') return phone as string
const obj = phone as any
if (obj.Valid == true || obj.valid == true) {
if (obj.String != null) return obj.String as string
if (obj.string != null) return obj.string as string
}
return ''
}
export function openidFromUser(user: any | null): string {
if (user == null) throw new Error('请先完成微信登录')
const obj = user as any
const value = obj.openid != null ? obj.openid : obj.Openid
const openid = value == null ? '' : value as string
if (openid.length == 0 || openid.indexOf('demo_openid_') == 0 || openid.indexOf('fake_user_') == 0) {
throw new Error('登录身份已失效,请重新登录')
}
return openid
}
export function setUser(user: User): void {
uni.setStorageSync(USER_KEY, user)
}
export function bindPhoneFromEvent(event: UniEvent): Promise<any> {
const detail = (event as any).detail as any
const errMsg = detail != null && detail.errMsg != null ? detail.errMsg as string : ''
console.log('[phone-auth] getPhoneNumber event', phoneAuthDetailLog(detail))
if (errMsg.indexOf('ok') < 0) {
console.log('[phone-auth] getPhoneNumber rejected', { errMsg })
return Promise.reject(new Error('手机号授权未完成'))
}
const phoneCode = detail != null && detail.code != null ? detail.code as string : ''
if (phoneCode.length == 0) {
console.log('[phone-auth] getPhoneNumber missing code', phoneAuthDetailLog(detail))
return Promise.reject(new Error('没有拿到手机号授权 code'))
}
return ensureLogin().then((user) => {
const openid = openidFromUser(user)
console.log('[phone-auth] backend bind start', {
openid: maskIdentifier(openid),
hasStoredPhone: phoneFromUser(user).length > 0,
phoneCodeLength: phoneCode.length,
envVersion: miniProgramEnvVersion(),
apiBase: apiBase()
})
return post('/api/auth/wechat/phone', {
openid,
phoneCode
}).then((res) => {
const nextUser = (res as any).user as User
const phone = phoneFromUser(nextUser)
if (phone.length == 0) {
console.log('[phone-auth] backend bind empty phone', {
openid: maskIdentifier(openidFromUser(nextUser))
})
return Promise.reject(new Error('后端未返回已绑定手机号'))
}
setUser(nextUser)
console.log('[phone-auth] backend bind success', {
openid: maskIdentifier(openidFromUser(nextUser)),
phone: maskPhoneForLog(phone)
})
return {
user: nextUser,
phone
}
}).catch((err) => {
const message = err != null && (err as any).message != null ? (err as any).message as string : '手机号绑定失败'
console.log('[phone-auth] backend bind failed', {
openid: maskIdentifier(openid),
message
})
return Promise.reject(err)
})
})
}
function phoneAuthDetailLog(detail: any | null): any {
if (detail == null) {
return {
errMsg: '',
hasCode: false,
codeLength: 0,
hasEncryptedData: false,
hasIv: false,
hasCloudID: false
}
}
const code = detail.code != null ? detail.code as string : ''
const encryptedData = detail.encryptedData != null ? detail.encryptedData as string : ''
const iv = detail.iv != null ? detail.iv as string : ''
const cloudID = detail.cloudID != null ? detail.cloudID as string : ''
return {
errMsg: detail.errMsg != null ? detail.errMsg as string : '',
hasCode: code.length > 0,
codeLength: code.length,
hasEncryptedData: encryptedData.length > 0,
hasIv: iv.length > 0,
hasCloudID: cloudID.length > 0
}
}
function maskIdentifier(value: string): string {
if (value.length <= 8) return value
return value.slice(0, 4) + '...' + value.slice(value.length - 4)
}
function maskPhoneForLog(phone: string): string {
if (phone.length < 7) return phone
return phone.slice(0, 3) + '****' + phone.slice(phone.length - 4)
}
function miniProgramEnvVersion(): string {
// #ifdef MP-WEIXIN
try {
const accountInfo = uni.getAccountInfoSync()
const info = accountInfo as any
if (info.miniProgram == null) return ''
const miniProgram = info.miniProgram as any
if (miniProgram.envVersion == null) return ''
return miniProgram.envVersion as string
} catch (e) {
return ''
}
// #endif
return ''
}