feat(desktop): add API client with auth headers

Add fetch wrapper for desktop renderer to call Multica backend REST API.
Attaches sid, device-id, and os-type headers automatically using
useAuthStore and electronAPI.auth.getDeviceIdHeader().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yushen 2026-02-13 16:31:43 +08:00
parent c79e87e000
commit 309c27b4dd

View file

@ -0,0 +1,72 @@
import { useAuthStore } from '../stores/auth'
// Backend API host — change this when switching environments
const API_HOST = 'https://api-dev.copilothub.ai'
/**
* Fetch request wrapper for desktop app.
* Attaches sid, device-id, and os-type headers automatically.
*/
export async function request<T = unknown>(url: string, options: RequestInit = {}): Promise<T> {
const sid = useAuthStore.getState().sid
const deviceIdHeader = await window.electronAPI.auth.getDeviceIdHeader()
const config: RequestInit = {
...options,
headers: {
'Content-Type': 'application/json',
'os-type': '3',
...(deviceIdHeader && { 'device-id': deviceIdHeader }),
...(sid && { 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),
})
}