web-view 不能直接打开 open.weixin.qq.com,移动端改为打开已配置的业务域名中转页。 Co-authored-by: Cursor <cursoragent@cursor.com>
70 lines
3.1 KiB
JavaScript
70 lines
3.1 KiB
JavaScript
import { resolveApiBase, resolveWebOrigin } from '@/config/api'
|
|
import { request, parseResponseJson } from '@/utils/request'
|
|
|
|
const LAUNCH_PATH = '/api/public/wechat-notices/oauth/launch'
|
|
|
|
function httpsHost(raw) {
|
|
const match = String(raw || '').match(/^https:\/\/([^/?#]+)/i)
|
|
return match ? match[1].toLowerCase() : ''
|
|
}
|
|
|
|
export async function fetchWechatNoticeStatus() {
|
|
const response = await request({
|
|
url: '/web/wechat-notices/binding',
|
|
method: 'GET',
|
|
timeout: 15000
|
|
})
|
|
const result = parseResponseJson(response)
|
|
const data = result?.data
|
|
if (
|
|
response.statusCode !== 200 || result?.code !== 200 || !data ||
|
|
['enabled', 'bound', 'subscribed'].some((key) => typeof data[key] !== 'boolean')
|
|
) {
|
|
throw new Error('暂时无法确认服务号关注状态')
|
|
}
|
|
return { enabled: data.enabled, bound: data.bound, subscribed: data.subscribed }
|
|
}
|
|
|
|
async function bindingRequest(method, path, data) {
|
|
const response = await request({ url: `/web/wechat-notices/binding${path}`, method, data, timeout: 15000 })
|
|
const result = parseResponseJson(response)
|
|
if (response.statusCode !== 200 || result?.code !== 200 || !result?.data) {
|
|
throw new Error(typeof result?.message === 'string' ? result.message : '绑定操作失败,请稍后重试')
|
|
}
|
|
return result.data
|
|
}
|
|
|
|
export function isWechatAuthorizeUrl(value) {
|
|
// web-view 只能打开业务域名。这里只接受门户上的授权中转页,不直接打开 open.weixin.qq.com。
|
|
if (typeof value !== 'string' || value.length > 300) return false
|
|
const match = value.match(/^https:\/\/([^/?#@]+)(\/[^?#]*)\?state=([A-Za-z0-9_-]{20,100})$/)
|
|
if (!match || match[2] !== LAUNCH_PATH) return false
|
|
const allowed = new Set([httpsHost(resolveWebOrigin()), httpsHost(resolveApiBase())].filter(Boolean))
|
|
return allowed.has(match[1].toLowerCase())
|
|
}
|
|
|
|
export async function startWechatBinding() {
|
|
const data = await bindingRequest('POST', '/start', { mode: 'mobile' })
|
|
if (!/^[A-Za-z0-9_-]{20,100}$/.test(data.sessionId || '') || !isWechatAuthorizeUrl(data.authorizeUrl)) {
|
|
throw new Error('授权地址无效,请稍后重试')
|
|
}
|
|
return { sessionId: data.sessionId, authorizeUrl: data.authorizeUrl }
|
|
}
|
|
|
|
export async function fetchWechatBindingSession(sessionId) {
|
|
const data = await bindingRequest('GET', `/session?sessionId=${encodeURIComponent(sessionId)}`)
|
|
if (!['waiting', 'scanned', 'confirmed', 'expired'].includes(data.status) || typeof data.subscribed !== 'boolean' ||
|
|
(data.status === 'scanned' && !/^\d{6}$/.test(data.verificationCode || ''))) {
|
|
throw new Error('授权状态异常,请重新绑定')
|
|
}
|
|
return { status: data.status, subscribed: data.subscribed, verificationCode: data.verificationCode || '' }
|
|
}
|
|
|
|
export async function confirmWechatBinding(sessionId) {
|
|
const data = await bindingRequest('POST', '/confirm', { sessionId })
|
|
if (['enabled', 'bound', 'subscribed'].some(key => typeof data[key] !== 'boolean')) {
|
|
throw new Error('绑定结果异常,请刷新状态')
|
|
}
|
|
return { enabled: data.enabled, bound: data.bound, subscribed: data.subscribed }
|
|
}
|