Use built-in static server for Mosaic
This commit is contained in:
parent
399bc04688
commit
5f1ba2938c
File diff suppressed because it is too large
Load Diff
|
|
@ -6,7 +6,7 @@
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"prebuild": "tsx scripts/validate-claims.ts && tsx scripts/generate-sitemap.ts && tsx scripts/generate-rss.ts && tsx scripts/generate-llms-txt.ts && tsx scripts/generate-claims-api.ts && tsx scripts/generate-page-markdown.ts",
|
"prebuild": "tsx scripts/validate-claims.ts && tsx scripts/generate-sitemap.ts && tsx scripts/generate-rss.ts && tsx scripts/generate-llms-txt.ts && tsx scripts/generate-claims-api.ts && tsx scripts/generate-page-markdown.ts",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "serve -s out -l 3000",
|
"start": "node server.js",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"add-claim-from-json": "tsx scripts/add-claim-to-data.ts",
|
"add-claim-from-json": "tsx scripts/add-claim-to-data.ts",
|
||||||
"check-new-articles": "tsx scripts/check-new-articles.ts",
|
"check-new-articles": "tsx scripts/check-new-articles.ts",
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
"next": "^15.5.12",
|
"next": "^15.5.12",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"serve": "^14.2.5",
|
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"three": "^0.183.1"
|
"three": "^0.183.1"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"totalArticles": 83,
|
"totalArticles": 83,
|
||||||
"totalClaims": 415,
|
"totalClaims": 415,
|
||||||
"lastUpdated": "2026-03-05T20:14:36.933Z",
|
"lastUpdated": "2026-03-05T20:17:13.100Z",
|
||||||
"latestArticleDate": "2026-02-14",
|
"latestArticleDate": "2026-02-14",
|
||||||
"topics": [
|
"topics": [
|
||||||
"business",
|
"business",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<link>https://kbanc.com</link>
|
<link>https://kbanc.com</link>
|
||||||
<description>Evidence-based claims about AI implementation, optimized for LLM extraction and research citation.</description>
|
<description>Evidence-based claims about AI implementation, optimized for LLM extraction and research citation.</description>
|
||||||
<language>en-us</language>
|
<language>en-us</language>
|
||||||
<lastBuildDate>Thu, 05 Mar 2026 20:14:36 GMT</lastBuildDate>
|
<lastBuildDate>Thu, 05 Mar 2026 20:17:12 GMT</lastBuildDate>
|
||||||
<atom:link href="https://kbanc.com/feed.xml" rel="self" type="application/rss+xml"/>
|
<atom:link href="https://kbanc.com/feed.xml" rel="self" type="application/rss+xml"/>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
const http = require('http')
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const port = Number(process.env.PORT || 3000)
|
||||||
|
const outDir = path.join(__dirname, 'out')
|
||||||
|
|
||||||
|
const contentTypes = {
|
||||||
|
'.css': 'text/css; charset=utf-8',
|
||||||
|
'.gif': 'image/gif',
|
||||||
|
'.html': 'text/html; charset=utf-8',
|
||||||
|
'.ico': 'image/x-icon',
|
||||||
|
'.jpg': 'image/jpeg',
|
||||||
|
'.jpeg': 'image/jpeg',
|
||||||
|
'.js': 'application/javascript; charset=utf-8',
|
||||||
|
'.json': 'application/json; charset=utf-8',
|
||||||
|
'.md': 'text/markdown; charset=utf-8',
|
||||||
|
'.png': 'image/png',
|
||||||
|
'.svg': 'image/svg+xml',
|
||||||
|
'.txt': 'text/plain; charset=utf-8',
|
||||||
|
'.webp': 'image/webp',
|
||||||
|
'.woff2': 'font/woff2',
|
||||||
|
'.xml': 'application/xml; charset=utf-8',
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeDecode = (value) => {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(value)
|
||||||
|
} catch {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const withinOutDir = (filePath) => {
|
||||||
|
const relativePath = path.relative(outDir, filePath)
|
||||||
|
return relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const findFile = (requestPath) => {
|
||||||
|
const normalizedPath = safeDecode(requestPath).replace(/\/+$/, '') || '/'
|
||||||
|
const relativePath = normalizedPath === '/' ? 'index.html' : normalizedPath.replace(/^\/+/, '')
|
||||||
|
const hasExtension = path.extname(relativePath) !== ''
|
||||||
|
|
||||||
|
const candidates = hasExtension
|
||||||
|
? [path.join(outDir, relativePath)]
|
||||||
|
: [
|
||||||
|
path.join(outDir, `${relativePath}.html`),
|
||||||
|
path.join(outDir, relativePath, 'index.html'),
|
||||||
|
path.join(outDir, relativePath),
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (!withinOutDir(candidate)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
if (!req.url) {
|
||||||
|
res.writeHead(400)
|
||||||
|
res.end('Bad Request')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`)
|
||||||
|
const filePath = findFile(requestUrl.pathname)
|
||||||
|
const resolvedPath = filePath || path.join(outDir, '404.html')
|
||||||
|
const extension = path.extname(resolvedPath).toLowerCase()
|
||||||
|
const contentType = contentTypes[extension] || 'application/octet-stream'
|
||||||
|
|
||||||
|
fs.readFile(resolvedPath, (error, contents) => {
|
||||||
|
if (error) {
|
||||||
|
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' })
|
||||||
|
res.end('Internal Server Error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(filePath ? 200 : 404, {
|
||||||
|
'Content-Type': contentType,
|
||||||
|
'Cache-Control': extension === '.html' ? 'no-cache' : 'public, max-age=31536000, immutable',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (req.method === 'HEAD') {
|
||||||
|
res.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end(contents)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`Static site server running on http://localhost:${port}`)
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue