ee93ff8ea0
#61 #61 Merge remote-tracking branch 'origin/main' into feature/061 #61 #61 #61 #61 #61 #61 #61 #61 #61 #61 日づけ不詳の表示修正 Co-authored-by: miteruzo <miteruzo@naver.com> Reviewed-on: #298
164 lines
5.9 KiB
JavaScript
164 lines
5.9 KiB
JavaScript
import axios from 'axios'
|
|
import crypto from 'crypto'
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
|
|
const formatDate = (date = new Date) => {
|
|
const year = date.getFullYear ()
|
|
const month = (date.getMonth () + 1).toString ().padStart (2, '0')
|
|
const day = date.getDate ().toString ().padStart (2, '0')
|
|
|
|
return `${ year }-${ month }-${ day }`
|
|
}
|
|
|
|
const SITE_TITLE = 'ぼざクリ タグ広場'
|
|
const DOMAIN = 'https://hub.nizika.monster'
|
|
const API_BASE_URL = 'https://hub.nizika.monster/api'
|
|
|
|
const fetchPosts = async tagName => (await axios.get (`${ API_BASE_URL }/posts`,
|
|
{ params: { ...(tagName && { tags: tagName,
|
|
match: 'all',
|
|
limit: '20' }) } })).data.posts
|
|
|
|
const fetchTags = async () => (await axios.get (`${ API_BASE_URL }/tags`)).data.tags
|
|
const fetchTagNames = async () => (await fetchTags ()).map (tag => tag.name)
|
|
|
|
const fetchWikiPages = async () => (await axios.get (`${ API_BASE_URL }/wiki`)).data
|
|
const fetchWikiTitles = async () => (await fetchWikiPages ()).map (page => page.title)
|
|
|
|
const createPostListOutlet = async tagName => `
|
|
<main class="flex-1 overflow-y-auto p-4">
|
|
<div class="mt-4">
|
|
<div class="flex gap-4"><a href="#" class="font-bold">広場</a></div>
|
|
<div class="mt-2">
|
|
<div class="flex flex-wrap gap-6 p-4">
|
|
${ (await fetchPosts (tagName)).slice (0, 20).map (post => `
|
|
<a class="w-40 h-40 overflow-hidden rounded-lg shadow-md hover:shadow-lg"
|
|
href="/posts/${ post.id }">
|
|
<img alt="${ post.title }"
|
|
title="${ post.title }"
|
|
loading="eager"
|
|
fetchpriority="high"
|
|
decoding="async"
|
|
class="object-none w-full h-full"
|
|
src="${ post.thumbnail }" />
|
|
</a>`).join ('') }
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>`
|
|
|
|
const createPostDetailOutlet = post => `
|
|
<div class="md:flex md:flex-1">
|
|
<div>
|
|
<div>
|
|
${ ['deerjikist', 'meme', 'character', 'general', 'material', 'meta', 'nico']
|
|
.map (cat => '<ul>' + post.tags.filter (tag => tag.category === cat).map (tag => `
|
|
<li>
|
|
<span>
|
|
<a href="/wiki/${ encodeURIComponent (tag.name) }">?</a>
|
|
</span>
|
|
<a href="/posts?${ new URLSearchParams ({ tags: tag.name }) }">
|
|
${ tag.name }
|
|
</a>
|
|
<span>${ tag['post_count'] }</span>
|
|
</li>`).join ('') + '</ul>').join ('') }
|
|
</div>
|
|
</div>
|
|
<main class="flex-1 overflow-y-auto p-4">
|
|
<img src="${ post.thumbnail }" alt="${ post.url }" />
|
|
</main>
|
|
</div>`
|
|
|
|
const posts = (await fetchPosts ()).map (post => [
|
|
`/posts/${ post.id }`,
|
|
`${ post.title || post.url } | ${ SITE_TITLE }`,
|
|
createPostDetailOutlet (post)])
|
|
|
|
const tags = []
|
|
for (const tag of await fetchTags ())
|
|
{
|
|
tags.push ([`/posts?${ new URLSearchParams ({ tags: tag.name }) }`,
|
|
`${ tag.name } | ${ SITE_TITLE }`,
|
|
await createPostListOutlet (tag.name)])
|
|
}
|
|
|
|
const wikiPages = (await fetchWikiPages ()).map (page => [
|
|
`/wiki/${ encodeURIComponent (page.title) }`,
|
|
`${ page.title } Wiki | ${ SITE_TITLE }`])
|
|
|
|
const routes = [
|
|
['/', `${ SITE_TITLE } 〜 ぼざろクリーチャーシリーズ綜合リンク集`,
|
|
await createPostListOutlet (), `
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "http://schema.org",
|
|
"@type": "WebSite",
|
|
"url": "https://hub.nizika.monster",
|
|
"potentialAction": {
|
|
"@type": "SearchAction",
|
|
"target": "https://hub.nizika.monster/posts?tags={search_term}",
|
|
"query-input": "required name=search_term"
|
|
}
|
|
}
|
|
</script>`],
|
|
...tags,
|
|
...posts,
|
|
['/wiki', `Wiki | ${ SITE_TITLE }`],
|
|
...wikiPages]
|
|
|
|
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
${ routes.map (([route]) => ` <url>
|
|
<loc>${ DOMAIN }${ route }</loc>
|
|
<lastmod>${ formatDate () }</lastmod>
|
|
<changefreq>daily</changefreq>
|
|
</url>`).join ('\n') }
|
|
</urlset>`
|
|
|
|
const rss = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<rss version="2.0">
|
|
<channel>
|
|
<title>The Series of Bocchi the Rock! Creatures Hub Feed</title>
|
|
<link>${ DOMAIN }/</link>
|
|
<description>最新の投稿、タグ、Wiki ページの一覧</description>
|
|
<language>ja</language>
|
|
<pubDate>${ (new Date).toUTCString () }</pubDate>
|
|
${ routes.map (([route, title]) => ` <item>
|
|
<title>${ title }</title>
|
|
<link>${ DOMAIN }${ route }</link>
|
|
<guid>${ DOMAIN }${ route }</guid>
|
|
<pubDate>${ (new Date).toUTCString () }</pubDate>
|
|
</item>`).join ('\n') }
|
|
</channel>
|
|
</rss>`
|
|
|
|
fs.writeFileSync (path.resolve ('dist/sitemap.xml'), xml)
|
|
fs.writeFileSync (path.resolve ('dist/rss.xml'), rss)
|
|
|
|
const baseHTML = fs.readFileSync (path.resolve ('dist/index.html'), 'utf8')
|
|
let htaccess = fs.readFileSync (path.resolve ('dist/.htaccess'), 'utf8')
|
|
routes.forEach (([route, title, outlet, helmet]) => {
|
|
const fileName = (
|
|
route === '/'
|
|
? 'index.html'
|
|
: `${ crypto.createHash ('sha256').update (encodeURIComponent (route)).digest ('hex') }.html`)
|
|
|
|
let html = baseHTML.replace (/(?<=<title>).*?(?=<\/title>)/, title)
|
|
html = html.replace ('<!-- outlet -->', outlet || '')
|
|
html = html.replace ('<!-- helmet -->', helmet || '')
|
|
fs.writeFileSync (path.resolve (`dist/${ fileName }`), html)
|
|
|
|
const [routeBase, q] = route.split ('?')
|
|
if (route !== '/')
|
|
{
|
|
htaccess = htaccess.replace ('# routes #', `
|
|
RewriteCond %{REQUEST_URI} ^${ routeBase }$
|
|
${ q ? `RewriteCond %{QUERY_STRING} (^|&)${ q }(&|$)` : '' }
|
|
RewriteRule ^ /${ fileName } [L]
|
|
# routes #
|
|
`)
|
|
}
|
|
})
|
|
fs.writeFileSync (path.resolve ('dist/.htaccess'), htaccess.replace ('# routes #', ''))
|