|
|
@@ -1,4 +1,5 @@ |
|
|
|
import axios from 'axios' |
|
|
|
import crypto from 'crypto' |
|
|
|
import fs from 'fs' |
|
|
|
import path from 'path' |
|
|
|
|
|
|
@@ -14,7 +15,10 @@ const SITE_TITLE = 'ぼざクリ タグ広場' |
|
|
|
const DOMAIN = 'https://hub.nizika.monster' |
|
|
|
const API_BASE_URL = 'https://hub.nizika.monster/api' |
|
|
|
|
|
|
|
const fetchPosts = async () => (await axios.get (`${ API_BASE_URL }/posts`)).data.posts |
|
|
|
const fetchPosts = async tagName => (await axios.get (`${ API_BASE_URL }/posts`, |
|
|
|
{ params: { ...(tagName && { tags: tagName, |
|
|
|
match: 'all', |
|
|
|
limit: '20' }) } })).data.posts |
|
|
|
const fetchPostIds = async () => (await fetchPosts ()).map (post => post.id) |
|
|
|
|
|
|
|
const fetchTags = async () => (await axios.get (`${ API_BASE_URL }/tags`)).data |
|
|
@@ -23,20 +27,82 @@ 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)).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.url }" /> |
|
|
|
</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 }` ]) |
|
|
|
`${ post.title || post.url } | ${ SITE_TITLE }`, |
|
|
|
createPostDetailOutlet (post)]) |
|
|
|
|
|
|
|
const tags = (await fetchTags ()).map (tag => [ |
|
|
|
`/posts?${ new URLSearchParams ({ tags: tag.name }) }`, |
|
|
|
`${ tag.name } | ${ SITE_TITLE }`]) |
|
|
|
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 } 〜 ぼざろクリーチャーシリーズ綜合リンク集サイト`], |
|
|
|
['/', `${ 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 }`], |
|
|
@@ -70,3 +136,29 @@ ${ routes.map (([route, title]) => ` <item> |
|
|
|
|
|
|
|
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 #', '')) |