Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-03-14 02:15:09 +07:00
parent ac9cecdcdb
commit 7f7ee254d8
9 changed files with 241 additions and 87 deletions
+83 -1
View File
@@ -11,6 +11,8 @@ import { convert } from "html-to-text"
import { slugify } from "@/lib/utils"
import { deleteR2ObjectByUrl, uploadBufferToR2 } from "@/lib/r2"
export const maxDuration = 900
type SplitMode = "toc" | "regex"
type SeriesMode = "none" | "existing" | "new"
@@ -38,6 +40,81 @@ interface EpubCoverAsset {
sourceId: string | null
}
type EpubCtor = new (epubfile: string, imagewebroot: string, chapterwebroot: string) => any
function sanitizeEpubNavMapBranch(branch: any): any {
if (Array.isArray(branch)) {
branch.forEach((item) => sanitizeEpubNavMapBranch(item))
return branch
}
if (!branch || typeof branch !== "object") {
return branch
}
if ("navLabel" in branch) {
const navLabel = (branch as any).navLabel
if (typeof navLabel === "string") {
;(branch as any).navLabel = navLabel
} else if (navLabel && typeof navLabel === "object") {
if (typeof navLabel.text === "string") {
;(branch as any).navLabel = navLabel.text
} else if (navLabel.text !== undefined && navLabel.text !== null) {
;(branch as any).navLabel = String(navLabel.text)
} else {
;(branch as any).navLabel = ""
}
} else if (navLabel === null || navLabel === undefined) {
;(branch as any).navLabel = ""
} else {
;(branch as any).navLabel = String(navLabel)
}
}
if ((branch as any).navPoint) {
sanitizeEpubNavMapBranch((branch as any).navPoint)
}
return branch
}
function getPatchedEpubCtor(): EpubCtor {
const loaded = require("epub2")
const EPub = (loaded?.EPub || loaded) as EpubCtor
const proto = (EPub as any)?.prototype
if (proto && !proto.__readerSafeNavLabelPatchApplied && typeof proto.walkNavMap === "function") {
const originalWalkNavMap = proto.walkNavMap
proto.walkNavMap = function patchedWalkNavMap(branch: any, ...args: any[]) {
const safeBranch = sanitizeEpubNavMapBranch(branch)
return originalWalkNavMap.call(this, safeBranch, ...args)
}
proto.__readerSafeNavLabelPatchApplied = true
}
return EPub
}
function isRequestAbortedError(error: unknown): boolean {
if (!error) return false
const candidate = error as { code?: unknown; message?: unknown; name?: unknown }
const code = typeof candidate.code === "string" ? candidate.code.toUpperCase() : ""
const message = typeof candidate.message === "string" ? candidate.message.toLowerCase() : ""
const name = typeof candidate.name === "string" ? candidate.name.toLowerCase() : ""
return (
code === "ECONNRESET" ||
code === "ABORT_ERR" ||
message.includes("aborted") ||
message.includes("connection reset") ||
name.includes("abort")
)
}
const CHAPTER_REGEX_PRESETS: Record<string, string> = {
vi_chuong: "^(?:Chương|Ch\\.)\\s*\\d+(?:\\.\\d+)?[^\\n]*$",
en_chapter: "^(?:Chapter|Ch\\.)\\s*\\d+(?:\\.\\d+)?[^\\n]*$",
@@ -838,7 +915,7 @@ async function saveCoverBufferToR2(cover: EpubCoverAsset): Promise<string | null
async function parseEpubSections(tempFilePath: string): Promise<{ metadata: any; sections: EpubSection[]; cover: EpubCoverAsset }> {
return new Promise((resolve, reject) => {
const EPub = require("epub2").EPub || require("epub2")
const EPub = getPatchedEpubCtor()
const epub = new EPub(tempFilePath, "", "")
epub.on("error", (err: any) => reject(err))
@@ -1168,6 +1245,11 @@ export async function POST(req: Request) {
replaced,
}, { status: responseStatus })
} catch (error: any) {
if (isRequestAbortedError(error)) {
console.warn("EPUB upload aborted by client or network interruption")
return NextResponse.json({ error: "Kết nối upload bị ngắt trong lúc xử lý" }, { status: 499 })
}
console.error("EPUB upload error:", error)
return NextResponse.json({ error: "Lỗi xử lý file EPUB", details: error.message }, { status: 500 })
}
+27 -22
View File
@@ -7,6 +7,28 @@ function toUTCDateOnly(value: Date): Date {
return new Date(Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate()))
}
async function upsertDailyNovelView(novelId: string, day: Date) {
const delegate = (prisma as any).novelViewDaily
if (!delegate || typeof delegate.upsert !== "function") return
await delegate.upsert({
where: {
novelId_day: {
novelId,
day,
},
},
update: {
views: { increment: 1 },
},
create: {
novelId,
day,
views: 1,
},
})
}
// Lấy danh sách bookmark
export async function GET(req: Request) {
try {
@@ -140,28 +162,11 @@ export async function POST(req: Request) {
if (shouldIncrementNovelView) {
const day = toUTCDateOnly(new Date())
await prisma.$transaction([
prisma.novel.update({
where: { id: novelId },
data: { views: { increment: 1 } }
}),
prisma.novelViewDaily.upsert({
where: {
novelId_day: {
novelId,
day,
},
},
update: {
views: { increment: 1 },
},
create: {
novelId,
day,
views: 1,
},
}),
])
await prisma.novel.update({
where: { id: novelId },
data: { views: { increment: 1 } }
})
await upsertDailyNovelView(novelId, day)
}
return NextResponse.json({ status: "updated", bookmark })