mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-02 04:16:38 +08:00
9cc21a47c5
- SmokeChain 结构化全链路冒烟(搜索→详情→目录→正文,分阶段失败定位), SmokeSource 支持不落库直测;Debug 接口改为结构化日志输出 - cmd/reader-smoke:批量书源冒烟 CLI(-file/-url 导入、并发、 各阶段通过率汇总、-json 完整报告) - GetContentForBook:书架维度正文(章节缓存 + 书源 replaceRegex + 用户替换净化规则),前端阅读器切换到该接口 - 替换净化规则 CRUD(后端 + /reader/replace 前端页),用户规则 按 scope/excludeScope 书名过滤、正则带匹配超时防回溯挂死 - rule.ApplyUserReplace 导出(regexp2 MatchTimeout)
184 lines
5.4 KiB
TypeScript
184 lines
5.4 KiB
TypeScript
import { LONG_REQUEST_TIMEOUT, api } from './client'
|
||
|
||
// 阅读子系统 API(/api/reader/*),字段与后端 model/reader.go 对齐。
|
||
|
||
export interface ReaderSearchOrigin {
|
||
source_id: string
|
||
origin: string
|
||
origin_name: string
|
||
origin_type: number
|
||
book_url: string
|
||
latest_chapter: string
|
||
}
|
||
|
||
export interface ReaderSearchBook {
|
||
name: string
|
||
author: string
|
||
kind: string
|
||
word_count: string
|
||
latest_chapter: string
|
||
intro: string
|
||
cover_url: string
|
||
book_url: string
|
||
origins: ReaderSearchOrigin[]
|
||
}
|
||
|
||
export interface ReaderSearchSkipped {
|
||
source_id: string
|
||
origin_name: string
|
||
reason: string
|
||
}
|
||
|
||
export interface ReaderSource {
|
||
id: string
|
||
name: string
|
||
group: string
|
||
type: number
|
||
source_url: string
|
||
enabled: boolean
|
||
enabled_explore: boolean
|
||
custom_order: number
|
||
weight: number
|
||
comment: string
|
||
last_update_time: number
|
||
respond_time: number
|
||
}
|
||
|
||
export interface ReaderBook {
|
||
id: string
|
||
origin: string
|
||
origin_name: string
|
||
book_url: string
|
||
toc_url: string
|
||
name: string
|
||
author: string
|
||
kind: string
|
||
cover_url: string
|
||
intro: string
|
||
type: number
|
||
latest_chapter_title: string
|
||
total_chapter_num: number
|
||
dur_chapter_index: number
|
||
dur_chapter_pos: number
|
||
dur_chapter_title: string
|
||
dur_chapter_time: number
|
||
order: number
|
||
}
|
||
|
||
export interface ReaderBookInfo {
|
||
name: string
|
||
author: string
|
||
kind: string
|
||
word_count: string
|
||
latest_chapter: string
|
||
intro: string
|
||
cover_url: string
|
||
toc_url: string
|
||
book_url: string
|
||
}
|
||
|
||
export interface ReaderTocChapter {
|
||
index: number
|
||
title: string
|
||
url: string
|
||
is_volume: boolean
|
||
update_time: string
|
||
}
|
||
|
||
export interface ReaderChapter {
|
||
index: number
|
||
title: string
|
||
url: string
|
||
is_volume: boolean
|
||
}
|
||
|
||
export interface ReaderChapterContent {
|
||
type: 'text' | 'audio' | 'image'
|
||
content?: string
|
||
tracks?: string[]
|
||
images?: string[]
|
||
}
|
||
|
||
export interface ReaderReplaceRule {
|
||
id: string
|
||
name: string
|
||
group: string
|
||
pattern: string
|
||
replacement: string
|
||
scope: string
|
||
scope_title: boolean
|
||
scope_content: boolean
|
||
exclude_scope: string
|
||
is_enabled: boolean
|
||
is_regex: boolean
|
||
order: number
|
||
}
|
||
|
||
const longOpts = { timeout: LONG_REQUEST_TIMEOUT } as const
|
||
|
||
export const readerAPI = {
|
||
// ── 书源 ──
|
||
listSources: () => api.get<{ sources: ReaderSource[] }>('/reader/sources').then((r) => r.data.sources),
|
||
importSources: (text: string) =>
|
||
api.post<{ imported: number }>('/reader/sources/import', { text }, longOpts).then((r) => r.data.imported),
|
||
setSourceEnabled: (id: string, enabled: boolean) => api.patch(`/reader/sources/${id}`, { enabled }),
|
||
deleteSource: (id: string) => api.delete(`/reader/sources/${id}`),
|
||
debugSource: (id: string, key: string) =>
|
||
api.post<{ logs: string[] }>(`/reader/sources/${id}/debug`, { key }, longOpts).then((r) => r.data.logs),
|
||
|
||
// ── 搜索 / 详情 / 目录 / 正文 ──
|
||
search: (key: string) =>
|
||
api
|
||
.post<{ books: ReaderSearchBook[]; skipped: ReaderSearchSkipped[] }>('/reader/search', { key }, longOpts)
|
||
.then((r) => r.data),
|
||
bookInfo: (params: { source_id?: string; source_url?: string; book_url: string }) =>
|
||
api.get<ReaderBookInfo>('/reader/book-info', { params, timeout: LONG_REQUEST_TIMEOUT }).then((r) => r.data),
|
||
toc: (params: { source_id?: string; source_url?: string; book_url: string; toc_url: string }) =>
|
||
api
|
||
.get<{ chapters: ReaderTocChapter[] }>('/reader/toc', { params, timeout: LONG_REQUEST_TIMEOUT })
|
||
.then((r) => r.data.chapters),
|
||
content: (params: { source_id?: string; source_url?: string; book_url: string; chapter_url: string }) =>
|
||
api
|
||
.get<ReaderChapterContent>('/reader/content', { params, timeout: LONG_REQUEST_TIMEOUT })
|
||
.then((r) => r.data),
|
||
|
||
// ── 书架 ──
|
||
listBooks: () => api.get<{ books: ReaderBook[] }>('/reader/books').then((r) => r.data.books),
|
||
addBook: (body: { origin: ReaderSearchOrigin; name: string; author: string; cover_url: string }) =>
|
||
api.post<ReaderBook>('/reader/books', body).then((r) => r.data),
|
||
removeBook: (id: string) => api.delete(`/reader/books/${id}`),
|
||
saveProgress: (id: string, body: { chapter_index: number; pos: number; chapter_title: string }) =>
|
||
api.put(`/reader/books/${id}/progress`, body),
|
||
listChapters: (id: string) =>
|
||
api.get<{ chapters: ReaderChapter[] }>(`/reader/books/${id}/chapters`).then((r) => r.data.chapters),
|
||
saveChapters: (id: string, chapters: ReaderChapter[]) => api.post(`/reader/books/${id}/chapters`, { chapters }),
|
||
// 书架维度正文(服务端已应用书源 replaceRegex 与用户替换净化规则)
|
||
bookContent: (id: string, chapter: number) =>
|
||
api
|
||
.get<ReaderChapterContent>(`/reader/books/${id}/content`, { params: { chapter }, timeout: LONG_REQUEST_TIMEOUT })
|
||
.then((r) => r.data),
|
||
|
||
// ── 替换净化规则 ──
|
||
listReplaceRules: () =>
|
||
api.get<{ rules: ReaderReplaceRule[] }>('/reader/replace-rules').then((r) => r.data.rules),
|
||
createReplaceRule: (body: ReplaceRuleInput) =>
|
||
api.post<ReaderReplaceRule>('/reader/replace-rules', body).then((r) => r.data),
|
||
updateReplaceRule: (id: string, body: ReplaceRuleInput) => api.patch(`/reader/replace-rules/${id}`, body),
|
||
deleteReplaceRule: (id: string) => api.delete(`/reader/replace-rules/${id}`),
|
||
}
|
||
|
||
export interface ReplaceRuleInput {
|
||
name: string
|
||
group: string
|
||
pattern: string
|
||
replacement: string
|
||
scope: string
|
||
scope_title: boolean
|
||
scope_content: boolean
|
||
exclude_scope: string
|
||
is_enabled: boolean
|
||
is_regex: boolean
|
||
timeout_millisecond: number
|
||
order: number
|
||
}
|