diff --git a/packages/client/src/hook/useGalleryDoc.ts b/packages/client/src/hook/useGalleryDoc.ts index 2708d70..9b24177 100644 --- a/packages/client/src/hook/useGalleryDoc.ts +++ b/packages/client/src/hook/useGalleryDoc.ts @@ -1,7 +1,17 @@ -import useSWR from "swr"; +import useSWR, { useSWRConfig } from "swr"; import type { Document } from "dbtype"; import { fetcher } from "./fetcher"; export function useGalleryDoc(id: string) { return useSWR(`/api/doc/${id}`, fetcher); +} + +export function useRehashDoc() { + const { mutate } = useSWRConfig(); + return async (id: string) => { + await fetch(`/api/doc/${id}/_rehash`, { + method: "POST", + }); + mutate(`/api/doc/${id}`); + }; } \ No newline at end of file diff --git a/packages/client/src/page/contentInfoPage.tsx b/packages/client/src/page/contentInfoPage.tsx index 9a86480..5438dec 100644 --- a/packages/client/src/page/contentInfoPage.tsx +++ b/packages/client/src/page/contentInfoPage.tsx @@ -1,10 +1,11 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { useGalleryDoc } from "../hook/useGalleryDoc.ts"; +import { useGalleryDoc, useRehashDoc } from "../hook/useGalleryDoc.ts"; import TagBadge from "@/components/gallery/TagBadge"; import StyledLink from "@/components/gallery/StyledLink"; import { Link } from "wouter"; import { classifyTags } from "../lib/classifyTags.tsx"; import { DescTagItem, DescItem } from "../components/gallery/DescItem.tsx"; +import { Button } from "@/components/ui/button.tsx"; export interface ContentInfoPageProps { params: { @@ -14,6 +15,7 @@ export interface ContentInfoPageProps { export function ContentInfoPage({ params }: ContentInfoPageProps) { const { data, error, isLoading } = useGalleryDoc(params.id); + const rehashDoc = useRehashDoc(); if (isLoading) { return
Loading...
@@ -43,7 +45,13 @@ export function ContentInfoPage({ params }: ContentInfoPageProps) { alt={data.title} /> - + +
+ +
diff --git a/packages/server/src/route/contents.ts b/packages/server/src/route/contents.ts index 8c37077..811b6b8 100644 --- a/packages/server/src/route/contents.ts +++ b/packages/server/src/route/contents.ts @@ -15,6 +15,8 @@ import { AllContentRouter } from "./all.ts"; import type { ContentLocation } from "./context.ts"; import { sendError } from "./error_handler.ts"; import { ParseQueryArgString, ParseQueryArray, ParseQueryBoolean, ParseQueryNumber } from "./util.ts"; +import { oshash } from "src/util/oshash.ts"; + const ContentIDHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { const num = Number.parseInt(ctx.params.num); @@ -154,6 +156,34 @@ const ContentHandler = (controller: DocumentAccessor) => async (ctx: Context, ne await next(); }; +function RehashContentHandler(controller: DocumentAccessor) { + return async (ctx: Context, next: Next) => { + const num = Number.parseInt(ctx.params.num); + const c = await controller.findById(num); + if (c === undefined || c.deleted_at !== null) { + return sendError(404); + } + const filepath = join(c.basepath, c.filename); + let new_hash: string; + try { + new_hash = (await oshash(filepath)).toString(); + } + catch (e) { + // if file is not found, return 404 + if ( (e as NodeJS.ErrnoException).code === "ENOENT") { + return sendError(404, "file not found"); + } + throw e; + } + const r = await controller.update({ + id: num, + content_hash: new_hash, + }); + ctx.body = JSON.stringify(r); + ctx.type = "json"; + }; +} + export const getContentRouter = (controller: DocumentAccessor) => { const ret = new Router(); ret.get("/search", PerCheck(Per.QueryContent), ContentQueryHandler(controller)); @@ -167,6 +197,7 @@ export const getContentRouter = (controller: DocumentAccessor) => { ret.del("/:num(\\d+)", AdminOnly, DeleteContentHandler(controller)); ret.all("/:num(\\d+)/(.*)", PerCheck(Per.QueryContent), ContentHandler(controller)); ret.use("/:num(\\d+)", PerCheck(Per.QueryContent), new AllContentRouter().routes()); + ret.post("/:num(\\d+)/_rehash", AdminOnly, RehashContentHandler(controller)); return ret; };