You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

121 lines
3.2 KiB

import Koa from 'koa'
import Router from '@koa/router'
import serve from 'koa-static'
import bodyParser from 'koa-bodyparser'
import fetch from 'isomorphic-unfetch'
import { randomBytes } from 'crypto'
import parse from "mdast-util-from-markdown"
const app = new Koa()
const router = new Router()
const gitea = {
apiBase: process.env.GITEA_API_BASE,
user: process.env.GITEA_USER,
repo: process.env.GITEA_REPO,
token: process.env.GITEA_TOKEN,
}
async function createNote(name, contentPlain) {
const content = Buffer.from(contentPlain).toString('base64')
const body = JSON.stringify({ content })
const url = `${gitea.apiBase}/repos/${gitea.user}/${gitea.repo}/contents/${name}.md`
const resp = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `token ${gitea.token}`,
},
body,
})
}
router.post('/micropub', async (ctx, next) => {
const resp = await fetch('https://tokens.indieauth.com/token', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: ctx.headers['authorization']
}
})
const data = await resp.json()
if (!resp.ok) {
next()
return
}
const name = randomBytes(12).toString('hex')
await createNote(name, ctx.request.body.content)
ctx.set('Location', `https://${name}.benatkin.com/`)
ctx.status = 201
ctx.body = {}
})
function readMarkdown(md) {
const parsed = parse(value);
const codeBlocks = parsed.children.filter((block) => block.type === "code");
const blocks = {
html: codeBlocks.filter((block) => block.lang === "html"),
js: codeBlocks.filter((block) => ["js", "javascript"].includes(block.lang)),
css: codeBlocks.filter((block) => block.lang === "css"),
}
const code = {
html: blocks.html.length === 1 ? blocks.html[0].value,
js: blocks.html.length === 1 ? blocks.js[0].value,
css: blocks.html.length === 1 ? blocks.css[0].value,
}
}
function renderSandbox({html, css, js}) {
return `<html>
<head>
<title>Sandbox</title>
${css ? `<style type="text/css">
${css}
</style>` : ''}
</head>
<body>
${html || ''}
${js ? `<script>
${js}
</script>` : ''}
</body>
</html>`
}
router.get('/', async (ctx, next) => {
const host = (ctx.headers['host'] || '').replace(/\.[^.]+\.[^.*]+$/, '')
if (host === 'notes') {
return await next()
}
const url = `${gitea.apiBase}/repos/${gitea.user}/${gitea.repo}/contents/${encodeURIComponent(host)}.txt`
const resp = await fetch(url, {
headers: {
Accept: 'application/json',
Authorization: `token ${gitea.token}`,
},
})
if (resp.ok) {
const { content } = await resp.json()
const contentPlain = Buffer.from(content, 'base64').toString('utf8')
const markdownContent = readMarkdown(contentPlain)
if (markdownContent) {
ctx.body = renderSandbox(markdownContent)
ctx.set('Content-Security-Policy', "default-src 'self'")
ctx.set('Content-Type', 'text/html')
} else {
ctx.body = contentPlain
}
} else {
ctx.status = 404
ctx.body = `${host} not found`
}
})
app
.use(bodyParser())
.use(router.routes())
.use(router.allowedMethods())
.use(serve('static'))
app.listen(3000)