115 lines
3.0 KiB
TypeScript
115 lines
3.0 KiB
TypeScript
import { allow, deny } from "@/lib/decision"
|
|
import { isApiError, toApiError, type ApiDecision } from "@/lib/errors"
|
|
import type { ChatMessage, ChatSession } from "@/lib/types"
|
|
|
|
import { httpJson } from "./http"
|
|
|
|
type Paginated<T> = {
|
|
items: T[]
|
|
meta: {
|
|
total: number
|
|
offset: number
|
|
limit: number
|
|
}
|
|
}
|
|
|
|
export type ListChatSessionsOptions = {
|
|
offset?: number
|
|
limit?: number
|
|
}
|
|
|
|
export type ListChatMessagesOptions = {
|
|
offset?: number
|
|
limit?: number
|
|
}
|
|
|
|
function withOffsetLimit(path: string, options?: { offset?: number; limit?: number }): string {
|
|
const offset = options?.offset ?? 0
|
|
const limit = options?.limit ?? 100
|
|
|
|
const searchParams = new URLSearchParams({
|
|
offset: String(offset),
|
|
limit: String(limit),
|
|
})
|
|
|
|
return `${path}?${searchParams.toString()}`
|
|
}
|
|
|
|
function unwrapItems<T>(value: Paginated<T> | T[]): T[] {
|
|
return Array.isArray(value) ? value : value.items
|
|
}
|
|
|
|
function denyFromError(error: unknown): ApiDecision {
|
|
if (error instanceof Error && error.message === "UNAUTHORIZED") {
|
|
return deny(401, "请先登录")
|
|
}
|
|
|
|
const apiError = toApiError(error)
|
|
return deny(apiError.code, apiError.msg)
|
|
}
|
|
|
|
export async function listChatSessions(options?: ListChatSessionsOptions): Promise<ChatSession[]> {
|
|
const res = await httpJson<Paginated<ChatSession> | ChatSession[]>(
|
|
withOffsetLimit("/api/v1/chat/sessions", options),
|
|
{
|
|
cache: "no-store",
|
|
},
|
|
)
|
|
return unwrapItems(res)
|
|
}
|
|
|
|
export async function getChatSessionById(sessionId: string): Promise<ChatSession | undefined> {
|
|
try {
|
|
return await httpJson<ChatSession>(`/api/v1/chat/sessions/${encodeURIComponent(sessionId)}`, {
|
|
cache: "no-store",
|
|
})
|
|
} catch (error) {
|
|
if (error instanceof Error && error.message === "UNAUTHORIZED") {
|
|
throw error
|
|
}
|
|
if (isApiError(error) && error.code === 404) {
|
|
return undefined
|
|
}
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export async function listChatMessages(
|
|
sessionId: string,
|
|
options?: ListChatMessagesOptions,
|
|
): Promise<ChatMessage[]> {
|
|
const res = await httpJson<Paginated<ChatMessage> | ChatMessage[]>(
|
|
withOffsetLimit(`/api/v1/chat/sessions/${encodeURIComponent(sessionId)}/messages`, options),
|
|
{
|
|
cache: "no-store",
|
|
},
|
|
)
|
|
return unwrapItems(res)
|
|
}
|
|
|
|
export async function sendTextMessage(sessionId: string, content: string): Promise<ApiDecision> {
|
|
try {
|
|
await httpJson<unknown>(`/api/v1/chat/sessions/${encodeURIComponent(sessionId)}/messages`, {
|
|
method: "POST",
|
|
cache: "no-store",
|
|
json: { type: "text", content },
|
|
})
|
|
return allow()
|
|
} catch (error) {
|
|
return denyFromError(error)
|
|
}
|
|
}
|
|
|
|
export async function sendImageMessage(sessionId: string, imageUrl: string): Promise<ApiDecision> {
|
|
try {
|
|
await httpJson<unknown>(`/api/v1/chat/sessions/${encodeURIComponent(sessionId)}/messages`, {
|
|
method: "POST",
|
|
cache: "no-store",
|
|
json: { type: "image", content: imageUrl },
|
|
})
|
|
return allow()
|
|
} catch (error) {
|
|
return denyFromError(error)
|
|
}
|
|
}
|