feat: comic 페이지에 대한 헤더 설정 기능 추가 및 라우터 개선
This commit is contained in:
parent
e2c451c708
commit
fe5ed4c4aa
2 changed files with 83 additions and 27 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import type { Context as ElysiaContext } from "elysia";
|
||||
import { Readable } from "node:stream";
|
||||
import { createReadableStreamFromZip, entriesByNaturalOrder, readZip } from "../util/zipwrap.ts";
|
||||
import { Entry } from "@zip.js/zip.js";
|
||||
|
||||
const imageExtensions = new Set(["gif", "png", "jpeg", "bmp", "webp", "jpg", "avif"]);
|
||||
|
||||
|
|
@ -18,6 +19,50 @@ type RenderOptions = {
|
|||
set: ResponseSet;
|
||||
};
|
||||
|
||||
async function setHeadersForEntry(entry: Entry, reqHeaders: Headers, set: ResponseSet) {
|
||||
const ext = entry.filename.split(".").pop()?.toLowerCase() ?? "jpeg";
|
||||
|
||||
set.headers["content-type"] = extensionToMime(ext);
|
||||
if (typeof entry.uncompressedSize === "number") {
|
||||
set.headers["content-length"] = entry.uncompressedSize;
|
||||
}
|
||||
|
||||
const lastModified = entry.lastModDate ?? new Date();
|
||||
const ifModifiedSince = reqHeaders.get("if-modified-since");
|
||||
set.headers["date"] = new Date().toUTCString();
|
||||
set.headers["last-modified"] = lastModified.toUTCString();
|
||||
if (ifModifiedSince) {
|
||||
const cachedDate = new Date(ifModifiedSince);
|
||||
if (!Number.isNaN(cachedDate.valueOf()) && lastModified <= cachedDate) {
|
||||
set.status = 304;
|
||||
// client's cache is valid
|
||||
return true;
|
||||
}
|
||||
}
|
||||
set.status = 200;
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function headComicPage({ path, page, reqHeaders, set }: RenderOptions) {
|
||||
const zip = await readZip(path);
|
||||
try {
|
||||
const entries = (await entriesByNaturalOrder(zip.reader)).filter((entry) => {
|
||||
const ext = entry.filename.split(".").pop()?.toLowerCase();
|
||||
return ext !== undefined && imageExtensions.has(ext);
|
||||
});
|
||||
if (page < 0 || page >= entries.length) {
|
||||
set.status = 404;
|
||||
return;
|
||||
}
|
||||
const entry = entries[page];
|
||||
if (await setHeadersForEntry(entry, reqHeaders, set)) {
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
await zip.reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
export async function renderComicPage({ path, page, reqHeaders, set }: RenderOptions) {
|
||||
const zip = await readZip(path);
|
||||
|
||||
|
|
@ -29,37 +74,16 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
|
|||
|
||||
if (page < 0 || page >= entries.length) {
|
||||
set.status = 404;
|
||||
await zip.reader.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
const entry = entries[page];
|
||||
const lastModified = entry.lastModDate ?? new Date();
|
||||
const ifModifiedSince = reqHeaders.get("if-modified-since");
|
||||
|
||||
const headers = (set.headers ??= {} as Record<string, string | number>);
|
||||
headers["Date"] = new Date().toUTCString();
|
||||
headers["Last-Modified"] = lastModified.toUTCString();
|
||||
|
||||
if (ifModifiedSince) {
|
||||
const cachedDate = new Date(ifModifiedSince);
|
||||
if (!Number.isNaN(cachedDate.valueOf()) && lastModified <= cachedDate) {
|
||||
set.status = 304;
|
||||
await zip.reader.close();
|
||||
if (await setHeadersForEntry(entry, reqHeaders, set)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const readStream = await createReadableStreamFromZip(zip.reader, entry);
|
||||
|
||||
const ext = entry.filename.split(".").pop()?.toLowerCase() ?? "jpeg";
|
||||
headers["Content-Type"] = extensionToMime(ext);
|
||||
if (typeof entry.uncompressedSize === "number") {
|
||||
headers["Content-Length"] = entry.uncompressedSize.toString();
|
||||
}
|
||||
|
||||
set.status = 200;
|
||||
|
||||
// Ensure zip file is closed after stream ends
|
||||
const streamWithCleanup = new ReadableStream({
|
||||
async start(controller) {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ import type { DocumentAccessor } from "../model/doc.ts";
|
|||
import { AdminOnly, createPermissionCheck, Permission as Per } from "../permission/permission.ts";
|
||||
import { sendError } from "./error_handler.ts";
|
||||
import { oshash } from "src/util/oshash.ts";
|
||||
import { renderComicPage } from "./comic.ts";
|
||||
import { headComicPage, renderComicPage } from "./comic.ts";
|
||||
|
||||
export const getContentRouter = (controller: DocumentAccessor) => {
|
||||
return new Elysia({ name: "content-router",
|
||||
return new Elysia({
|
||||
name: "content-router",
|
||||
prefix: "/doc",
|
||||
})
|
||||
.get("/search", async ({ query }) => {
|
||||
|
|
@ -156,6 +157,21 @@ export const getContentRouter = (controller: DocumentAccessor) => {
|
|||
}
|
||||
return { document, docId };
|
||||
})
|
||||
.head("/comic/thumbnail", async ({ document, request, set }) => {
|
||||
if (document.content_type !== "comic") {
|
||||
throw sendError(404);
|
||||
}
|
||||
const path = join(document.basepath, document.filename);
|
||||
await headComicPage({
|
||||
path,
|
||||
page: 0,
|
||||
reqHeaders: request.headers,
|
||||
set,
|
||||
});
|
||||
}, {
|
||||
beforeHandle: createPermissionCheck(Per.QueryContent),
|
||||
params: t.Object({ num: t.Numeric() }),
|
||||
})
|
||||
.get("/comic/thumbnail", async ({ document, request, set }) => {
|
||||
if (document.content_type !== "comic") {
|
||||
throw sendError(404);
|
||||
|
|
@ -172,6 +188,22 @@ export const getContentRouter = (controller: DocumentAccessor) => {
|
|||
beforeHandle: createPermissionCheck(Per.QueryContent),
|
||||
params: t.Object({ num: t.Numeric() }),
|
||||
})
|
||||
.head("/comic/:page", async ({ document, params: { page }, request, set }) => {
|
||||
if (document.content_type !== "comic") {
|
||||
throw sendError(404);
|
||||
}
|
||||
const pageIndex = page;
|
||||
const path = join(document.basepath, document.filename);
|
||||
await headComicPage({
|
||||
path,
|
||||
page: pageIndex,
|
||||
reqHeaders: request.headers,
|
||||
set,
|
||||
});
|
||||
}, {
|
||||
beforeHandle: createPermissionCheck(Per.QueryContent),
|
||||
params: t.Object({ num: t.Numeric(), page: t.Numeric() }),
|
||||
})
|
||||
.get("/comic/:page", async ({ document, params: { page }, request, set }) => {
|
||||
if (document.content_type !== "comic") {
|
||||
throw sendError(404);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue