Skip to content

Instantly share code, notes, and snippets.

@a10k
Last active June 2, 2021 08:11
Show Gist options
  • Save a10k/884ed613a04b21320caeea6636835b73 to your computer and use it in GitHub Desktop.
Save a10k/884ed613a04b21320caeea6636835b73 to your computer and use it in GitHub Desktop.
iridium-plug
router.get("/.list", async (ctx) => {
ctx.body = fs.readdirSync(`${BASE}/.iridium-notebooks/`)
});
router.post("/proxy", async (ctx) => {
ctx.body = await fetch(ctx.request.body.url, ctx.request.body.options)
.then((d) => {
return d.text();
})
.catch((d) => {});
});
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
/.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
{
"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