mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-06-18 15:54:08 +08:00
refactor(frontend-api): 统一 useApiBase 并移除 localhost:8000 硬编码
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<div>
|
||||
<h4 class="text-red-800 font-semibold">API连接问题</h4>
|
||||
<p class="text-red-700 text-sm mt-1">{{ appStore.apiMessage || '无法连接到后端服务' }}</p>
|
||||
<p class="text-red-600 text-xs mt-2">请确保后端服务正在运行 (端口: 8000)</p>
|
||||
<p class="text-red-600 text-xs mt-2">请确保后端服务正在运行</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,4 +18,4 @@
|
||||
import { useAppStore } from '~/stores/app'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -101,13 +101,13 @@ const props = defineProps({
|
||||
message: { type: Object, default: null },
|
||||
})
|
||||
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
|
||||
const normalizeMaybeUrl = (u) => {
|
||||
const raw = String(u || '').trim()
|
||||
if (!raw) return ''
|
||||
if (/^https?:\/\//i.test(raw) || /^blob:/i.test(raw) || /^data:/i.test(raw)) return raw
|
||||
if (/^\/api\//i.test(raw)) return `${mediaBase}${raw}`
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
return raw
|
||||
}
|
||||
|
||||
|
||||
@@ -137,8 +137,7 @@ const topGroup = computed(() => {
|
||||
return o && typeof o === 'object' && typeof o.displayName === 'string' ? o : null
|
||||
})
|
||||
|
||||
// Keep the same behavior as the chat page: media (including avatars) comes from backend :8000 in dev.
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const resolveMediaUrl = (value) => {
|
||||
const raw = String(value || '').trim()
|
||||
if (!raw) return ''
|
||||
@@ -147,13 +146,13 @@ const resolveMediaUrl = (value) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
}
|
||||
// Most backend fields are like "/api/...", so just prefix.
|
||||
return `${mediaBase}${raw.startsWith('/') ? '' : '/'}${raw}`
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
return raw.startsWith('/') ? raw : `/${raw}`
|
||||
}
|
||||
|
||||
const topContactAvatarUrl = computed(() => {
|
||||
|
||||
@@ -308,7 +308,7 @@ const indexBuild = computed(() => {
|
||||
})
|
||||
|
||||
// Media URL resolving (same behavior as other wrapped components)
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const resolveMediaUrl = (value) => {
|
||||
const raw = String(value || '').trim()
|
||||
if (!raw) return ''
|
||||
@@ -316,12 +316,13 @@ const resolveMediaUrl = (value) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
}
|
||||
return `${mediaBase}${raw.startsWith('/') ? '' : '/'}${raw}`
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
return raw.startsWith('/') ? raw : `/${raw}`
|
||||
}
|
||||
|
||||
const avatarFallback = (name) => {
|
||||
|
||||
@@ -270,7 +270,7 @@ const props = defineProps({
|
||||
const nfInt = new Intl.NumberFormat('zh-CN', { maximumFractionDigits: 0 })
|
||||
const formatInt = (n) => nfInt.format(Math.round(Number(n) || 0))
|
||||
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const resolveMediaUrl = (value, opts = { backend: false }) => {
|
||||
const raw = String(value || '').trim()
|
||||
if (!raw) return ''
|
||||
@@ -278,13 +278,15 @@ const resolveMediaUrl = (value, opts = { backend: false }) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
}
|
||||
if (opts.backend || raw.startsWith('/api/')) {
|
||||
return `${mediaBase}${raw.startsWith('/') ? '' : '/'}${raw}`
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
if (opts.backend) {
|
||||
const origin = apiBase.endsWith('/api') ? apiBase.slice(0, -4) : apiBase
|
||||
return `${origin}${raw.startsWith('/') ? '' : '/'}${raw}`
|
||||
}
|
||||
return raw.startsWith('/') ? raw : `/${raw}`
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ const formatScore = (n) => {
|
||||
}
|
||||
const clampPct = (n) => Math.max(0, Math.min(100, Math.round(Number(n || 0) * 100)))
|
||||
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const resolveMediaUrl = (value) => {
|
||||
const raw = String(value || '').trim()
|
||||
if (!raw) return ''
|
||||
@@ -174,12 +174,13 @@ const resolveMediaUrl = (value) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
}
|
||||
return `${mediaBase}${raw.startsWith('/') ? '' : '/'}${raw}`
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
return raw.startsWith('/') ? raw : `/${raw}`
|
||||
}
|
||||
|
||||
const avatarFallback = (name) => {
|
||||
|
||||
@@ -944,6 +944,8 @@ const formatDurationZh = (seconds) => {
|
||||
return h > 0 ? `${d}天${h}小时` : `${d}天`
|
||||
}
|
||||
|
||||
const apiBase = useApiBase()
|
||||
|
||||
const resolveMediaUrl = (value, opts = { backend: false }) => {
|
||||
const raw = String(value || '').trim()
|
||||
if (!raw) return ''
|
||||
@@ -952,13 +954,12 @@ const resolveMediaUrl = (value, opts = { backend: false }) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
// Keep same-origin `/api` so Nuxt devProxy / backend-mounted UI both work.
|
||||
return `/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
}
|
||||
// Keep `/api/...` as same-origin (avoid hardcoding backend host like `localhost:8000`).
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
return raw.startsWith('/') ? raw : `/${raw}`
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,7 @@ const onAvatarError = () => { avatarOk.value = false }
|
||||
|
||||
const displayNameShown = computed(() => String(props.displayName || props.maskedName || '').trim())
|
||||
|
||||
// Keep the same behavior as the chat page: media (including avatars) comes from backend :8000 in dev.
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const resolveMediaUrl = (value) => {
|
||||
const raw = String(value || '').trim()
|
||||
if (!raw) return ''
|
||||
@@ -88,13 +87,13 @@ const resolveMediaUrl = (value) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
}
|
||||
// Most backend fields are like "/api/...", so just prefix.
|
||||
return `${mediaBase}${raw.startsWith('/') ? '' : '/'}${raw}`
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
return raw.startsWith('/') ? raw : `/${raw}`
|
||||
}
|
||||
|
||||
const resolvedAvatarUrl = computed(() => resolveMediaUrl(props.avatarUrl))
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
// API请求组合式函数
|
||||
export const useApi = () => {
|
||||
const config = useRuntimeConfig()
|
||||
const baseURL = useApiBase()
|
||||
|
||||
// 基础请求函数
|
||||
const request = async (url, options = {}) => {
|
||||
try {
|
||||
// Default to same-origin `/api` so Nuxt devProxy / backend-mounted UI both work.
|
||||
// Override via `NUXT_PUBLIC_API_BASE`, e.g. `http://127.0.0.1:8000/api`.
|
||||
const apiBase = String(config?.public?.apiBase || '').trim()
|
||||
const baseURL = (apiBase ? apiBase : '/api').replace(/\/$/, '')
|
||||
|
||||
const response = await $fetch(url, {
|
||||
baseURL,
|
||||
...options,
|
||||
|
||||
@@ -3630,8 +3630,8 @@ const startExportPolling = (exportId) => {
|
||||
if (!exportId) return
|
||||
|
||||
if (process.client && typeof window !== 'undefined' && typeof EventSource !== 'undefined') {
|
||||
const base = 'http://localhost:8000'
|
||||
const url = `${base}/api/chat/exports/${encodeURIComponent(String(exportId))}/events`
|
||||
const apiBase = useApiBase()
|
||||
const url = `${apiBase}/chat/exports/${encodeURIComponent(String(exportId))}/events`
|
||||
try {
|
||||
exportEventSource = new EventSource(url)
|
||||
exportEventSource.onmessage = (ev) => {
|
||||
@@ -3889,8 +3889,8 @@ watch(
|
||||
)
|
||||
|
||||
const getExportDownloadUrl = (exportId) => {
|
||||
const base = process.client ? 'http://localhost:8000' : ''
|
||||
return `${base}/api/chat/exports/${encodeURIComponent(String(exportId || ''))}/download`
|
||||
const apiBase = useApiBase()
|
||||
return `${apiBase}/chat/exports/${encodeURIComponent(String(exportId || ''))}/download`
|
||||
}
|
||||
|
||||
const startChatExport = async () => {
|
||||
@@ -6089,7 +6089,7 @@ const normalizeMessage = (msg) => {
|
||||
const sender = isSent ? '我' : (msg.senderDisplayName || msg.senderUsername || selectedContact.value?.name || '')
|
||||
const fallbackAvatar = (!isSent && !selectedContact.value?.isGroup) ? (selectedContact.value?.avatar || null) : null
|
||||
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const normalizeMaybeUrl = (u) => (typeof u === 'string' ? u.trim() : '')
|
||||
const isUsableMediaUrl = (u) => {
|
||||
const v = normalizeMaybeUrl(u)
|
||||
@@ -6121,7 +6121,7 @@ const normalizeMessage = (msg) => {
|
||||
try {
|
||||
const host = new URL(u).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(u)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(u)}`
|
||||
}
|
||||
} catch {}
|
||||
return u
|
||||
@@ -6129,15 +6129,15 @@ const normalizeMessage = (msg) => {
|
||||
|
||||
const fromUsername = String(msg.fromUsername || '').trim()
|
||||
const fromAvatar = fromUsername
|
||||
? `${mediaBase}/api/chat/avatar?account=${encodeURIComponent(selectedAccount.value || '')}&username=${encodeURIComponent(fromUsername)}`
|
||||
? `${apiBase}/chat/avatar?account=${encodeURIComponent(selectedAccount.value || '')}&username=${encodeURIComponent(fromUsername)}`
|
||||
: (() => {
|
||||
// App/web link shares may not provide `fromUsername` (sourceusername), so we don't have a WeChat avatar.
|
||||
// Fall back to a best-effort website favicon fetched via backend.
|
||||
const href = String(msg.url || '').trim()
|
||||
return href ? `${mediaBase}/api/chat/media/favicon?url=${encodeURIComponent(href)}` : ''
|
||||
return href ? `${apiBase}/chat/media/favicon?url=${encodeURIComponent(href)}` : ''
|
||||
})()
|
||||
|
||||
const localEmojiUrl = msg.emojiMd5 ? `${mediaBase}/api/chat/media/emoji?account=${encodeURIComponent(selectedAccount.value || '')}&md5=${encodeURIComponent(msg.emojiMd5)}&username=${encodeURIComponent(selectedContact.value?.username || '')}` : ''
|
||||
const localEmojiUrl = msg.emojiMd5 ? `${apiBase}/chat/media/emoji?account=${encodeURIComponent(selectedAccount.value || '')}&md5=${encodeURIComponent(msg.emojiMd5)}&username=${encodeURIComponent(selectedContact.value?.username || '')}` : ''
|
||||
const localImageUrl = (() => {
|
||||
if (!msg.imageMd5 && !msg.imageFileId) return ''
|
||||
const parts = [
|
||||
@@ -6146,7 +6146,7 @@ const normalizeMessage = (msg) => {
|
||||
msg.imageFileId ? `file_id=${encodeURIComponent(msg.imageFileId)}` : '',
|
||||
`username=${encodeURIComponent(selectedContact.value?.username || '')}`,
|
||||
].filter(Boolean)
|
||||
return `${mediaBase}/api/chat/media/image?${parts.join('&')}`
|
||||
return `${apiBase}/chat/media/image?${parts.join('&')}`
|
||||
})()
|
||||
const normalizedImageUrl = (() => {
|
||||
const cur = (isUsableMediaUrl(msg.imageUrl) ? normalizeMaybeUrl(msg.imageUrl) : '')
|
||||
@@ -6165,7 +6165,7 @@ const normalizeMessage = (msg) => {
|
||||
msg.videoThumbFileId ? `file_id=${encodeURIComponent(msg.videoThumbFileId)}` : '',
|
||||
`username=${encodeURIComponent(selectedContact.value?.username || '')}`,
|
||||
].filter(Boolean)
|
||||
return `${mediaBase}/api/chat/media/video_thumb?${parts.join('&')}`
|
||||
return `${apiBase}/chat/media/video_thumb?${parts.join('&')}`
|
||||
})()
|
||||
|
||||
const localVideoUrl = (() => {
|
||||
@@ -6176,7 +6176,7 @@ const normalizeMessage = (msg) => {
|
||||
msg.videoFileId ? `file_id=${encodeURIComponent(msg.videoFileId)}` : '',
|
||||
`username=${encodeURIComponent(selectedContact.value?.username || '')}`,
|
||||
].filter(Boolean)
|
||||
return `${mediaBase}/api/chat/media/video?${parts.join('&')}`
|
||||
return `${apiBase}/chat/media/video?${parts.join('&')}`
|
||||
})()
|
||||
|
||||
const normalizedVideoThumbUrl = (isUsableMediaUrl(msg.videoThumbUrl) ? normalizeMaybeUrl(msg.videoThumbUrl) : '') || localVideoThumbUrl
|
||||
@@ -6186,7 +6186,7 @@ const normalizeMessage = (msg) => {
|
||||
if (msg.voiceUrl) return msg.voiceUrl
|
||||
if (!serverIdStr) return ''
|
||||
if (String(msg.renderType || '') !== 'voice') return ''
|
||||
return `${mediaBase}/api/chat/media/voice?account=${encodeURIComponent(selectedAccount.value || '')}&server_id=${encodeURIComponent(serverIdStr)}`
|
||||
return `${apiBase}/chat/media/voice?account=${encodeURIComponent(selectedAccount.value || '')}&server_id=${encodeURIComponent(serverIdStr)}`
|
||||
})()
|
||||
|
||||
const remoteFromServer = (
|
||||
@@ -6228,7 +6228,7 @@ const normalizeMessage = (msg) => {
|
||||
const quoteServerIdStr = String(msg.quoteServerId || '').trim()
|
||||
const quoteTypeStr = String(msg.quoteType || '').trim()
|
||||
const quoteVoiceUrl = quoteServerIdStr
|
||||
? `${mediaBase}/api/chat/media/voice?account=${encodeURIComponent(selectedAccount.value || '')}&server_id=${encodeURIComponent(quoteServerIdStr)}`
|
||||
? `${apiBase}/chat/media/voice?account=${encodeURIComponent(selectedAccount.value || '')}&server_id=${encodeURIComponent(quoteServerIdStr)}`
|
||||
: ''
|
||||
const quoteImageUrl = (() => {
|
||||
if (!quoteServerIdStr) return ''
|
||||
@@ -6239,7 +6239,7 @@ const normalizeMessage = (msg) => {
|
||||
`server_id=${encodeURIComponent(quoteServerIdStr)}`,
|
||||
convUsername ? `username=${encodeURIComponent(convUsername)}` : ''
|
||||
].filter(Boolean)
|
||||
return parts.length ? `${mediaBase}/api/chat/media/image?${parts.join('&')}` : ''
|
||||
return parts.length ? `${apiBase}/chat/media/image?${parts.join('&')}` : ''
|
||||
})()
|
||||
const quoteThumbUrl = (() => {
|
||||
const raw = isUsableMediaUrl(msg.quoteThumbUrl) ? normalizeMaybeUrl(msg.quoteThumbUrl) : ''
|
||||
@@ -6249,7 +6249,7 @@ const normalizeMessage = (msg) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
@@ -6744,7 +6744,7 @@ const formatChatHistoryVideoDuration = (value) => {
|
||||
}
|
||||
|
||||
const normalizeChatHistoryRecordItem = (rec) => {
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const account = encodeURIComponent(selectedAccount.value || '')
|
||||
const username = encodeURIComponent(selectedContact.value?.username || '')
|
||||
|
||||
@@ -6768,7 +6768,7 @@ const normalizeChatHistoryRecordItem = (rec) => {
|
||||
})()
|
||||
if (fileId) {
|
||||
previewCandidates.push(
|
||||
`${mediaBase}/api/chat/media/image?account=${account}&file_id=${encodeURIComponent(fileId)}&username=${username}`
|
||||
`${apiBase}/chat/media/image?account=${account}&file_id=${encodeURIComponent(fileId)}&username=${username}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6782,7 +6782,7 @@ const normalizeChatHistoryRecordItem = (rec) => {
|
||||
srcServerId ? `server_id=${encodeURIComponent(srcServerId)}` : '',
|
||||
`username=${username}`
|
||||
].filter(Boolean)
|
||||
previewCandidates.push(`${mediaBase}/api/chat/media/image?${previewParts.join('&')}`)
|
||||
previewCandidates.push(`${apiBase}/chat/media/image?${previewParts.join('&')}`)
|
||||
}
|
||||
|
||||
out._linkPreviewCandidates = previewCandidates
|
||||
@@ -6793,8 +6793,8 @@ const normalizeChatHistoryRecordItem = (rec) => {
|
||||
const fromUsername = String(out.fromUsername || '').trim()
|
||||
out.fromUsername = fromUsername
|
||||
out.fromAvatar = fromUsername
|
||||
? `${mediaBase}/api/chat/avatar?account=${account}&username=${encodeURIComponent(fromUsername)}`
|
||||
: (linkUrl ? `${mediaBase}/api/chat/media/favicon?url=${encodeURIComponent(linkUrl)}` : '')
|
||||
? `${apiBase}/chat/avatar?account=${account}&username=${encodeURIComponent(fromUsername)}`
|
||||
: (linkUrl ? `${apiBase}/chat/media/favicon?url=${encodeURIComponent(linkUrl)}` : '')
|
||||
out._fromAvatarLast = out.fromAvatar
|
||||
out._fromAvatarImgOk = false
|
||||
out._fromAvatarImgError = false
|
||||
@@ -6804,17 +6804,17 @@ const normalizeChatHistoryRecordItem = (rec) => {
|
||||
out.videoDuration = String(out.duration || '').trim()
|
||||
const thumbCandidates = []
|
||||
if (out.videoMd5) {
|
||||
thumbCandidates.push(`${mediaBase}/api/chat/media/video_thumb?account=${account}&md5=${encodeURIComponent(out.videoMd5)}&username=${username}`)
|
||||
thumbCandidates.push(`${apiBase}/chat/media/video_thumb?account=${account}&md5=${encodeURIComponent(out.videoMd5)}&username=${username}`)
|
||||
}
|
||||
if (out.videoThumbMd5 && out.videoThumbMd5 !== out.videoMd5) {
|
||||
thumbCandidates.push(`${mediaBase}/api/chat/media/video_thumb?account=${account}&md5=${encodeURIComponent(out.videoThumbMd5)}&username=${username}`)
|
||||
thumbCandidates.push(`${apiBase}/chat/media/video_thumb?account=${account}&md5=${encodeURIComponent(out.videoThumbMd5)}&username=${username}`)
|
||||
}
|
||||
out._videoThumbCandidates = thumbCandidates
|
||||
out._videoThumbCandidateIndex = 0
|
||||
out._videoThumbError = false
|
||||
out.videoThumbUrl = thumbCandidates[0] || ''
|
||||
out.videoUrl = out.videoMd5
|
||||
? `${mediaBase}/api/chat/media/video?account=${account}&md5=${encodeURIComponent(out.videoMd5)}&username=${username}`
|
||||
? `${apiBase}/chat/media/video?account=${account}&md5=${encodeURIComponent(out.videoMd5)}&username=${username}`
|
||||
: ''
|
||||
if (!out.content || /^\[.+\]$/.test(String(out.content || '').trim())) out.content = '[视频]'
|
||||
} else if (out.renderType === 'emoji') {
|
||||
@@ -6823,7 +6823,7 @@ const normalizeChatHistoryRecordItem = (rec) => {
|
||||
const remoteAesKey = String(out.aeskey || '').trim()
|
||||
out.emojiRemoteUrl = remoteEmojiUrl
|
||||
out.emojiUrl = out.emojiMd5
|
||||
? `${mediaBase}/api/chat/media/emoji?account=${account}&md5=${encodeURIComponent(out.emojiMd5)}&username=${username}${remoteEmojiUrl ? `&emoji_url=${encodeURIComponent(remoteEmojiUrl)}` : ''}${remoteAesKey ? `&aes_key=${encodeURIComponent(remoteAesKey)}` : ''}`
|
||||
? `${apiBase}/chat/media/emoji?account=${account}&md5=${encodeURIComponent(out.emojiMd5)}&username=${username}${remoteEmojiUrl ? `&emoji_url=${encodeURIComponent(remoteEmojiUrl)}` : ''}${remoteAesKey ? `&aes_key=${encodeURIComponent(remoteAesKey)}` : ''}`
|
||||
: ''
|
||||
if (!out.content || /^\[.+\]$/.test(String(out.content || '').trim())) out.content = '[表情]'
|
||||
} else if (out.renderType === 'image') {
|
||||
@@ -6835,7 +6835,7 @@ const normalizeChatHistoryRecordItem = (rec) => {
|
||||
srcServerId ? `server_id=${encodeURIComponent(srcServerId)}` : '',
|
||||
`username=${username}`
|
||||
].filter(Boolean)
|
||||
out.imageUrl = imgParts.length ? `${mediaBase}/api/chat/media/image?${imgParts.join('&')}` : ''
|
||||
out.imageUrl = imgParts.length ? `${apiBase}/chat/media/image?${imgParts.join('&')}` : ''
|
||||
if (!out.content || /^\[.+\]$/.test(String(out.content || '').trim())) out.content = '[图片]'
|
||||
}
|
||||
|
||||
@@ -7190,7 +7190,7 @@ const resolveChatHistoryLinkRecord = async (rec) => {
|
||||
const content = String(resp.content || '').trim()
|
||||
const url = String(resp.url || '').trim()
|
||||
const from = String(resp.from || '').trim()
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
const normalizePreviewUrl = (u) => {
|
||||
const raw = String(u || '').trim()
|
||||
if (!raw) return ''
|
||||
@@ -7199,7 +7199,7 @@ const resolveChatHistoryLinkRecord = async (rec) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
@@ -7214,8 +7214,8 @@ const resolveChatHistoryLinkRecord = async (rec) => {
|
||||
const fromUsername = String(resp.fromUsername || '').trim()
|
||||
if (fromUsername) rec.fromUsername = fromUsername
|
||||
const fromAvatarUrl = fromUsername
|
||||
? `${mediaBase}/api/chat/avatar?account=${encodeURIComponent(selectedAccount.value || '')}&username=${encodeURIComponent(fromUsername)}`
|
||||
: (url ? `${mediaBase}/api/chat/media/favicon?url=${encodeURIComponent(url)}` : '')
|
||||
? `${apiBase}/chat/avatar?account=${encodeURIComponent(selectedAccount.value || '')}&username=${encodeURIComponent(fromUsername)}`
|
||||
: (url ? `${apiBase}/chat/media/favicon?url=${encodeURIComponent(url)}` : '')
|
||||
if (fromAvatarUrl) {
|
||||
const last = String(rec._fromAvatarLast || '').trim()
|
||||
rec.fromAvatar = fromAvatarUrl
|
||||
|
||||
@@ -775,7 +775,8 @@ const handleDecrypt = async () => {
|
||||
const params = new URLSearchParams()
|
||||
params.set('key', formData.key)
|
||||
params.set('db_storage_path', formData.db_storage_path)
|
||||
const url = `http://localhost:8000/api/decrypt_stream?${params.toString()}`
|
||||
const apiBase = useApiBase()
|
||||
const url = `${apiBase}/decrypt_stream?${params.toString()}`
|
||||
|
||||
dbDecryptProgress.message = '连接中...'
|
||||
const eventSource = new EventSource(url)
|
||||
@@ -904,7 +905,8 @@ const decryptAllImages = async () => {
|
||||
if (mediaAccount.value) params.set('account', mediaAccount.value)
|
||||
if (mediaKeys.xor_key) params.set('xor_key', mediaKeys.xor_key)
|
||||
if (mediaKeys.aes_key) params.set('aes_key', mediaKeys.aes_key)
|
||||
const url = `http://localhost:8000/api/media/decrypt_all_stream?${params.toString()}`
|
||||
const apiBase = useApiBase()
|
||||
const url = `${apiBase}/media/decrypt_all_stream?${params.toString()}`
|
||||
|
||||
// 使用EventSource接收SSE
|
||||
const eventSource = new EventSource(url)
|
||||
|
||||
@@ -257,13 +257,13 @@ watch(selectedAccount, async () => {
|
||||
await loadSessions()
|
||||
})
|
||||
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
|
||||
const normalizeMaybeUrl = (u) => {
|
||||
const raw = String(u || '').trim()
|
||||
if (!raw) return ''
|
||||
if (/^https?:\/\//i.test(raw) || /^blob:/i.test(raw) || /^data:/i.test(raw)) return raw
|
||||
if (/^\/api\//i.test(raw)) return `${mediaBase}${raw}`
|
||||
if (/^\/api\//i.test(raw)) return `${apiBase}${raw.slice(4)}`
|
||||
return raw
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -794,7 +794,7 @@ const filteredSnsUsers = computed(() => {
|
||||
|
||||
const pageSize = 20
|
||||
|
||||
const mediaBase = process.client ? 'http://localhost:8000' : ''
|
||||
const apiBase = useApiBase()
|
||||
|
||||
// 朋友圈导出(HTML 离线 ZIP)
|
||||
const exportJob = ref(null)
|
||||
@@ -835,8 +835,7 @@ const startSnsExportPolling = (exportId) => {
|
||||
if (!exportId) return
|
||||
|
||||
if (process.client && typeof window !== 'undefined' && typeof EventSource !== 'undefined') {
|
||||
const base = 'http://localhost:8000'
|
||||
const url = `${base}/api/sns/exports/${encodeURIComponent(String(exportId))}/events`
|
||||
const url = `${apiBase}/sns/exports/${encodeURIComponent(String(exportId))}/events`
|
||||
try {
|
||||
exportEventSource = new EventSource(url)
|
||||
exportEventSource.onmessage = (ev) => {
|
||||
@@ -867,8 +866,7 @@ const downloadSnsExport = (exportId) => {
|
||||
if (!process.client) return
|
||||
const id = String(exportId || '').trim()
|
||||
if (!id) return
|
||||
const base = 'http://localhost:8000'
|
||||
const url = `${base}/api/sns/exports/${encodeURIComponent(id)}/download`
|
||||
const url = `${apiBase}/sns/exports/${encodeURIComponent(id)}/download`
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
@@ -1109,7 +1107,7 @@ const selfInfo = ref({ wxid: '', nickname: '' })
|
||||
const loadSelfInfo = async () => {
|
||||
if (!selectedAccount.value) return
|
||||
try {
|
||||
const resp = await $fetch(`${mediaBase}/api/sns/self_info?account=${encodeURIComponent(selectedAccount.value)}`)
|
||||
const resp = await $fetch(`${apiBase}/sns/self_info?account=${encodeURIComponent(selectedAccount.value)}`)
|
||||
if (resp && resp.wxid) {
|
||||
selfInfo.value = resp
|
||||
}
|
||||
@@ -1145,7 +1143,7 @@ const selectSnsUser = async (username) => {
|
||||
const getArticleThumbProxyUrl = (contentUrl) => {
|
||||
const u = String(contentUrl || '').trim()
|
||||
if (!u) return ''
|
||||
return `${mediaBase}/api/sns/article_thumb?url=${encodeURIComponent(u)}`
|
||||
return `${apiBase}/sns/article_thumb?url=${encodeURIComponent(u)}`
|
||||
}
|
||||
|
||||
const guessOfficialAccountNameFromTitle = (title) => {
|
||||
@@ -1443,7 +1441,7 @@ const postAvatarUrl = (username) => {
|
||||
const acc = String(selectedAccount.value || '').trim()
|
||||
const u = String(username || '').trim()
|
||||
if (!acc || !u) return ''
|
||||
return `${mediaBase}/api/chat/avatar?account=${encodeURIComponent(acc)}&username=${encodeURIComponent(u)}`
|
||||
return `${apiBase}/chat/avatar?account=${encodeURIComponent(acc)}&username=${encodeURIComponent(u)}`
|
||||
}
|
||||
|
||||
const cleanLikeName = (v) => String(v ?? '').replace(/\u00A0/g, ' ').trim()
|
||||
@@ -1460,7 +1458,7 @@ const normalizeMediaUrl = (u) => {
|
||||
try {
|
||||
const host = new URL(raw).hostname.toLowerCase()
|
||||
if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
|
||||
}
|
||||
} catch {}
|
||||
return raw
|
||||
@@ -1515,8 +1513,10 @@ const getSnsMediaUrl = (post, m, idx, rawUrl) => {
|
||||
if (!raw) return ''
|
||||
const rawLower = raw.toLowerCase()
|
||||
|
||||
// If backend already provides a local media endpoint, keep it as-is.
|
||||
if (rawLower.startsWith('/api/') || rawLower.startsWith('blob:') || rawLower.startsWith('data:')) return raw
|
||||
// If backend already provides a local media endpoint, rewrite it to the effective API base
|
||||
// (so web builds with a custom API port still work).
|
||||
if (rawLower.startsWith('/api/')) return `${apiBase}${raw.slice(4)}`
|
||||
if (rawLower.startsWith('blob:') || rawLower.startsWith('data:')) return raw
|
||||
|
||||
// For Moments images/thumbnails, prefer a backend endpoint that can decrypt local cache.
|
||||
if (/^https?:\/\//i.test(raw)) {
|
||||
@@ -1568,7 +1568,7 @@ const getSnsMediaUrl = (post, m, idx, rawUrl) => {
|
||||
// Bump this when changing backend matching logic to avoid stale cached wrong images.
|
||||
parts.set('v', '9')
|
||||
parts.set('url', raw)
|
||||
return `${mediaBase}/api/sns/media?${parts.toString()}`
|
||||
return `${apiBase}/sns/media?${parts.toString()}`
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
@@ -1589,7 +1589,7 @@ const getSnsVideoUrl = (postId, mediaId) => {
|
||||
// 本地缓存视频
|
||||
const acc = String(selectedAccount.value || '').trim()
|
||||
if (!acc || !postId || !mediaId) return ''
|
||||
return `${mediaBase}/api/sns/video?account=${encodeURIComponent(acc)}&post_id=${encodeURIComponent(postId)}&media_id=${encodeURIComponent(mediaId)}`
|
||||
return `${apiBase}/sns/video?account=${encodeURIComponent(acc)}&post_id=${encodeURIComponent(postId)}&media_id=${encodeURIComponent(mediaId)}`
|
||||
}
|
||||
|
||||
const getSnsRemoteVideoSrc = (post, m) => {
|
||||
@@ -1610,7 +1610,7 @@ const getSnsRemoteVideoSrc = (post, m) => {
|
||||
// When cache is disabled, bust browser caching so backend really downloads+decrypts each time.
|
||||
if (!snsUseCache.value) parts.set('_t', String(Date.now()))
|
||||
parts.set('v', '1')
|
||||
return `${mediaBase}/api/sns/video_remote?${parts.toString()}`
|
||||
return `${apiBase}/sns/video_remote?${parts.toString()}`
|
||||
}
|
||||
|
||||
const localVideoStatus = ref({})
|
||||
@@ -1726,7 +1726,7 @@ const getLivePhotoVideoSrc = (post, m, idx = 0) => {
|
||||
if (!snsUseCache.value) parts.set('_t', String(Date.now()))
|
||||
// Version bump for frontend cache busting when endpoint changes.
|
||||
parts.set('v', '1')
|
||||
return `${mediaBase}/api/sns/video_remote?${parts.toString()}`
|
||||
return `${apiBase}/sns/video_remote?${parts.toString()}`
|
||||
}
|
||||
|
||||
// 图片预览 + 候选匹配选择
|
||||
@@ -2114,7 +2114,7 @@ const getProxyExternalUrl = (url) => {
|
||||
// 目前难以计算enc,代理获取封面图(thumbnail)
|
||||
const u = String(url || '').trim()
|
||||
if (!u) return ''
|
||||
return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(u)}`
|
||||
return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(u)}`
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -89,8 +89,8 @@ export const useChatRealtimeStore = defineStore('chatRealtime', () => {
|
||||
if (!account) return
|
||||
if (typeof EventSource === 'undefined') return
|
||||
|
||||
const base = 'http://localhost:8000'
|
||||
const url = `${base}/api/chat/realtime/stream?account=${encodeURIComponent(account)}`
|
||||
const apiBase = useApiBase()
|
||||
const url = `${apiBase}/chat/realtime/stream?account=${encodeURIComponent(account)}`
|
||||
|
||||
try {
|
||||
eventSource = new EventSource(url)
|
||||
@@ -223,4 +223,3 @@ export const useChatRealtimeStore = defineStore('chatRealtime', () => {
|
||||
toggle,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user