multica/apps/web/service/request.ts
Naiyuan Qing ae86bba599 feat(web): add client-side auth with session storage and route protection
- Add lib/auth.ts for cookie-based session storage
- Add AuthGuard component for route protection
- Update request.ts to include sid in Authorization header
- Update login-form.tsx to save session and redirect if authenticated
- Wrap ChatPage with AuthGuard

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 14:31:02 +08:00

74 lines
2.1 KiB
TypeScript

import { API_HOST } from '@/lib/constant';
import { generateDeviceIdHeader, getOrCreateDeviceId } from '@/lib/device';
import { getSid } from '@/lib/auth';
// Fetch request wrapper
export async function request<T = unknown>(url: string, options: RequestInit = {}): Promise<T> {
// Get or generate Device ID
let deviceIdHeader = '';
let sid: string | null = null;
if (typeof window !== 'undefined') {
const deviceId = getOrCreateDeviceId();
deviceIdHeader = await generateDeviceIdHeader(deviceId);
sid = getSid();
}
const config: RequestInit = {
...options,
headers: {
'Content-Type': 'application/json',
'os-type': '3',
...(deviceIdHeader && { 'Device-Id': deviceIdHeader }),
...(sid && { 'Authorization': `Bearer ${sid}` }),
...options.headers,
},
};
const response = await fetch(`${API_HOST}${url}`, config);
let data: T;
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
data = await response.json();
} else {
const text = await response.text();
data = { message: text || response.statusText } as T;
}
if (!response.ok) {
console.error('API Error:', {
status: response.status,
url,
data,
});
throw new Error(
(data as { errMsg?: string; message?: string })?.errMsg ||
(data as { message?: string })?.message ||
`Request failed with status ${response.status}`
);
}
return data;
}
// GET request
export function get<T = unknown>(url: string, params?: Record<string, string | number | boolean>) {
const filteredParams = params
? Object.fromEntries(
Object.entries(params).filter(([, v]) => v !== undefined && v !== null)
)
: undefined;
const queryString =
filteredParams && Object.keys(filteredParams).length > 0
? `?${new URLSearchParams(filteredParams as Record<string, string>).toString()}`
: '';
return request<T>(url + queryString, { method: 'GET' });
}
// POST request
export function post<T = unknown>(url: string, data?: unknown) {
return request<T>(url, {
method: 'POST',
body: JSON.stringify(data),
});
}