wip check point

This commit is contained in:
Rizky 2023-12-16 17:06:31 +07:00
parent 8cffe6fa69
commit 6ddbd121cb
39 changed files with 443 additions and 498 deletions

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../../pkgs/core/server/api/api-ctx";
import argon from "@node-rs/argon2"; import argon from "@node-rs/argon2";
import { session } from "utils/session"; import { session } from "utils/session";

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../../pkgs/core/server/api/api-ctx";
import { session } from "utils/session"; import { session } from "utils/session";
export const _ = { export const _ = {

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../../pkgs/core/server/api/api-ctx";
import { user } from "dbgen"; import { user } from "dbgen";
import { session } from "utils/session"; import { session } from "utils/session";

View File

@ -1,7 +1,7 @@
import { dirAsync } from "fs-jetpack"; import { dirAsync } from "fs-jetpack";
import trim from "lodash.trim"; import trim from "lodash.trim";
import { dirname } from "path"; import { dirname } from "path";
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { g } from "utils/global"; import { g } from "utils/global";
import { baseTypings } from "../../web/src/utils/script/types/base"; import { baseTypings } from "../../web/src/utils/script/types/base";

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
export const _ = { export const _ = {
url: "/_web/comp/:id", url: "/_web/comp/:id",

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
export const _ = { export const _ = {
url: "/_font/**", url: "/_font/**",

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { g } from "utils/global"; import { g } from "utils/global";
export const _ = { export const _ = {

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
export const _ = { export const _ = {
url: "/local-ip", url: "/local-ip",
async api() { async api() {

View File

@ -1,5 +1,5 @@
import { dir } from "dir"; import { dir } from "dir";
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { g } from "utils/global"; import { g } from "utils/global";
export const _ = { export const _ = {

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { buildNpm } from "../util/build-npm"; import { buildNpm } from "../util/build-npm";
export const _ = { export const _ = {

View File

@ -1,6 +1,6 @@
import { dir } from "dir"; import { dir } from "dir";
import { stat } from "fs/promises"; import { stat } from "fs/promises";
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { g } from "utils/global"; import { g } from "utils/global";
export const _ = { export const _ = {

View File

@ -2,7 +2,7 @@ import crypto from "crypto";
import { dir } from "dir"; import { dir } from "dir";
import { readAsync } from "fs-jetpack"; import { readAsync } from "fs-jetpack";
import mime from "mime-types"; import mime from "mime-types";
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { glb } from "../global"; import { glb } from "../global";
import { g } from "utils/global"; import { g } from "utils/global";

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
export const _ = { export const _ = {
url: "/_web/page/:id", url: "/_web/page/:id",

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { session } from "utils/session"; import { session } from "utils/session";
import { user } from "dbgen"; import { user } from "dbgen";

View File

@ -1,7 +1,7 @@
import { createHash } from "crypto"; import { createHash } from "crypto";
import { dir } from "dir"; import { dir } from "dir";
import { readAsync } from "fs-jetpack"; import { readAsync } from "fs-jetpack";
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
const cache = { const cache = {
md5: "", md5: "",
content: null as any, content: null as any,

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import ts from "typescript"; import ts from "typescript";
import { createHash } from "crypto"; import { createHash } from "crypto";

View File

@ -1,4 +1,4 @@
import { apiContext } from "service-srv"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
import { dir } from "dir"; import { dir } from "dir";
import fs from "fs"; import fs from "fs";

View File

@ -7,6 +7,19 @@ import { Loading } from "../../utils/ui/loading";
export default page({ export default page({
url: "/ed/:site_id/:page_id", url: "/ed/:site_id/:page_id",
component: ({}) => { component: ({}) => {
console.log("momoka");
setTimeout(() => {
(async () => {
console.log(
await fetch("/moka", {
method: "POST",
body: "{}",
})
);
})();
}, 2000);
return <>uwuw</>;
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
const w = window as any; const w = window as any;

View File

@ -2,197 +2,30 @@ import { Root as ReactRoot, createRoot } from "react-dom/client";
import { defineReact, defineWindow } from "web-utils"; import { defineReact, defineWindow } from "web-utils";
import { Root } from "./base/root"; import { Root } from "./base/root";
import "./index.css"; import "./index.css";
import { createAPI, createDB, reloadDBAPI } from "./utils/script/init-api";
import { w } from "./utils/types/general";
import { registerMobile } from "./render/live/logic/mobile"; import { registerMobile } from "./render/live/logic/mobile";
import { isLocalhost } from "./utils/ui/is-localhost"; import { reloadDBAPI } from "./utils/script/init-api";
import { w } from "./utils/types/general";
import { sworkerAddCache, sworkerRegister } from "./sworker-boot";
const start = async () => { const start = async () => {
const base = `${location.protocol}//${location.host}`; const base = `${location.protocol}//${location.host}`;
let react = { let react = {
root: null as null | ReactRoot, root: null as null | ReactRoot,
}; };
(window as any).mobile = registerMobile(); w.mobile = registerMobile();
if (navigator.serviceWorker) {
if (!isLocalhost()) {
const sw = await registerServiceWorker();
const cacheCurrentPage = () => {
const swController = navigator.serviceWorker.controller;
if (swController) {
[location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach(
(url) => {
swController.postMessage({
type: "add-cache",
url: url,
});
}
);
}
};
cacheCurrentPage();
navigator.serviceWorker.addEventListener("message", (e) => {
cacheCurrentPage();
if (react.root) {
if (e.data.type === "offline") {
w.offline = true;
const click = () => {
if (react.root) react.root.render(<Root />);
};
setTimeout(click, 5000);
react.root.render(
<>
<Root />
<div
className={cx(
css`
position: fixed;
bottom: 20px;
left: 0px;
right: 0px;
z-index: 999;
`,
"flex justify-center cursor-pointer"
)}
>
<div
className="bg-orange-500 text-white px-4 py-2 rounded-full text-sm"
onClick={click}
>
Network Failed
</div>
</div>
</>
);
}
if (e.data.type === "activated") {
if (e.data.shouldRefresh && sw) {
react.root.render(
<>
<Root />
<div
className={cx(
css`
position: fixed;
bottom: 20px;
left: 0px;
right: 0px;
z-index: 999;
`,
"flex justify-center"
)}
>
<div className="bg-blue-400 text-white px-4 py-2 rounded-full text-sm">
Updating App...
</div>
</div>
</>
);
sw.unregister().then(() => {
window.location.reload();
});
} else {
const localVersion = localStorage.getItem("prasi-version");
if (localVersion !== e.data.version) {
localStorage.setItem("prasi-version", e.data.version);
const click = () => {
if (react.root) react.root.render(<Root />);
};
setTimeout(click, 5000);
react.root.render(
<>
<Root />
<div
className={cx(
css`
position: fixed;
bottom: 20px;
left: 0px;
right: 0px;
z-index: 999;
`,
"flex justify-center cursor-pointer"
)}
>
<div
className="bg-green-600 text-white px-4 py-2 rounded-full text-sm"
onClick={click}
>
Prasi Updated{" "}
<span className="opacity-50">{e.data.version}</span>
</div>
</div>
</>
);
}
}
}
}
});
} else {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
registration.unregister();
}
});
}
}
// sworkerRegister(react);
defineReact(); defineReact();
await defineWindow(false); await defineWindow(false);
w.serverurl = base; // await reloadDBAPI(base, "prod");
await reloadDBAPI(base, "prod"); // sworkerAddCache(base);
if (navigator.serviceWorker) {
const swc = navigator.serviceWorker.controller;
if (swc) {
[location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach((url) => {
swc.postMessage({
type: "add-cache",
url: url,
});
});
if (w.prasiApi && w.prasiApi[base] && w.prasiApi[base].apiEntry) {
const routes = Object.entries(w.prasiApi[base].apiEntry).map(
([k, v]: any) => ({
url: v.url,
name: k,
})
);
swc.postMessage({
type: "define-route",
routes,
});
}
}
}
w.api = createAPI(base);
w.db = createDB(base);
const el = document.getElementById("root"); const el = document.getElementById("root");
if (el) { if (el) {
react.root = createRoot(el); react.root = createRoot(el);
react.root.render(<Root />); react.root.render(<Root />);
} }
}; };
const registerServiceWorker = async () => {
if ("serviceWorker" in navigator) {
try {
return await navigator.serviceWorker.register(
new URL("./sworker.ts", import.meta.url),
{
type: "module",
scope: "/",
}
);
} catch (error) {
console.error(`Registration failed with ${error}`);
}
}
};
start(); start();

View File

@ -2,6 +2,7 @@ import init from "wasm-gzip";
import { jscript } from "../../../utils/script/jscript"; import { jscript } from "../../../utils/script/jscript";
import { dbClient } from "../../vi/load/db/client-db"; import { dbClient } from "../../vi/load/db/client-db";
import { PG } from "./ed-global"; import { PG } from "./ed-global";
import { fetchViaProxy } from "../../vi/load/proxy";
let w = window as unknown as { db: ReturnType<typeof dbClient> }; let w = window as unknown as { db: ReturnType<typeof dbClient> };

View File

@ -16,7 +16,6 @@ export const EdScriptSnippet: FC<{}> = ({}) => {
font-size: 12px; font-size: 12px;
`)} `)}
onClick={() => { onClick={() => {
console.log(p.script.do_edit);
p.script.do_edit( p.script.do_edit(
`\ `\
<div {...props}> <div {...props}>

View File

@ -0,0 +1,177 @@
import { Root as ReactRoot } from "react-dom/client";
import { Root } from "./base/root";
import { w } from "./utils/types/general";
import { isLocalhost } from "./utils/ui/is-localhost";
export const sworkerRegister = async (react: { root: null | ReactRoot }) => {
if (navigator.serviceWorker) {
if (!isLocalhost()) {
const sw = await registerServiceWorker();
const cacheCurrentPage = () => {
const swController = navigator.serviceWorker.controller;
if (swController) {
[location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach(
(url) => {
swController.postMessage({
type: "add-cache",
url: url,
});
}
);
}
};
cacheCurrentPage();
navigator.serviceWorker.addEventListener("message", (e) => {
cacheCurrentPage();
if (react.root) {
if (e.data.type === "offline") {
w.offline = true;
const click = () => {
if (react.root) react.root.render(<Root />);
};
setTimeout(click, 5000);
react.root.render(
<>
<Root />
<div
className={cx(
css`
position: fixed;
bottom: 20px;
left: 0px;
right: 0px;
z-index: 999;
`,
"flex justify-center cursor-pointer"
)}
>
<div
className="bg-orange-500 text-white px-4 py-2 rounded-full text-sm"
onClick={click}
>
Network Failed
</div>
</div>
</>
);
}
if (e.data.type === "activated") {
if (e.data.shouldRefresh && sw) {
react.root.render(
<>
<Root />
<div
className={cx(
css`
position: fixed;
bottom: 20px;
left: 0px;
right: 0px;
z-index: 999;
`,
"flex justify-center"
)}
>
<div className="bg-blue-400 text-white px-4 py-2 rounded-full text-sm">
Updating App...
</div>
</div>
</>
);
sw.unregister().then(() => {
window.location.reload();
});
} else {
const localVersion = localStorage.getItem("prasi-version");
if (localVersion !== e.data.version) {
localStorage.setItem("prasi-version", e.data.version);
const click = () => {
if (react.root) react.root.render(<Root />);
};
setTimeout(click, 5000);
react.root.render(
<>
<Root />
<div
className={cx(
css`
position: fixed;
bottom: 20px;
left: 0px;
right: 0px;
z-index: 999;
`,
"flex justify-center cursor-pointer"
)}
>
<div
className="bg-green-600 text-white px-4 py-2 rounded-full text-sm"
onClick={click}
>
Prasi Updated{" "}
<span className="opacity-50">{e.data.version}</span>
</div>
</div>
</>
);
}
}
}
}
});
} else {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
registration.unregister();
}
});
}
}
};
const registerServiceWorker = async () => {
if ("serviceWorker" in navigator) {
try {
return await navigator.serviceWorker.register(
new URL("./sworker.ts", import.meta.url),
{
type: "module",
scope: "/",
}
);
} catch (error) {
console.error(`Registration failed with ${error}`);
}
}
};
export const sworkerAddCache = (base: string) => {
if (navigator.serviceWorker) {
if (!isLocalhost()) {
const swc = navigator.serviceWorker.controller;
if (swc) {
[location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach((url) => {
swc.postMessage({
type: "add-cache",
url: url,
});
});
if (w.prasiApi && w.prasiApi[base] && w.prasiApi[base].apiEntry) {
const routes = Object.entries(w.prasiApi[base].apiEntry).map(
([k, v]: any) => ({
url: v.url,
name: k,
})
);
swc.postMessage({
type: "define-route",
routes,
});
}
}
}
}
};

View File

@ -27,6 +27,7 @@ export const w = window as unknown as {
prasiApi: Record<string, PrasiAPI>; prasiApi: Record<string, PrasiAPI>;
loadedFonts: string[]; loadedFonts: string[];
prasiApiDbPull: boolean; prasiApiDbPull: boolean;
mobile?: any;
params: any; params: any;
editorGlbDefault: string; editorGlbDefault: string;
ts: number; ts: number;

View File

@ -1,4 +1,4 @@
import { apiContext } from "../server/api-ctx"; import { apiContext } from "../server/api/api-ctx";
import { g } from "../utils/global"; import { g } from "../utils/global";
export const _ = { export const _ = {

View File

@ -1,4 +1,4 @@
import { apiContext } from "../server/api-ctx"; import { apiContext } from "../server/api/api-ctx";
import { DBArg, execQuery } from "../utils/query"; import { DBArg, execQuery } from "../utils/query";
export const _ = { export const _ = {

View File

@ -1,5 +1,5 @@
import { g } from "utils/global"; import { g } from "utils/global";
import { apiContext } from "../server/api-ctx"; import { apiContext } from "../server/api/api-ctx";
import { dir } from "../utils/dir"; import { dir } from "../utils/dir";
export const _ = { export const _ = {

View File

@ -1,5 +1,5 @@
import { readAsync } from "fs-jetpack"; import { readAsync } from "fs-jetpack";
import { apiContext } from "../server/api-ctx"; import { apiContext } from "../server/api/api-ctx";
import { g } from "../utils/global"; import { g } from "../utils/global";
import { dir } from "../utils/dir"; import { dir } from "../utils/dir";

View File

@ -2,7 +2,7 @@ import mp from "@surfy/multipart-parser";
import { writeAsync } from "fs-jetpack"; import { writeAsync } from "fs-jetpack";
import { g } from "utils/global"; import { g } from "utils/global";
import { apiContext } from "../server/api-ctx"; import { apiContext } from "../server/api/api-ctx";
import { dir } from "../utils/dir"; import { dir } from "../utils/dir";
export const _ = { export const _ = {
url: "/_upload", url: "/_upload",

View File

@ -1,7 +1,5 @@
import { parcelBuild } from "utils/parcel"; import { parcelBuild } from "utils/parcel";
import { generateAPIFrm } from "./server/api-frm"; import { prepareAPITypes } from "./server/api/prep-api-ts";
import { prepareApiRoutes } from "./server/api-scan";
import { prepareAPITypes } from "./server/prep-api-ts";
import { startDevWatcher } from "./utils/dev-watcher"; import { startDevWatcher } from "./utils/dev-watcher";
import { ensureNotRunning } from "./utils/ensure"; import { ensureNotRunning } from "./utils/ensure";
import { g } from "./utils/global"; import { g } from "./utils/global";
@ -55,14 +53,11 @@ if (!g.apiPrepared) {
await syncActionDefinition(); await syncActionDefinition();
g.log.info("WS Action defined"); g.log.info("WS Action defined");
await generateAPIFrm();
await prepareAPITypes(); await prepareAPITypes();
g.log.info("API Prepared"); g.log.info("API Prepared");
g.apiPrepared = true; g.apiPrepared = true;
} }
await prepareApiRoutes();
if (!g.parcel) { if (!g.parcel) {
await parcelBuild(); await parcelBuild();
} }

View File

@ -1,70 +0,0 @@
import { transform } from "@swc/core";
import { g } from "../utils/global";
import { createHash } from "crypto";
export const generateAPIFrm = async () => {
const res = await transform(
`
(BigInt.prototype).toJSON = function () {
return "BigInt::" + this.toString();
};
const replacer = (key, value) => {
if (typeof value === "string" && value.startsWith('BigInt::')) {
return BigInt(value.substr(8));
}
return value;
}
window.addEventListener('message', (e) => {
const msg = e.data;
const init = Object.assign({}, msg.init)
let input = msg.input;
let url = msg.input;
if (typeof msg.input === 'string') {
if (!input.startsWith('http')) {
url = new URL(\`\$\{location.origin\}\$\{input\}\`)
} else {
url = new URL(input)
}
}
if (init && init.body && typeof init.body === 'object') {
if (Array.isArray(init.body)) {
const body = new FormData();
body.append("file", init.body[0]);
init.body = body;
}
}
fetch(url.pathname, init)
.then(async (res) => {
if (res) {
const body = await res.text();
if (res.ok) {
try {
parent.postMessage({result: JSON.parse(body, replacer), id: msg.id }, '*')
} catch(e) {
parent.postMessage({result: body, id: msg.id }, '*')
}
} else {
try {
parent.postMessage({error: JSON.parse(body, replacer), id: msg.id }, '*')
} catch(e) {
parent.postMessage({error: body, id: msg.id }, '*')
}
}
}
})
})
parent.postMessage('initialized', '*')`,
{ minify: true }
);
g.frm = {
js: res.code,
etag: createHash("md5").update(res.code).digest("hex"),
};
};

View File

@ -1,8 +1,8 @@
import { file } from "bun"; import { file } from "bun";
import { inspectAsync, listAsync } from "fs-jetpack"; import { inspectAsync, listAsync } from "fs-jetpack";
import { join } from "path"; import { join } from "path";
import { dir } from "../utils/dir"; import { dir } from "../../utils/dir";
import { g } from "../utils/global"; import { g } from "../../utils/global";
import { parseArgs } from "./parse-args"; import { parseArgs } from "./parse-args";
export const prepareApiRoutes = async () => { export const prepareApiRoutes = async () => {

View File

@ -1,7 +1,7 @@
import { spawn, spawnSync } from "bun"; import { spawn } from "bun";
import { existsAsync, readAsync } from "fs-jetpack"; import { existsAsync, readAsync } from "fs-jetpack";
import { dir } from "../utils/dir"; import { dir } from "../../utils/dir";
import { g } from "../utils/global"; import { g } from "../../utils/global";
export const prepareAPITypes = async () => { export const prepareAPITypes = async () => {
const out: string[] = []; const out: string[] = [];

View File

@ -1,8 +1,7 @@
import { WebSocketHandler } from "bun";
import { lookup } from "mime-types";
import { createRouter } from "radix3"; import { createRouter } from "radix3";
import { dir } from "../utils/dir";
import { g } from "../utils/global"; import { g } from "../utils/global";
import { serveWS } from "./serve-ws";
import { serveStatic } from "./serve-static";
import { serveAPI } from "./serve-api"; import { serveAPI } from "./serve-api";
export const cache = { export const cache = {
@ -19,127 +18,24 @@ export const cache = {
export type WSData = { url: URL }; export type WSData = { url: URL };
export const createServer = async () => { export const createServer = async () => {
g.router = createRouter({ strictTrailingSlash: false }); await serveAPI.init();
await serveStatic.init();
for (const route of Object.values(g.api)) {
g.router.insert(route.url.replace(/\*/gi, "**"), route);
}
const { wsHandler } = await import("../../../app/srv/ws/handler");
g.server = Bun.serve({ g.server = Bun.serve({
port: g.port, port: g.port,
websocket: { maxRequestBodySize: 9999999,
maxPayloadLength: 9999999, development: true,
closeOnBackpressureLimit: true, websocket: await serveWS(),
drain(ws) {
// console.log("Backpressure relieved...");
},
close(ws, code, reason) {
const pathname = ws.data.url.pathname;
if (wsHandler[pathname]) {
const close = wsHandler[pathname].close;
if (close) {
close(ws, code, reason);
}
}
},
message(ws, message) {
const pathname = ws.data.url.pathname;
if (wsHandler[pathname]) {
const msg = wsHandler[pathname].message;
if (msg) {
msg(ws, message);
}
}
},
open(ws) {
const pathname = ws.data.url.pathname;
if (wsHandler[pathname]) {
const open = wsHandler[pathname].open;
if (open) {
open(ws);
}
}
},
} as WebSocketHandler<WSData>,
async fetch(req, server) { async fetch(req, server) {
const url = new URL(req.url); const url = new URL(req.url);
const response = async () => { if (serveStatic.exists(url)) {
if (wsHandler[url.pathname]) { return serveStatic.serve(url);
if (
server.upgrade(req, {
data: {
url: new URL(req.url),
},
})
) {
return;
}
return new Response("Upgrade failed :(", { status: 500 });
} }
try { await serveAPI.serve(url, req);
const api = await serveAPI(url, req);
if (api) {
return api;
}
} catch (e) {
g.log.error(e);
}
const webPath = "app/static"; return serveStatic.serve(url);
try {
const found = cache.static[url.pathname];
if (found && g.mode === "prod") {
return responseCached(req, found);
}
const file = Bun.file(dir.path(`${webPath}${url.pathname}`));
if (
(await file.exists()) &&
file.type !== "application/octet-stream" // is not directory
) {
if (g.mode === "dev") {
return new Response(file);
}
if (!cache.static[url.pathname]) {
cache.static[url.pathname] = {
type: lookup(url.pathname) || "text/plain",
content: await file.arrayBuffer(),
};
}
const filebr = Bun.file(dir.path(`${webPath}-br${url.pathname}`));
if (
(await filebr.exists()) &&
filebr.type !== "application/octet-stream" // is not directory
) {
cache.static[url.pathname].br = await filebr.arrayBuffer();
}
const found = cache.static[url.pathname];
if (found) {
return responseCached(req, found);
}
}
} catch (e) {
g.log.error(e);
}
try {
return new Response(
Bun.file(dir.path(`${webPath}/index.html`)) as any
);
} catch (e) {
g.log.error(e);
return new Response("Loading...");
}
};
const res = await response();
return res;
}, },
}); });
@ -149,16 +45,3 @@ export const createServer = async () => {
g.log.info(`Started at port: ${g.server.port}`); g.log.info(`Started at port: ${g.server.port}`);
} }
}; };
const responseCached = (req: Request, found: (typeof cache.static)[string]) => {
if (req.headers.get("accept-encoding")?.includes("br") && found.br) {
const res = new Response(found.br);
res.headers.set("content-type", found.type);
res.headers.set("content-encoding", "br");
return res;
}
const res = new Response(found.content);
res.headers.set("content-type", found.type);
return res;
};

View File

@ -1,14 +1,17 @@
import { createResponse } from "./api-ctx"; import { createRouter } from "radix3";
import { g } from "../utils/global"; import { g } from "utils/global";
import { createResponse } from "./api/api-ctx";
import { prepareApiRoutes } from "./api/api-scan";
const replacer = (key: string, value: string) => { export const serveAPI = {
if (typeof value === "string" && value.startsWith("BigInt::")) { init: async () => {
return BigInt(value.substring(8)); g.router = createRouter({ strictTrailingSlash: false });
for (const route of Object.values(g.api)) {
g.router.insert(route.url.replace(/\*/gi, "**"), route);
} }
return value; await prepareApiRoutes();
}; },
serve: async (url: URL, req: Request) => {
export const serveAPI = async (url: URL, req: Request) => {
let found = g.router.lookup(url.pathname); let found = g.router.lookup(url.pathname);
if (!found?.url) { if (!found?.url) {
if (!url.pathname.endsWith("/")) { if (!url.pathname.endsWith("/")) {
@ -28,7 +31,9 @@ export const serveAPI = async (url: URL, req: Request) => {
}); });
if (req.method !== "GET") { if (req.method !== "GET") {
if (!req.headers.get("content-type")?.startsWith("multipart/form-data")) { if (
!req.headers.get("content-type")?.startsWith("multipart/form-data")
) {
try { try {
const text = await req.text(); const text = await req.text();
const json = JSON.parse(text, replacer); const json = JSON.parse(text, replacer);
@ -55,7 +60,7 @@ export const serveAPI = async (url: URL, req: Request) => {
} }
} }
} catch (e) { } catch (e) {
g.log.error({ pathname: url.pathname, error: e }); throw e;
} }
} }
} }
@ -93,4 +98,12 @@ export const serveAPI = async (url: URL, req: Request) => {
return current.res; return current.res;
} }
},
};
const replacer = (key: string, value: string) => {
if (typeof value === "string" && value.startsWith("BigInt::")) {
return BigInt(value.substring(8));
}
return value;
}; };

View File

@ -0,0 +1,59 @@
import { dir } from "dir";
import { inspectTreeAsync } from "fs-jetpack";
import { InspectTreeResult } from "fs-jetpack/types";
import { join } from "path";
import mime from "mime";
const webPath = "app/static";
const cache = {
static: {} as Record<string, { type: string; content: any }>,
};
export const serveStatic = {
init: async () => {
const list = await inspectTreeAsync(dir.path(`${webPath}`));
const walk = async (
list: InspectTreeResult,
parent?: InspectTreeResult[]
) => {
for (const item of list.children) {
if (item.type === "file") {
const path = join(
...(parent || [{ name: "static" }]).map((e) => e.name),
item.name
);
const file = await Bun.file(dir.path(`app/${path}`));
if (await file.exists()) {
cache.static[path.substring("static".length)] = {
type: mime.getType(path) || "application/octet-stream",
content: await file.arrayBuffer(),
};
}
} else {
await walk(item, parent ? [...parent, list] : [list]);
}
}
};
if (list) {
await walk(list);
}
},
exists: (url: URL) => {
return !!cache.static[url.pathname];
},
serve: (url: URL) => {
const file = cache.static[url.pathname];
if (file) {
return new Response(file.content, {
headers: { "content-type": file.type },
});
}
const index = cache.static["/index.html"];
if (index) {
return new Response(index.content, {
headers: { "content-type": index.type },
});
}
},
};

View File

@ -0,0 +1,41 @@
import { WebSocketHandler } from "bun";
import { WSData } from "./create";
export const serveWS: () => Promise<WebSocketHandler<WSData>> = async () => {
const { wsHandler } = await import("../../../app/srv/ws/handler");
return {
maxPayloadLength: 9999999,
closeOnBackpressureLimit: true,
drain(ws) {
// console.log("Backpressure relieved...");
},
close(ws, code, reason) {
const pathname = ws.data.url.pathname;
if (wsHandler[pathname]) {
const close = wsHandler[pathname].close;
if (close) {
close(ws, code, reason);
}
}
},
message(ws, message) {
const pathname = ws.data.url.pathname;
if (wsHandler[pathname]) {
const msg = wsHandler[pathname].message;
if (msg) {
msg(ws, message);
}
}
},
open(ws) {
const pathname = ws.data.url.pathname;
if (wsHandler[pathname]) {
const open = wsHandler[pathname].open;
if (open) {
open(ws);
}
}
},
} as WebSocketHandler<WSData>;
};

View File

@ -31,7 +31,7 @@
"./node_modules/.prisma/client/index.d.ts" "./node_modules/.prisma/client/index.d.ts"
], ],
"service-srv": [ "service-srv": [
"./pkgs/core/server/api-ctx.ts" "./pkgs/core/server/api/api-ctx.ts"
], ],
"utils/*": [ "utils/*": [
"./pkgs/core/utils/*" "./pkgs/core/utils/*"