feat: initial version
This commit is contained in:
parent
7e418e22a5
commit
3e9e5eda97
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"preset": [
|
||||||
|
"@lumeweb/presetter-kernel-module-preset"
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"official": true,
|
||||||
|
"vite": {
|
||||||
|
"build": {
|
||||||
|
"lib": {
|
||||||
|
"formats": {
|
||||||
|
"0": "umd"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production run-s clean build:typescript:* build:vite build:html",
|
||||||
|
"build:vite": "vite build && shx mv lib/*.cjs lib/bootloader.js",
|
||||||
|
"build:html": "shx cp src/index.html lib/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "@lumeweb/hosted-kernel",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"readme": "ERROR: No README data found!",
|
||||||
|
"_id": "@lumeweb/hosted-kernel@0.1.0",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "gitea@git.lumeweb.com:LumeWeb/libkernel.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "presetter bootstrap",
|
||||||
|
"build": "run build",
|
||||||
|
"semantic-release": "semantic-release"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lumeweb/presetter-kernel-module-preset": "^0.1.0-develop.43"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@lumeweb/libweb": "^0.2.0-develop.26",
|
||||||
|
"binconv": "^0.2.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html class="no-js" lang="">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript" src="./bootloader.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { boot } from "./kernel.js";
|
||||||
|
|
||||||
|
document.title = "Hosted Lume Kernel";
|
||||||
|
let header = document.createElement("h1");
|
||||||
|
header.textContent =
|
||||||
|
"Something went wrong! You should not be visiting this page, this page should only be accessed via an invisible iframe.";
|
||||||
|
document.body.appendChild(header);
|
||||||
|
|
||||||
|
boot();
|
|
@ -0,0 +1,116 @@
|
||||||
|
import {
|
||||||
|
addContextToErr,
|
||||||
|
downloadObject,
|
||||||
|
Err,
|
||||||
|
maybeInitDefaultPortals,
|
||||||
|
setActivePortalMasterKey,
|
||||||
|
} from "@lumeweb/libweb";
|
||||||
|
import { log, logErr, sendAuthUpdate } from "./util.js";
|
||||||
|
import {
|
||||||
|
defaultKernelLink,
|
||||||
|
setKernelLoaded,
|
||||||
|
setLoginComplete,
|
||||||
|
} from "./vars.js";
|
||||||
|
import { getStoredUserKey } from "./storage.js";
|
||||||
|
import { readableStreamToBlob } from "binconv";
|
||||||
|
|
||||||
|
export function boot() {
|
||||||
|
let userKey;
|
||||||
|
|
||||||
|
try {
|
||||||
|
userKey = getStoredUserKey();
|
||||||
|
} catch (e) {
|
||||||
|
logErr(addContextToErr(e, "user key could not be fetched"));
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userKey) {
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log("user is already logged in, attempting to load kernel");
|
||||||
|
setActivePortalMasterKey(userKey);
|
||||||
|
setLoginComplete(true);
|
||||||
|
sendAuthUpdate();
|
||||||
|
loadKernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadKernel() {
|
||||||
|
let [, portalLoadErr] = maybeInitDefaultPortals();
|
||||||
|
if (portalLoadErr) {
|
||||||
|
let err = addContextToErr(portalLoadErr, "unable to init portals");
|
||||||
|
setKernelLoaded(err);
|
||||||
|
logErr(err);
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [kernelCode, err] = await downloadDefaultKernel();
|
||||||
|
|
||||||
|
if (err !== null) {
|
||||||
|
let extErr = addContextToErr(err, "unable to download kernel");
|
||||||
|
setKernelLoaded(extErr);
|
||||||
|
logErr(extErr);
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise(async (resolve, reject) => {
|
||||||
|
const url = URL.createObjectURL(await readableStreamToBlob(kernelCode));
|
||||||
|
|
||||||
|
const el = document.createElement("script");
|
||||||
|
el.src = url;
|
||||||
|
el.onload = () => {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
resolve(null);
|
||||||
|
};
|
||||||
|
el.onerror = (
|
||||||
|
event: Event | string,
|
||||||
|
source?: string,
|
||||||
|
lineno?: number,
|
||||||
|
colno?: number,
|
||||||
|
error?: Error,
|
||||||
|
) => {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.head.appendChild(el);
|
||||||
|
});
|
||||||
|
|
||||||
|
setKernelLoaded("success");
|
||||||
|
sendAuthUpdate();
|
||||||
|
log("kernel successfully loaded");
|
||||||
|
return;
|
||||||
|
} catch (err: any) {
|
||||||
|
let extErr = addContextToErr(err, "unable to eval kernel");
|
||||||
|
setKernelLoaded(extErr);
|
||||||
|
logErr(extErr);
|
||||||
|
logErr(err.toString());
|
||||||
|
console.error(extErr);
|
||||||
|
console.error(err);
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadKernel(
|
||||||
|
kernelCid: string,
|
||||||
|
): Promise<[kernelCode: ReadableStream, err: Err]> {
|
||||||
|
const [code, err] = await downloadObject(kernelCid);
|
||||||
|
|
||||||
|
if (err != null) {
|
||||||
|
return [null as any, err];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [code, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadDefaultKernel(): Promise<
|
||||||
|
[kernelCode: ReadableStream, err: Err]
|
||||||
|
> {
|
||||||
|
return downloadKernel(defaultKernelLink);
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
import {
|
||||||
|
addContextToErr,
|
||||||
|
bytesToHex,
|
||||||
|
hexToBytes,
|
||||||
|
setActivePortalMasterKey,
|
||||||
|
} from "@lumeweb/libweb";
|
||||||
|
import {
|
||||||
|
getLoginComplete,
|
||||||
|
getLogoutComplete,
|
||||||
|
setLoginComplete,
|
||||||
|
setLogoutComplete,
|
||||||
|
} from "./vars.js";
|
||||||
|
import { log, logErr, reloadKernel, sendAuthUpdate } from "./util.js";
|
||||||
|
import { loadKernel } from "./kernel.js";
|
||||||
|
|
||||||
|
function handleStorage(event: StorageEvent) {
|
||||||
|
if (event.key !== null && event.key !== "key") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (getLogoutComplete()) {
|
||||||
|
reloadKernel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === null && getLoginComplete()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "key" && !getLoginComplete()) {
|
||||||
|
let userKey;
|
||||||
|
|
||||||
|
try {
|
||||||
|
userKey = getStoredUserKey();
|
||||||
|
} catch (e) {
|
||||||
|
logErr(addContextToErr(e, "user key could not be fetched"));
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userKey === null) {
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log("user is now logged in, attempting to load kernel");
|
||||||
|
setActivePortalMasterKey(userKey);
|
||||||
|
setLoginComplete(true);
|
||||||
|
loadKernel();
|
||||||
|
sendAuthUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLogoutComplete(true);
|
||||||
|
sendAuthUpdate();
|
||||||
|
log("attempting to do a page reload");
|
||||||
|
reloadKernel();
|
||||||
|
}
|
||||||
|
window.addEventListener("storage", handleStorage);
|
||||||
|
|
||||||
|
export function saveUserKey(key: Uint8Array) {
|
||||||
|
window.localStorage.setItem("key", bytesToHex(key));
|
||||||
|
const event = new StorageEvent("storage", {
|
||||||
|
key: "key",
|
||||||
|
});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStoredUserKey() {
|
||||||
|
const key = window.localStorage.getItem("key");
|
||||||
|
|
||||||
|
if (key) {
|
||||||
|
return hexToBytes(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import {
|
||||||
|
getKernelLoaded,
|
||||||
|
getLoginComplete,
|
||||||
|
getLogoutComplete,
|
||||||
|
} from "./vars.js";
|
||||||
|
import { objAsString } from "@lumeweb/libweb";
|
||||||
|
|
||||||
|
export function sendAuthUpdate() {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
method: "kernelAuthStatus",
|
||||||
|
data: {
|
||||||
|
loginComplete: getLoginComplete(),
|
||||||
|
kernelLoaded: getKernelLoaded(),
|
||||||
|
logoutComplete: getLogoutComplete(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"*",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootloaderWLog(isErr: boolean, ...inputs: any) {
|
||||||
|
// Build the message, each item gets its own line. We do this because items
|
||||||
|
// are often full objects.
|
||||||
|
let message = "[lumeweb-kernel-bootloader]";
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
message += "\n";
|
||||||
|
message += objAsString(inputs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the log by sending it to the parent.
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
method: "log",
|
||||||
|
data: {
|
||||||
|
isErr,
|
||||||
|
message,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"*",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function log(...inputs: any) {
|
||||||
|
bootloaderWLog(false, ...inputs);
|
||||||
|
}
|
||||||
|
export function logErr(...inputs: any) {
|
||||||
|
bootloaderWLog(true, ...inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reloadKernel() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { x25519 } from "@noble/curves/ed25519";
|
||||||
|
|
||||||
|
export const defaultKernelLink =
|
||||||
|
"zduQ9EzFDZLM7z7RKbGex8duA29tHKk5gqriPvRRLcr1CUCvi6BYPoxHN7";
|
||||||
|
|
||||||
|
const store = new Map<string, any>(
|
||||||
|
Object.entries({
|
||||||
|
loginComplete: false,
|
||||||
|
logoutComplete: false,
|
||||||
|
kernelLoaded: "not yet",
|
||||||
|
communicationKey: null,
|
||||||
|
frontendCommunicationPubKey: null,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export function setLoginComplete(status: boolean) {
|
||||||
|
store.set("loginComplete", status);
|
||||||
|
}
|
||||||
|
export function getLoginComplete(): boolean {
|
||||||
|
return store.get("loginComplete");
|
||||||
|
}
|
||||||
|
export function setLogoutComplete(status: boolean) {
|
||||||
|
store.set("logoutComplete", status);
|
||||||
|
}
|
||||||
|
export function getLogoutComplete(): boolean {
|
||||||
|
return store.get("logoutComplete");
|
||||||
|
}
|
||||||
|
export function setKernelLoaded(status: string) {
|
||||||
|
store.set("kernelLoaded", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKernelLoaded(): string {
|
||||||
|
return store.get("kernelLoaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommunicationKey(): Uint8Array {
|
||||||
|
if (!store.get("communicationKey")) {
|
||||||
|
store.set("communicationKey", x25519.utils.randomPrivateKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
return store.get("communicationKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommunicationPubKey() {
|
||||||
|
return x25519.getPublicKey(getCommunicationKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFrontendCommunicationPubkey(): Uint8Array {
|
||||||
|
return store.get("frontendCommunicationPubKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setFrontendCommunicationPubkey(key: Uint8Array) {
|
||||||
|
store.set("frontendCommunicationPubKey", key);
|
||||||
|
}
|
Loading…
Reference in New Issue