feat(chat): implement WebSocket chat client with useChatSocket hook

Replace the stubbed chat API with a real WebSocket hook that
connects to /ws/chat and supports DM creation, messaging,
session join/leave, and history retrieval. Keep REST stub
functions for backward compatibility with existing pages
that require listChatSessions/getChatSessionById imports.
This commit is contained in:
zetaloop
2026-05-01 04:25:56 +08:00
parent 1f20198f23
commit a3f0b49112
8 changed files with 426 additions and 215 deletions
+31 -4
View File
@@ -60,6 +60,34 @@ function getCookieValue(name: string): string | null {
return null
}
let csrfReady: Promise<void> | null = null
function needsCsrf(method: string) {
return method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE"
}
async function prepareCsrf(path: string) {
if (typeof document === "undefined") return
if (getCookieValue("__Host-XSRF-TOKEN") && getCookieValue("__Host-XSRF-GUARD")) return
csrfReady ??= fetch("/healthz", {
cache: "no-store",
credentials: "include",
})
.then(() => {})
.finally(() => {
csrfReady = null
})
await csrfReady
if (!getCookieValue("__Host-XSRF-TOKEN") && path !== "/healthz") {
await fetch("/api/v1/games?offset=0&limit=1", {
cache: "no-store",
credentials: "include",
})
}
}
function isApiErrorWithMessage(value: unknown): value is { code: number; message: string } {
if (typeof value !== "object" || value === null) return false
const v = value as { code?: unknown; message?: unknown }
@@ -91,10 +119,8 @@ export async function httpJson<T>(path: string, init?: JsonRequestInit): Promise
}
const method = (rest.method ?? "GET").toUpperCase()
if (
(method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE") &&
!headers.has("XSRF-TOKEN")
) {
if (needsCsrf(method) && !headers.has("XSRF-TOKEN")) {
await prepareCsrf(path)
const xsrfToken = getCookieValue("__Host-XSRF-TOKEN")
if (xsrfToken) headers.set("XSRF-TOKEN", xsrfToken)
}
@@ -103,6 +129,7 @@ export async function httpJson<T>(path: string, init?: JsonRequestInit): Promise
...rest,
headers,
body,
credentials: rest.credentials ?? "include",
})
const { json: data, text } = await readJsonBody(res)