Last active
August 27, 2021 12:37
-
-
Save a10k/5d2401cd060939741aa64bf140212d98 to your computer and use it in GitHub Desktop.
iridum-docker
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
html`<div style="overflow:hidden;"> | |
<h1 style="height:90px;vertical-align:top;display:inline-flex;padding-top:18px;">localhost</h1> | |
<img src="https://thumbs.dreamstime.com/b/sailing-ship-stormy-sea-ink-black-white-retro-styled-illustration-tall-111997490.jpg" width="110px" style="float:right;"/>`; | |
html`<hr>`; | |
viewof make = { | |
var g = Inputs.text({ | |
label: 'Navigate to…', | |
submit: "Create or Open" | |
}); | |
g.querySelector('input').setAttribute("autocomplete", "off") | |
g.querySelector('button').addEventListener("click",()=>{ | |
var p= g.querySelector('input').value | |
window.location.href = `/${p}` | |
}) | |
return g | |
}; | |
viewof search = { | |
var g = Inputs.text({label: 'Search' }); | |
g.querySelector('input').setAttribute("autocomplete","off") | |
return g | |
}; | |
{ | |
var l = await fetch("./.list").then(d => d.json()) | |
var dom = html`<ul class="iridium_pages"> | |
${l.filter(d=>d.match(/\.ojs$/)) | |
.filter(d=>d!=="_.ojs") | |
.map(d=>{ | |
return html`<li> | |
<a _target="_blank" href="./${d.replace(".ojs","")}">${d.replace(".ojs","")}</a> | |
</li>` | |
})} | |
</ul> | |
<style> | |
ul.iridium_pages { | |
font-size: 13px; | |
columns: ${Math.round((width-128)/220)}; | |
list-style-type: none; | |
padding-left: 128px; | |
} | |
ul.iridium_pages a[href]{ | |
color: #333; | |
} | |
</style> | |
` | |
var instance = new Mark(dom); | |
instance.mark(search); | |
return dom; | |
}; | |
html`<hr>`; | |
viewof login = { | |
var g = Inputs.text({ | |
label: 'Auth', | |
submit: "Login" | |
}); | |
g.querySelector('input').setAttribute("autocomplete", "off") | |
g.querySelector('input').setAttribute("type", "password") | |
g.querySelector('button').addEventListener("click", () => { | |
var inp = g.querySelector('input').value | |
window.document.cookie = `secret=${inp}` | |
window.location.href = window.location.href | |
}) | |
return g | |
}; | |
html`<ul class="iridium_pages"> | |
<li><a onclick=${()=>{ | |
window.document.cookie = `secret=none` | |
window.location.href = window.location.href | |
}}>Logout`; | |
Mark = require("mark.js"); | |
html = htl.html; | |
cms_styles = html`<style> | |
.IridiumViewOnly .observablehq--inspect{ | |
display:none; | |
} | |
.IridiumViewOnly .observablehq{ | |
min-height: 0px; | |
} | |
.IridiumViewOnly .observablehq > style:only-child:before{ | |
display:none; | |
} | |
.IridiumViewOnly .observablehq--changed:before, .IridiumViewOnly .observablehq--running:before{ | |
background-color: transparent; | |
} | |
.IridiumViewOnly .observablehq--md-pre{ | |
display:none; | |
} | |
</style>`; |
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
FROM node:erbium-alpine3.14 | |
RUN apk update | |
RUN apk add git | |
WORKDIR /usr/src/app | |
COPY . . | |
RUN npm install | |
EXPOSE 80 | |
CMD node index.js |
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 simpleGit = require("simple-git"); | |
const parser = require("@observablehq/parser"); | |
const CompilerModule = require("@alex.garcia/unofficial-observablehq-compiler"); | |
const BASE = __dirname; | |
const PASSWORD = process.env.iridium_key || "editor"; | |
//To edit set this on your browser and refresh | |
//document.cookie = "secret=editor" | |
const gen_viewer_markup = (url, data, isEditor) => | |
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:," /> | |
<script src="/iridium.js"></script> | |
</head> | |
<body data-hint-view-only="IridiumViewOnly"> | |
<div id="iridium-root-wrapper"> | |
<div id="iridium-root"></div> | |
</div> | |
<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=>{ | |
if(!d.ok){ | |
throw new Error("Error"); | |
} | |
if(Notification.permission !== "denied"){ | |
Notification.requestPermission(); | |
} | |
new Notification("💚",{ | |
body: "Saved" | |
}) | |
yes(true); | |
}).catch(e=>{ | |
if(Notification.permission !== "denied"){ | |
Notification.requestPermission(); | |
} | |
new Notification("🤬",{ | |
body: "Failed" | |
}) | |
}) | |
}); | |
}; | |
Iridium.render( | |
Iridium.html\`<\${Iridium.IridiumApp} Ir=\${Iridium} />\`, | |
document.getElementById('iridium-root'), | |
); | |
</script> | |
<style> | |
.IridiumTitle:after { | |
content: "${url}"; | |
} | |
</style> | |
</body> | |
</html> | |
` | |
: `<!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:," /> | |
<script src="/iridium.js"></script> | |
</head> | |
<body class="IridiumApp IridiumViewOnly"> | |
<div id="iridium-root-wrapper"> | |
<div id="iridium-root" style="padding: 24px;"></div> | |
</div> | |
<script type="module"> | |
import {Runtime, Inspector} from "/runtime.js"; | |
import define from "/${url || "_"}.js"; | |
new Runtime().module(define, Inspector.into("#iridium-root")); | |
</script> | |
</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}/files/`); | |
}); | |
router.post("/save", async (ctx) => { | |
const cookies = ctx.cookie; | |
const editor = cookies && cookies.secret == PASSWORD; | |
if (editor) { | |
const body = ctx.request.body; | |
const notebook_url = body.path; | |
const notebook_file_path = `${BASE}/files/${notebook_url || "_"}.ojs`; | |
const compiled_file_path = `${BASE}/files/${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); | |
let dir = `${BASE}/files`; | |
let git = simpleGit(dir); | |
try { | |
await git.raw([ 'config', '--global', 'user.email', 'notebook' ]) | |
await git.raw([ 'config', '--global', 'user.name', 'notebook' ]) | |
await git.init(); | |
await git.add("*.ojs"); | |
await git.commit(notebook_url); | |
} catch (e) { | |
/* handle all errors here */ | |
} | |
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 == PASSWORD; | |
const notebook_url = ctx.url.replace(/^\//, ""); | |
const notebook_file_path = `${BASE}/files/${notebook_url || "_"}.ojs`; | |
let notebook_content = "html`<h1> 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) => { | |
try { | |
var parsed_cell = parser.parseCell(d.sourceCode); | |
return `${d.pin ? "/*PIN*/" : ""} | |
${d.sourceCode};`; | |
} catch (e) { | |
return `${d.pin ? "/*PIN*/" : ""} | |
/* Syntax error */`; | |
} | |
}) | |
.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`, { maxage: 0 })); //static server for editor | |
app.use(koaStatic(`${BASE}/files`, { maxage: 0 })); //static server for files | |
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
{ | |
"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", | |
"simple-git": "^2.36.2" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment