Browse Source

build simple micropub/gitea endpoint

main
Benjamin Atkin 5 years ago
parent
commit
77f9b76f13
  1. 4
      .env
  2. 1
      .gitignore
  3. 118
      app.js
  4. 1668
      package-lock.json
  5. 11
      package.json
  6. 8
      static/app.js
  7. 13
      static/index.html
  8. 0
      static/style.css

4
.env

@ -0,0 +1,4 @@
export GITEA_API_BASE=https://gitea.benatkin.com/api/v1
export GITEA_USER=ben
export GITEA_REPO=notes
export GITEA_TOKEN=a1ba576e25dca938172496eebab5c35ed88a2889

1
.gitignore

@ -0,0 +1 @@
node_modules

118
app.js

@ -0,0 +1,118 @@
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;
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}.txt`;
const resp = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `token ${gitea.token}`,
},
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) => {
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 = {};
});
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}`,
},
});
console.log({url, resp});
if (resp.ok) {
const { content } = await resp.json();
const contentPlain = Buffer.from(content, 'base64').toString('utf8');
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
.use(bodyParser())
.use(router.routes())
.use(router.allowedMethods())
.use(serve('static'));
app.listen(3000);

1668
package-lock.json
File diff suppressed because it is too large
View File

11
package.json

@ -4,9 +4,18 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
"license": "MIT",
"dependencies": {
"@koa/router": "^10.0.0",
"isomorphic-unfetch": "^3.1.0",
"koa": "^2.13.1",
"koa-bodyparser": "^4.3.0",
"koa-static": "^5.0.0",
"mdast-util-from-markdown": "^0.8.5"
}
}

8
static/app.js

@ -0,0 +1,8 @@
async function getData() {
const resp = await fetch('/api/notes');
const data = await resp.json();
const el = document.getElementById('notes');
el.innerText = data.content;
}
getData();

13
static/index.html

@ -0,0 +1,13 @@
<!doctype html>
<head>
<title>Notes</title>
<link rel="me" href="https://github.com/benatkin">
<link rel="micropub" href="https://notes.benatkin.com/micropub">
<link rel="token_endpoint" href="https://tokens.indieauth.com/token">
<link rel="authorization_endpoint" href="https://indieauth.com/auth">
<link rel="stylesheet" href="/style.css">
<script type="module" src="/app.js"></script>
<body>
<pre id="notes"></pre>
</body>
</head>

0
static/style.css

Loading…
Cancel
Save