feat: add WeChat session authentication
This commit is contained in:
98
utils/api.ts
98
utils/api.ts
@@ -14,13 +14,18 @@ import type {
|
||||
VoiceLessonFinished,
|
||||
VoiceProcessingStatus
|
||||
} from './types'
|
||||
import {
|
||||
AuthenticationError,
|
||||
clearAuthSession,
|
||||
ensureAuthSession,
|
||||
getAccessToken
|
||||
} from './auth'
|
||||
import type { AuthSession } from './auth'
|
||||
|
||||
const DEFAULT_API_BASE_URL = 'https://feedback.shay7sev.site'
|
||||
const LEGACY_DEFAULT_API_BASE_URL = 'http://127.0.0.1:8080'
|
||||
const API_BASE_URL_KEY = 'teaching-feedback-api-base-url-v1'
|
||||
|
||||
export const DEVELOPMENT_USER_ID = '5a4da7f0-d70c-465f-bcab-124c504aa9f0'
|
||||
|
||||
interface ApiErrorBody {
|
||||
error?: string
|
||||
}
|
||||
@@ -62,6 +67,8 @@ interface RawHealthStatus {
|
||||
speech_available: boolean
|
||||
speech_provider: string | null
|
||||
summary_configured: boolean
|
||||
wechat_auth_configured: boolean
|
||||
development_auth_enabled: boolean
|
||||
}
|
||||
|
||||
interface RawVoiceAudioSegment {
|
||||
@@ -158,23 +165,56 @@ export function setApiBaseUrl(value: string): string {
|
||||
}
|
||||
|
||||
export function getApiErrorMessage(error: unknown): string {
|
||||
if (error instanceof AuthenticationError) return error.message
|
||||
if (!(error instanceof ApiRequestError)) return '请求失败,请稍后重试'
|
||||
if (error.statusCode === 401) return '开发身份未通过后端校验'
|
||||
if (error.statusCode === 401) return '登录状态已失效,请重试'
|
||||
if (error.statusCode === 404) return '请求的数据不存在或无权访问'
|
||||
if (error.statusCode === 503 && error.message.includes('database')) return '后端尚未配置数据库连接'
|
||||
return error.message
|
||||
}
|
||||
|
||||
function request<T>(path: string, method: HttpMethod = 'GET', data?: object, timeout = 10000): Promise<T> {
|
||||
export function ensureAuthentication(): Promise<AuthSession> {
|
||||
return ensureAuthSession(getApiBaseUrl())
|
||||
}
|
||||
|
||||
async function request<T>(
|
||||
path: string,
|
||||
method: HttpMethod = 'GET',
|
||||
data?: object,
|
||||
timeout = 10000,
|
||||
authenticated = true
|
||||
): Promise<T> {
|
||||
const apiBaseUrl = getApiBaseUrl()
|
||||
if (!authenticated) return requestOnce<T>(apiBaseUrl, path, method, data, timeout)
|
||||
|
||||
let accessToken = await getAccessToken(apiBaseUrl)
|
||||
try {
|
||||
return await requestOnce<T>(apiBaseUrl, path, method, data, timeout, accessToken)
|
||||
} catch (error) {
|
||||
if (!(error instanceof ApiRequestError) || error.statusCode !== 401) throw error
|
||||
clearAuthSession()
|
||||
accessToken = await getAccessToken(apiBaseUrl, true)
|
||||
return requestOnce<T>(apiBaseUrl, path, method, data, timeout, accessToken)
|
||||
}
|
||||
}
|
||||
|
||||
function requestOnce<T>(
|
||||
apiBaseUrl: string,
|
||||
path: string,
|
||||
method: HttpMethod,
|
||||
data: object | undefined,
|
||||
timeout: number,
|
||||
accessToken?: string
|
||||
): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${getApiBaseUrl()}${path}`,
|
||||
url: `${apiBaseUrl}${path}`,
|
||||
method,
|
||||
data,
|
||||
timeout,
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-User-Id': DEVELOPMENT_USER_ID
|
||||
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {})
|
||||
},
|
||||
success(response) {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
@@ -288,14 +328,16 @@ function mapFeedbackSession(session: RawFeedbackSession): FeedbackSession {
|
||||
}
|
||||
|
||||
export async function getHealth(): Promise<HealthStatus> {
|
||||
const result = await request<RawHealthStatus>('/health')
|
||||
const result = await request<RawHealthStatus>('/health', 'GET', undefined, 10000, false)
|
||||
return {
|
||||
status: result.status,
|
||||
databaseConfigured: result.database_configured,
|
||||
speechConfigured: result.speech_configured,
|
||||
speechAvailable: result.speech_available,
|
||||
speechProvider: result.speech_provider,
|
||||
summaryConfigured: result.summary_configured
|
||||
summaryConfigured: result.summary_configured,
|
||||
wechatAuthConfigured: result.wechat_auth_configured,
|
||||
developmentAuthEnabled: result.development_auth_enabled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +432,41 @@ export async function createVoiceLesson(sessionId: string): Promise<VoiceLessonC
|
||||
}
|
||||
}
|
||||
|
||||
export function uploadVoiceSegment(
|
||||
export async function uploadVoiceSegment(
|
||||
lessonId: string,
|
||||
filePath: string,
|
||||
sequence: number,
|
||||
durationMs: number
|
||||
): Promise<VoiceAudioSegment> {
|
||||
const apiBaseUrl = getApiBaseUrl()
|
||||
let accessToken = await getAccessToken(apiBaseUrl)
|
||||
try {
|
||||
return await uploadVoiceSegmentOnce(
|
||||
apiBaseUrl,
|
||||
accessToken,
|
||||
lessonId,
|
||||
filePath,
|
||||
sequence,
|
||||
durationMs
|
||||
)
|
||||
} catch (error) {
|
||||
if (!(error instanceof ApiRequestError) || error.statusCode !== 401) throw error
|
||||
clearAuthSession()
|
||||
accessToken = await getAccessToken(apiBaseUrl, true)
|
||||
return uploadVoiceSegmentOnce(
|
||||
apiBaseUrl,
|
||||
accessToken,
|
||||
lessonId,
|
||||
filePath,
|
||||
sequence,
|
||||
durationMs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function uploadVoiceSegmentOnce(
|
||||
apiBaseUrl: string,
|
||||
accessToken: string,
|
||||
lessonId: string,
|
||||
filePath: string,
|
||||
sequence: number,
|
||||
@@ -398,7 +474,7 @@ export function uploadVoiceSegment(
|
||||
): Promise<VoiceAudioSegment> {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.uploadFile({
|
||||
url: `${getApiBaseUrl()}/api/v1/feedback-lessons/${encodeURIComponent(lessonId)}/segments`,
|
||||
url: `${apiBaseUrl}/api/v1/feedback-lessons/${encodeURIComponent(lessonId)}/segments`,
|
||||
filePath,
|
||||
name: 'audio',
|
||||
formData: {
|
||||
@@ -407,7 +483,7 @@ export function uploadVoiceSegment(
|
||||
audio_format: 'mp3'
|
||||
},
|
||||
timeout: 120000,
|
||||
header: { 'X-User-Id': DEVELOPMENT_USER_ID },
|
||||
header: { Authorization: `Bearer ${accessToken}` },
|
||||
success(response) {
|
||||
let body: RawVoiceAudioSegment | ApiErrorBody
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user