redirecter for ao3 that adds opengraph metadata

fix generator output

Changed files
+40 -27
src
app
works
[workId]
chapters
[chapterId]
lib
+13 -5
src/app/works/[workId]/chapters/[chapterId]/page.js
···
import { getWork } from "@fujocoded/ao3.js"
import DOM from "fauxdom"
+
import siteMap from "@/lib/siteMap.js"
export async function generateMetadata({ params, _searchParams }, _parent) {
// read route params
const { workId, chapterId } = await params
-
const work = await getWork({workId: workId, chapterId: chapterId})
+
const parentData = await parent;
+
const base = parentData.metadataBase.replace('https://', '').replace('/', '')
+
const subdomain = base.split(".").length > 0 ? base.split(".")[0] : null
+
const archive = subdomain && Object.keys(siteMap).includes(subdomain) ? siteMap[subdomain] : null
+
const domainParam = archive ? `?archive=https://${archive}` : ''
+
const data = await fetch(`/api/works/${workId}/chapters/${chapterId}${domainParam}`)
+
const work = await data.json()
if (work.locked) {
return {
title: 'Locked Work',
description: 'This work is locked to the public. Log in to see it!',
-
metadataBase: new URL('https://'+process.env.DOMAIN)
+
metadataBase: new URL('https://'+archive)
}
}
-
const parentWork = await getWork({workId: workId})
+
const parentWorkData = await fetch(`/api/works/${workId}`)
+
const parentWork = await data.json()
const authors = work.authors.map((a) => {
if (a.anonymous) {
return "Anonymous"
···
return {
title: title,
description: description,
-
metadataBase: new URL('https://'+process.env.DOMAIN)
+
metadataBase: new URL('https://'+base)
}
}
···
const { workId, chapterId } = await params
return (
<div dangerouslySetInnerHTML={{__html: `<script type="text/javascript">
-
window.location.replace("https://archiveofourown.org/works/${workId}/chapters/${chapterId}");
+
window.location.replace("https://archiveofourown.org/works/${workId}");
</script>`}}></div>
)
}
+5 -3
src/app/works/[workId]/page.js
···
import { getWork } from "@fujocoded/ao3.js"
import DOM from "fauxdom"
-
export async function generateMetadata({ params, _searchParams }, _parent) {
+
export async function generateMetadata({ params, _searchParams }, parent) {
// read route params
const { workId, chapterId } = await params
const work = await getWork({workId: workId, chapterId: chapterId})
···
"",
)
const addr = `works/${workId}`
+
const parentData = await parent;
+
const base = parentData.metadataBase.replace('https://', '').replace('/', '')
return {
title: title ? title : 'Locked Work',
description: description ? description : 'This but this work is locked to the public. Log in to see it!',
-
metadataBase: new URL('https://'+process.env.DOMAIN)
+
metadataBase: new URL('https://'+base)
}
}
···
const { workId, chapterId } = await params
return (
<div dangerouslySetInnerHTML={{__html: `<script type="text/javascript">
-
window.location.replace("https://archiveofourown.org/works/${workId}");
+
window.location.replace("https://archiveofourown.org/works/${workId}/chapters/${chapterId}");
</script>`}}></div>
)
}
+18 -18
src/lib/ogimage.js
···
gap: 10
}}
>
-
{image.props.rating && image.rating === 'E' && (<Explicit fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
-
{image.props.rating && image.rating === 'M' && (<Mature fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
-
{image.props.rating && image.rating === 'T' && (<Teen fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
-
{image.props.rating && image.rating === 'G' && (<General fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
-
{image.props.rating && image.rating === 'NR' && (<NotRated fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
+
{image.props.get('rating') && image.rating === 'E' && (<Explicit fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
+
{image.props.get('rating') && image.rating === 'M' && (<Mature fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
+
{image.props.get('rating') && image.rating === 'T' && (<Teen fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
+
{image.props.get('rating') && image.rating === 'G' && (<General fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
+
{image.props.get('rating') && image.rating === 'NR' && (<NotRated fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
-
{image.props.warnings && image.warning === 'NW' && (<NoWarnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
-
{image.props.warnings && image.warning === 'CNTW' && (<ChoseNotToWarn fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
-
{image.props.warnings && image.warning === 'W' && (<Warnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
+
{image.props.get('warnings') && image.warning === 'NW' && (<NoWarnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
+
{image.props.get('warnings') && image.warning === 'CNTW' && (<ChoseNotToWarn fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
+
{image.props.get('warnings') && image.warning === 'W' && (<Warnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
-
{image.props.category && image.category === 'F' && (<Yuri fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
-
{image.props.category && image.category === 'M' && (<Yaoi fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
-
{image.props.category && image.category === 'FM' && (<Het fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
-
{image.props.category && image.category === 'G' && (<Gen fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
-
{image.props.category && image.category === 'MX' && (<MultiShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
-
{image.props.category && image.category === 'O' && (<OtherShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
+
{image.props.get('category') && image.category === 'F' && (<Yuri fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
+
{image.props.get('category') && image.category === 'M' && (<Yaoi fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
+
{image.props.get('category') && image.category === 'FM' && (<Het fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
+
{image.props.get('category') && image.category === 'G' && (<Gen fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
+
{image.props.get('category') && image.category === 'MX' && (<MultiShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
+
{image.props.get('category') && image.category === 'O' && (<OtherShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
</div>
<div
style={{
···
alignItems: "flex-end"
}}
>
-
{image.props.charTags && (<div
+
{image.props.get('charTags') && (<div
style={{
display: "flex",
flexWrap: "wrap",
···
</span>
))}
</div>)}
-
{image.props.relTags && (<div
+
{image.props.get('relTags') && (<div
style={{
display: "flex",
flexWrap: "wrap",
···
</span>
))}
</div>)}
-
{image.props.summary && (<div
+
{image.props.get('summary') && (<div
style={{
display: "flex",
flexDirection: "column",
···
color: theme.accent2
}}
>
-
{image.props.wordcount && `${image.words} words • `}{(image.props.chapters && image.chapterCount !== null) && `${image.chapterCount} chapters • `}{image.props.postedAt && `posted on ${image.postedAt} • `}{image.props.updatedAt && `updated on ${image.updatedAt} • `}{addr}
+
{image.props.get('wordcount') && `${image.words} words • `}{(image.props.get('chapters') && image.chapterCount !== null) && `${image.chapterCount} chapters • `}{image.props.get('postedAt') && `posted on ${image.postedAt} • `}{image.props.get('updatedAt') && `updated on ${image.updatedAt} • `}{addr}
</div>
</div>
</div>
+4 -1
src/lib/sanitizeData.js
···
export default async function sanitizeData ({ type, data, props}) {
const archive = props && props.archive ? props.archive : process.env.ARCHIVE
+
console.log(props)
const baseFont = props.baseFont ? props.baseFont : process.env.DEFAULT_BASE_FONT
const baseFontData = baseFonts[baseFont]
const titleFont = props.titleFont ? props.titleFont : process.env.DEFAULT_TITLE_FONT
const titleFontData = titleFonts[titleFont]
-
const themeData = props.theme ? themes[props.theme] : themes[process.env.DEFAULT_THEME]
+
const archClean = props.has('archive') ? props.get('archive').replace("https://", '').replace('/', '') : null
+
const theme = props.has('theme') ? props.get('theme') : (props.has('archive') && !["ao3.org", "archiveofourown.org", "archive.transformativeworks.org"].includes(archClean) && Object.values(siteMap).includes(archClean) ? Object.keys(siteMap)[Object.values(siteMap).indexOf(archClean)] : process.env.DEFAULT_THEME)
+
const themeData = themes[theme]
const parentWork = type === 'work' && data.chapterInfo ? await getWork(data.id, archive) : null
const bfs = await Promise.all(baseFontData.defs.map(async (bf) => {
return {