|
|
@ -1,24 +1,25 @@ |
|
|
const Koa = require('koa'); |
|
|
|
|
|
const Router = require('@koa/router'); |
|
|
|
|
|
const serve = require('koa-static'); |
|
|
|
|
|
const bodyParser = require('koa-bodyparser'); |
|
|
|
|
|
const fetch = require('isomorphic-unfetch'); |
|
|
|
|
|
const randomBytes = require('crypto').randomBytes; |
|
|
|
|
|
|
|
|
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 app = new Koa() |
|
|
|
|
|
const router = new Router() |
|
|
|
|
|
|
|
|
const gitea = { |
|
|
const gitea = { |
|
|
apiBase: process.env.GITEA_API_BASE, |
|
|
apiBase: process.env.GITEA_API_BASE, |
|
|
user: process.env.GITEA_USER, |
|
|
user: process.env.GITEA_USER, |
|
|
repo: process.env.GITEA_REPO, |
|
|
repo: process.env.GITEA_REPO, |
|
|
token: process.env.GITEA_TOKEN, |
|
|
token: process.env.GITEA_TOKEN, |
|
|
}; |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
async function createNote(name, contentPlain) { |
|
|
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}.txt`; |
|
|
|
|
|
|
|
|
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, { |
|
|
const resp = await fetch(url, { |
|
|
method: 'POST', |
|
|
method: 'POST', |
|
|
headers: { |
|
|
headers: { |
|
|
@ -27,31 +28,7 @@ async function createNote(name, contentPlain) { |
|
|
Authorization: `token ${gitea.token}`, |
|
|
Authorization: `token ${gitea.token}`, |
|
|
}, |
|
|
}, |
|
|
body, |
|
|
body, |
|
|
}); |
|
|
|
|
|
console.log({url, resp}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async function appendNote(content) { |
|
|
|
|
|
const originalResp = await fetch(`${gitea.apiBase}/repos/${gitea.user}/${gitea.repo}/contents/notes.txt`, { |
|
|
|
|
|
headers: { |
|
|
|
|
|
Accept: 'application/json', |
|
|
|
|
|
Authorization: `token ${gitea.token}`, |
|
|
|
|
|
}, |
|
|
|
|
|
}); |
|
|
|
|
|
const originalData = await originalResp.json(); |
|
|
|
|
|
const originalContentPlain = Buffer.from(originalData.content, 'base64').toString('utf8'); |
|
|
|
|
|
const newContentPlain = originalContentPlain + "\n\n" + content; |
|
|
|
|
|
const newContent = Buffer.from(newContentPlain).toString('base64'); |
|
|
|
|
|
const body = JSON.stringify({content: newContent, sha: originalData.sha}); |
|
|
|
|
|
const resp = await fetch(`${gitea.apiBase}/repos/${gitea.user}/${gitea.repo}/contents/notes.txt`, { |
|
|
|
|
|
method: 'PUT', |
|
|
|
|
|
headers: { |
|
|
|
|
|
Accept: 'application/json', |
|
|
|
|
|
'Content-Type': 'application/json', |
|
|
|
|
|
Authorization: `token ${gitea.token}`, |
|
|
|
|
|
}, |
|
|
|
|
|
body, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
router.post('/micropub', async (ctx, next) => { |
|
|
router.post('/micropub', async (ctx, next) => { |
|
|
@ -61,58 +38,85 @@ router.post('/micropub', async (ctx, next) => { |
|
|
Accept: 'application/json', |
|
|
Accept: 'application/json', |
|
|
Authorization: ctx.headers['authorization'] |
|
|
Authorization: ctx.headers['authorization'] |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
const data = await resp.json(); |
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
const data = await resp.json() |
|
|
if (!resp.ok) { |
|
|
if (!resp.ok) { |
|
|
next(); |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
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(md); |
|
|
|
|
|
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 : undefined, |
|
|
|
|
|
js: blocks.html.length === 1 ? blocks.js[0].value : undefined, |
|
|
|
|
|
css: blocks.html.length === 1 ? blocks.css[0].value : undefined, |
|
|
|
|
|
} |
|
|
|
|
|
if (code.html || code.js || code.css) return code |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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>` |
|
|
} |
|
|
} |
|
|
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 = {}; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
router.get('/', async (ctx, next) => { |
|
|
router.get('/', async (ctx, next) => { |
|
|
const host = (ctx.headers['host'] || '').replace(/\.[^.]+\.[^.*]+$/, '') |
|
|
const host = (ctx.headers['host'] || '').replace(/\.[^.]+\.[^.*]+$/, '') |
|
|
if (host === 'notes') { |
|
|
if (host === 'notes') { |
|
|
return await next(); |
|
|
|
|
|
|
|
|
return await next() |
|
|
} |
|
|
} |
|
|
const url = `${gitea.apiBase}/repos/${gitea.user}/${gitea.repo}/contents/${encodeURIComponent(host)}.txt`; |
|
|
|
|
|
|
|
|
const url = `${gitea.apiBase}/repos/${gitea.user}/${gitea.repo}/contents/${encodeURIComponent(host)}.md` |
|
|
const resp = await fetch(url, { |
|
|
const resp = await fetch(url, { |
|
|
headers: { |
|
|
headers: { |
|
|
Accept: 'application/json', |
|
|
Accept: 'application/json', |
|
|
Authorization: `token ${gitea.token}`, |
|
|
Authorization: `token ${gitea.token}`, |
|
|
}, |
|
|
}, |
|
|
}); |
|
|
|
|
|
console.log({url, resp}); |
|
|
|
|
|
|
|
|
}) |
|
|
if (resp.ok) { |
|
|
if (resp.ok) { |
|
|
const { content } = await resp.json(); |
|
|
|
|
|
const contentPlain = Buffer.from(content, 'base64').toString('utf8'); |
|
|
|
|
|
ctx.body = contentPlain; |
|
|
|
|
|
|
|
|
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' 'unsafe-inline'") |
|
|
|
|
|
ctx.set('Content-Type', 'text/html') |
|
|
} else { |
|
|
} else { |
|
|
ctx.status = 404; |
|
|
|
|
|
ctx.body = `${host} not found`; |
|
|
|
|
|
|
|
|
ctx.body = contentPlain |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
ctx.status = 404 |
|
|
|
|
|
ctx.body = `${host} not found` |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
router.get('/api/notes', async (ctx, next) => { |
|
|
|
|
|
const resp = await fetch(`${gitea.apiBase}/repos/${gitea.user}/${gitea.repo}/contents/notes.txt`, { |
|
|
|
|
|
headers: { |
|
|
|
|
|
Accept: 'application/json', |
|
|
|
|
|
Authorization: `token ${gitea.token}`, |
|
|
|
|
|
}, |
|
|
|
|
|
}); |
|
|
|
|
|
const { content } = await resp.json(); |
|
|
|
|
|
const contentPlain = Buffer.from(content, 'base64').toString('utf8'); |
|
|
|
|
|
ctx.body = { content: contentPlain }; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
app |
|
|
app |
|
|
.use(bodyParser()) |
|
|
.use(bodyParser()) |
|
|
.use(router.routes()) |
|
|
.use(router.routes()) |
|
|
.use(router.allowedMethods()) |
|
|
.use(router.allowedMethods()) |
|
|
.use(serve('static')); |
|
|
|
|
|
|
|
|
.use(serve('static')) |
|
|
|
|
|
|
|
|
app.listen(3000); |
|
|
|
|
|
|
|
|
app.listen(3000) |