feat(db): 内容库迁入 SQLite,分类与侧栏导航改为数据库驱动
This commit is contained in:
+75
-1
@@ -1,3 +1,6 @@
|
||||
import type { NavModel } from './data/nav'
|
||||
import type { Category } from './data/types'
|
||||
|
||||
export type Project = {
|
||||
name: string
|
||||
route_path: string
|
||||
@@ -9,7 +12,6 @@ type ProjectsPayload = {
|
||||
|
||||
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, {
|
||||
@@ -67,3 +69,75 @@ export function subscribeProjects(handlers: ProjectEventsHandlers): () => void {
|
||||
source.close()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 内容目录:数据来自后端的 SQLite 内容库(data/stash.db),不再是前端静态数组
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const CATALOG_ENDPOINT = '/api/catalog'
|
||||
const CATALOG_EVENTS_ENDPOINT = '/api/catalog/events'
|
||||
|
||||
type CatalogPayload = {
|
||||
categories?: Category[]
|
||||
nav?: NavModel
|
||||
}
|
||||
|
||||
/** 内容目录的完整返回:分类树 + 侧栏导航 */
|
||||
export type Catalog = {
|
||||
categories: Category[]
|
||||
nav: NavModel
|
||||
}
|
||||
|
||||
/** 服务端字段可能缺失,统一在这里补成空数组,组件层就不用判空 */
|
||||
function normalizeNav(nav: NavModel | undefined): NavModel {
|
||||
return {
|
||||
topLevel: Array.isArray(nav?.topLevel) ? nav.topLevel : [],
|
||||
groups: Array.isArray(nav?.groups) ? nav.groups : [],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取完整内容目录。
|
||||
*
|
||||
* 数据量只有几十 KB,一次全量取回即可;搜索仍然在前端按关键字过滤。
|
||||
* 分类与导航一起下发,因为首页和侧栏同时需要它们。
|
||||
*/
|
||||
export async function fetchCatalog(signal?: AbortSignal): Promise<Catalog> {
|
||||
const response = await fetch(CATALOG_ENDPOINT, {
|
||||
signal,
|
||||
headers: { Accept: 'application/json' },
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求 ${CATALOG_ENDPOINT} 失败:HTTP ${response.status}`)
|
||||
}
|
||||
|
||||
const payload = (await response.json()) as CatalogPayload
|
||||
|
||||
return {
|
||||
categories: Array.isArray(payload.categories) ? payload.categories : [],
|
||||
nav: normalizeNav(payload.nav),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅内容库变更。
|
||||
*
|
||||
* 服务端用 SQLite 的 data_version 检测「外部工具改了库」,事件里不带数据,
|
||||
* 只表示「该重新拉一次了」,避免两套数据结构需要保持同步。
|
||||
*/
|
||||
export function subscribeCatalog(onChange: () => void): () => void {
|
||||
if (typeof EventSource === 'undefined') {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
const source = new EventSource(CATALOG_EVENTS_ENDPOINT)
|
||||
const handleChange = () => onChange()
|
||||
|
||||
source.addEventListener('catalog', handleChange)
|
||||
|
||||
return () => {
|
||||
source.removeEventListener('catalog', handleChange)
|
||||
source.close()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user