redirecter for ao3 that adds opengraph metadata
1import { getSeries, getUser, getWork } from "@fujocoded/ao3.js"
2import { Hono } from "hono"
3import PageSkeleton from "./pages/PageSkeleton.jsx"
4import Home from "./pages/Home.jsx"
5import Image from "./pages/Image.jsx"
6import {
7 createIPX,
8 ipxFSStorage,
9 createIPXNodeServer
10} from "ipx"
11
12const ipx = createIPX({
13 storage: ipxFSStorage({ dir: "./imagecache" })
14})
15
16const app = new Hono()
17
18app.use("/", createIPXNodeServer(ipx))
19
20app.get("/", (c) => {
21 return c.html(<Home />)
22})
23
24app.get("/works/:workId", async (c) => {
25 const workId = c.req.param("workId")
26 const work = await getWork({
27 workId: c.req.param("workId"),
28 chapterId: c.req.param("chapterId"),
29 })
30 const authorsFormatted = work.authors.map((a) => {
31 if (a.anonymous) return "Anonymous"
32 if (a.pseud !== a.username) return `${a.pseud} (${a.username})`
33 return a.username
34 })
35 const authors = authorsFormatted.length > 1
36 ? authorsFormatted.slice(0, -1).join(", ") + " & " +
37 authorsFormatted.slice(-1)
38 : authorsFormatted[0]
39 const title = `${work.title} by ${authors} - ${work.fandoms.join(", ")}`
40 const desc = `Rating: ${work.rating} | ${work.category} | Updated ${
41 work.updatedAt ? work.updatedAt : work.publishedAt
42 } | Words: ${work.words} | ${
43 work.complete ? "Complete | " : ""
44 } ${work.summary}`
45 return c.html(
46 <PageSkeleton title={title} description={desc} addr={`works/${workId}`} />,
47 )
48})
49
50app.get("/works/:workId/chapters/:chapterId", async (c) => {
51 const workId = c.req.param("workId")
52 const chapterId = c.req.param("chapterId")
53 const work = await getWork({
54 workId: c.req.param("workId"),
55 chapterId: c.req.param("chapterId"),
56 })
57 const authorsFormatted = work.authors.map((a) => {
58 if (a.anonymous) return "Anonymous"
59 if (a.pseud !== a.username) return `${a.pseud} (${a.username})`
60 return a.username
61 })
62 const authors = authorsFormatted.length > 1
63 ? authorsFormatted.slice(0, -1).join(", ") + " & " +
64 authorsFormatted.slice(-1)
65 : authorsFormatted[0]
66 const title =
67 `${work.title} by ${authors}, Chapter ${work.chapterInfo.index}${
68 work.chapterInfo.name ? `: ${work.chapterInfo.name}` : ""
69 } - ${work.fandoms.join(", ")}`
70 const desc = `Rating: ${work.rating} | ${work.category} | Updated ${
71 work.updatedAt ? work.updatedAt : work.publishedAt
72 } | Words: ${work.words} | ${work.complete ? "Complete | " : ""} ${
73 work.chapterInfo.summary ? work.chapterInfo.summary : work.summary
74 }`
75 return c.html(
76 <PageSkeleton
77 title={title}
78 description={desc}
79 addr={`works/${workId}/chapters/${chapterId}`}
80 />,
81 )
82})
83
84app.get("/series/:seriesId", async (c) => {
85 const seriesId = c.req.param("seriesId")
86 const series = await getSeries({ seriesId: seriesId })
87 const authorsFormatted = series.authors.map((a) => {
88 if (a.anonymous) return "Anonymous"
89 if (a.pseud !== a.username) return `${a.pseud} (${a.username})`
90 return a.username
91 })
92 const authors = authorsFormatted.length > 1
93 ? authorsFormatted.slice(0, -1).join(", ") + " & " +
94 authorsFormatted.slice(-1)
95 : authorsFormatted[0]
96 const title = `${series.name} by ${authors}`
97 const desc = ` Updated ${
98 series.updatedAt ? series.updatedAt : series.publishedAt
99 } | Works: ${series.worksCount} | ${
100 series.complete ? "Complete | " : ""
101 } ${series.notes}`
102 return c.html(
103 <PageSkeleton
104 title={title}
105 description={desc}
106 addr={`series/${seriesId}`}
107 />,
108 )
109})
110
111app.get("/users/:username", async (c) => {
112 const username = c.req.param("username")
113 const user = await getUser({ username: username })
114 return c.html(
115 <PageSkeleton
116 title={`${username}`}
117 description={user.header}
118 addr={`users/${username}`}
119 />,
120 )
121})
122
123app.get("/users/:username/pseuds/:pseud", async (c) => {
124 const username = c.req.param("username")
125 const pseud = c.req.param("pseud")
126 const user = await getUser({ username: username })
127 return c.html(
128 <PageSkeleton
129 title={`${pseud} (${username})`}
130 description={user.header}
131 addr={`users/${username}`}
132 />,
133 )
134})
135
136app.get("/preview/works/:workId", async (c) => {
137 const workId = c.req.param("workId")
138 const work = await getWork({
139 workId: workId,
140 })
141 const addr = `works/${workId}`
142 return Image({ data: work, addr: addr })
143})
144
145app.get("/preview/works/:workId/chapters/:chapterId", async (c) => {
146 const workId = c.req.param("workId")
147 const chapterId = c.req.param("chapterId")
148 const work = await getWork({
149 workId: workId,
150 chapterId: chapterId,
151 })
152 const addr = `works/${workId}/chapters/${chapterId}`
153 return Image({ data: work, addr: addr })
154})
155
156export default app