Last active
June 2, 2021 08:11
-
-
Save a10k/884ed613a04b21320caeea6636835b73 to your computer and use it in GitHub Desktop.
iridium-plug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
router.get("/.list", async (ctx) => { | |
ctx.body = fs.readdirSync(`${BASE}/.iridium-notebooks/`) | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
router.post("/proxy", async (ctx) => { | |
ctx.body = await fetch(ctx.request.body.url, ctx.request.body.options) | |
.then((d) => { | |
return d.text(); | |
}) | |
.catch((d) => {}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Koa = require("koa"); | |
const koaStatic = require("koa-static"); | |
const cors = require("@koa/cors"); | |
const fetch = require("node-fetch"); | |
const cookie = require("koa-cookie"); | |
const app = new Koa(); | |
const Router = require("koa-router"); | |
const bodyParser = require("koa-bodyparser"); | |
const router = new Router(); | |
const fs = require("fs"); | |
const rimraf = require("rimraf"); | |
const parser = require("@observablehq/parser"); | |
const CompilerModule = require("@alex.garcia/unofficial-observablehq-compiler"); | |
const BASE = __dirname; | |
//To edit set this on your browser and refresh | |
//document.cookie = "secret=editor" | |
const gen_viewer_markup = (url, data, isEditor) => `<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<link rel="icon" href="data:," /> | |
<link rel="stylesheet" href="/index.css" /> | |
</head> | |
<body class="${ | |
isEditor ? "" : "IridiumViewOnly" | |
}" data-hint-view-only="IridiumViewOnly"> | |
<div id="iridium-root-wrapper"> | |
<div id="iridium-root"></div> | |
</div> | |
<script src="/index.js"></script> | |
<script> | |
// Override with custom load, save, new, delete, list promises. | |
Iridium.load = () => { | |
return new Promise((yes, no) => { | |
yes(${data}); | |
}); | |
}; | |
Iridium.save = (name,data) => { | |
return new Promise((yes, no) => { | |
fetch("/save", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify({ | |
content: data, | |
path: "${url}" | |
}), | |
}).then(d=>{ | |
yes(true); | |
}) | |
}); | |
}; | |
</script> | |
<style> | |
.IridiumTitle:after { | |
content: "${url}"; | |
} | |
</style> | |
</body> | |
</html> | |
`; | |
app.use(bodyParser()); | |
router.post("/proxy", async (ctx) => { | |
ctx.body = await fetch(ctx.request.body.url, ctx.request.body.options) | |
.then((d) => { | |
return d.text(); | |
}) | |
.catch((d) => {}); | |
}); | |
router.get("/.list", async (ctx) => { | |
ctx.body = fs.readdirSync(`${BASE}/.iridium-notebooks/`); | |
}); | |
router.post("/save", async (ctx) => { | |
const cookies = ctx.cookie; | |
const editor = cookies && cookies.secret == "editor"; | |
if (editor) { | |
const body = ctx.request.body; | |
const notebook_url = body.path; | |
const notebook_file_path = `${BASE}/.iridium-notebooks/${notebook_url}.ojs`; | |
const compiled_file_path = `${BASE}/.iridium-compiled/${notebook_url}.js`; | |
const ojs = cell_array_to_ojs(body.content); | |
const js = source_to_esm(ojs); | |
fs.writeFileSync(notebook_file_path, ojs); | |
fs.writeFileSync(compiled_file_path, js); | |
ctx.body = { | |
status: `{"STATUS":"OK"}`, | |
}; | |
} else { | |
ctx.status = 403; | |
} | |
}); | |
const valid_url_only = (str) => { | |
return ( | |
"/" + | |
str | |
.replace(/^\//, "") | |
.replace(/\/.*$/, "") | |
.replace(/[^a-zA-Z0-9]/g, "-") | |
); | |
}; | |
app.use(async (ctx, next) => { | |
try { | |
//try static routes first | |
await next(); | |
const status = ctx.status || 404; | |
if (status === 404) { | |
ctx.throw(404); | |
} | |
} catch (err) { | |
ctx.url = valid_url_only(ctx.url); | |
const cookies = ctx.cookie; | |
ctx.type = "html"; | |
const editor = cookies && cookies.secret == "editor"; | |
const notebook_url = ctx.url.replace(/^\//, ""); | |
const notebook_file_path = `${BASE}/.iridium-notebooks/${notebook_url}.ojs`; | |
let notebook_content = "md`# 404!`"; | |
if (fs.existsSync(notebook_file_path)) { | |
notebook_content = fs.readFileSync(notebook_file_path, { | |
encoding: "utf8", | |
}); | |
} | |
ctx.body = gen_viewer_markup( | |
notebook_url, | |
JSON.stringify(notebook_content_to_cells(notebook_content), null, 4), | |
editor | |
); | |
} | |
}); | |
const notebook_content_to_cells = (source) => { | |
var parsed = parser.parseModule(source); | |
var ends = parsed.cells.map((d) => d.end); | |
var contents = ends | |
.map((d, i) => { | |
return source.substring(i ? ends[i - 1] : 0, d); | |
}) | |
.map((d, i) => { | |
return { | |
id: i, | |
sourceCode: d | |
.replace(/^\n+/g, "") | |
.replace(/^\/\*PIN\*\//g, "") | |
.replace(/^\n+/g, "") | |
.replace(/;$/, ""), | |
pin: d.startsWith("/*PIN*/") || d.startsWith("\n/*PIN*/"), | |
}; | |
}); | |
return contents; | |
}; | |
const cell_array_to_ojs = (cells) => { | |
return cells | |
.map((d) => { | |
return `${d.pin ? "/*PIN*/" : ""} | |
${d.sourceCode};`; | |
}) | |
.join("\n"); | |
}; | |
const source_to_esm = (source) => { | |
const compiler = new CompilerModule.Compiler({ | |
resolveImportPath: (p) => { | |
return p; | |
}, | |
}); | |
return compiler.module(source); | |
}; | |
app.use(cookie.default()); | |
app.use(cors()); | |
app.use(router.routes()); | |
app.use(koaStatic(`${BASE}/editor`)); //static server for editor | |
app.use(koaStatic(`${BASE}/files`)); //static server for files | |
app.use(koaStatic(`${BASE}/.iridium-compiled`)); //static server for compiled js for imports | |
app.listen(80); //http |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/.iridium-compiled | |
/.iridium-notebooks | |
/editor | |
/icons | |
journal-arrow-up.svg | |
pin-angle-fill.svg | |
pin-angle.svg | |
play-fill.svg | |
play.svg | |
plus-square.svg | |
trash2.svg | |
index.css | |
index.js | |
/files | |
index.js | |
package.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "iridium-plug", | |
"version": "0.0.1", | |
"description": "localhost", | |
"main": "index.js", | |
"scripts": { | |
"start": "node index.js" | |
}, | |
"author": "Alok Pepakayala", | |
"license": "ISC", | |
"dependencies": { | |
"@alex.garcia/unofficial-observablehq-compiler": "^0.6.0-alpha.9", | |
"@koa/cors": "^3.1.0", | |
"@observablehq/parser": "^4.4.1", | |
"koa": "^2.13.1", | |
"koa-bodyparser": "^4.3.0", | |
"koa-cookie": "^1.0.0", | |
"koa-router": "^10.0.0", | |
"koa-static": "^5.0.0", | |
"node-fetch": "^2.6.1", | |
"rimraf": "^3.0.2" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment