Files
stash/stash-web/src/api.ts
T
CNWei 6dabeabaf2 refactor: 清理遗留设置模型与无用代码
- 删除 settings 的 sections 与 sidebar 六个旧排序/显隐字段,前后端类型与校验同步
- 删除旧分类模型 types、旧 /api/catalog 客户端、旧路由解析与 settings CRUD
- 删除未使用的 AppProject::initial
- 删除 27 条已无组件的 CSS 规则(159 行)
- 保持现有首页/接口页渲染,cargo test 52 passed
2026-11-03 19:49:04 +08:00

69 lines
2.0 KiB
TypeScript

export type Project = {
name: string
route_path: string
}
type ProjectsPayload = {
projects?: Project[]
}
const PROJECTS_ENDPOINT = '/api/projects'
const EVENTS_ENDPOINT = '/api/projects/events'
/** 读取服务端扫描出的项目列表。 */
export async function fetchProjects(signal?: AbortSignal): Promise<Project[]> {
const response = await fetch(PROJECTS_ENDPOINT, {
signal,
headers: { Accept: 'application/json' },
})
if (!response.ok) {
throw new Error(`请求 ${PROJECTS_ENDPOINT} 失败:HTTP ${response.status}`)
}
const payload = (await response.json()) as ProjectsPayload
return Array.isArray(payload.projects) ? payload.projects : []
}
type ProjectEventsHandlers = {
onProjects: (projects: Project[]) => void
/** SSE 连接建立 / 断开时回调,浏览器的 EventSource 会自行重连。 */
onConnectionChange?: (connected: boolean) => void
}
/**
* 订阅服务端的项目快照推送。
* 浏览器不支持 SSE 时静默降级为空清理函数,页面仍可用手动刷新。
*/
export function subscribeProjects(handlers: ProjectEventsHandlers): () => void {
if (typeof EventSource === 'undefined') {
return () => {}
}
const source = new EventSource(EVENTS_ENDPOINT)
const handleProjects = (event: Event) => {
try {
const payload = JSON.parse((event as MessageEvent<string>).data) as ProjectsPayload
if (Array.isArray(payload.projects)) {
handlers.onProjects(payload.projects)
}
} catch {
// 单条事件格式异常时保留旧数据,等待下一次推送
}
}
const handleOpen = () => handlers.onConnectionChange?.(true)
const handleError = () => handlers.onConnectionChange?.(false)
source.addEventListener('projects', handleProjects)
source.addEventListener('open', handleOpen)
source.addEventListener('error', handleError)
return () => {
source.removeEventListener('projects', handleProjects)
source.removeEventListener('open', handleOpen)
source.removeEventListener('error', handleError)
source.close()
}
}