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 fetchPostIds = async () => (await fetchPosts ()).map (post => post.id) const fetchTags = async () => (await axios.get (`${ API_BASE_URL }/tags`)).data 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 => `
${ (await fetchPosts (tagName)).map (post => ` ${ post.title } `).join ('') }
` const createPostDetailOutlet = post => `
${ ['deerjikist', 'meme', 'character', 'general', 'material', 'meta', 'nico'] .map (cat => '
    ' + post.tags.filter (tag => tag.category === cat).map (tag => `
  • ? ${ tag.name } ${ tag['post_count'] }
  • `).join ('') + '
').join ('') }
${ post.url }
` 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 (), ` `], ...tags, ...posts, ['/wiki', `Wiki | ${ SITE_TITLE }`], ...wikiPages] const xml = ` ${ routes.map (([route]) => ` ${ DOMAIN }${ route } ${ formatDate () } daily `).join ('\n') } ` const rss = ` The Series of Bocchi the Rock! Creatures Hub Feed ${ DOMAIN }/ 最新の投稿、タグ、Wiki ページの一覧 ja ${ (new Date).toUTCString () } ${ routes.map (([route, title]) => ` ${ title } ${ DOMAIN }${ route } ${ DOMAIN }${ route } ${ (new Date).toUTCString () } `).join ('\n') } ` 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) 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 #', ''))