Files
stock/frontend/src/lib/api.ts
T

152 lines
3.2 KiB
TypeScript

const API_BASE = import.meta.env.VITE_API_BASE_URL || ''
export interface ApiResponse<T> {
success?: boolean
data?: T
error?: string
}
export interface UserInfo {
id: string
username: string
email?: string
role: 'system_admin' | 'admin' | 'user'
status: string
created_at: string
}
export interface Role {
id: number
name: 'system_admin' | 'admin' | 'user'
description?: string
permissions: string[]
created_at: string
}
export interface AuthResponse {
token: string
user: UserInfo
}
export interface StockRow {
symbol: string
name: string
close: number
change_pct: number
amount?: number
}
export interface MarketOverview {
latest_trade_date: string
counts: {
up: number
down: number
flat: number
total: number
}
avg_change_pct: number
indices: StockRow[]
top_gainers: StockRow[]
top_losers: StockRow[]
turnover_leaders: StockRow[]
}
export interface SyncJob {
id: string
job_date: string
status: 'running' | 'success' | 'failed'
records_count: number
started_at: string
finished_at?: string
error_message?: string
trigger_by: string
}
export function getToken(): string | null {
try {
return localStorage.getItem('access_token')
} catch {
return null
}
}
export function setToken(token: string) {
localStorage.setItem('access_token', token)
}
export function removeToken() {
localStorage.removeItem('access_token')
}
async function request<T>(
path: string,
options: RequestInit = {},
): Promise<T> {
const url = `${API_BASE}${path}`
const token = getToken()
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers as Record<string, string>),
}
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
const res = await fetch(url, {
...options,
headers,
})
let data: any
try {
data = await res.json()
} catch {
data = {}
}
if (!res.ok) {
const msg = data.error || data.message || `HTTP ${res.status}`
throw new Error(msg)
}
return data as T
}
export const api = {
login: (body: { username: string; password: string }) =>
request<AuthResponse>('/api/auth/login', {
method: 'POST',
body: JSON.stringify(body),
}),
me: () => request<UserInfo>('/api/auth/me'),
logout: () => request<{ message: string }>('/api/auth/logout', { method: 'POST' }),
listUsers: () => request<UserInfo[]>('/api/admin/users'),
createUser: (body: { username: string; email?: string; password: string; role?: string }) =>
request<UserInfo>('/api/admin/users', {
method: 'POST',
body: JSON.stringify(body),
}),
updateUser: (id: string, body: Partial<{ email: string; role: string; status: string }>) =>
request<UserInfo>(`/api/admin/users/${id}`, {
method: 'PUT',
body: JSON.stringify(body),
}),
deleteUser: (id: string) =>
request<{ message: string }>(`/api/admin/users/${id}`, { method: 'DELETE' }),
listRoles: () => request<Role[]>('/api/admin/roles'),
stockOverview: () => request<MarketOverview>('/api/stocks/overview'),
stockSyncStatus: () => request<SyncJob | null>('/api/stocks/sync/status'),
triggerStockSync: () =>
request<{ job: SyncJob }>('/api/admin/stocks/sync', { method: 'POST' }),
}