This commit is contained in:
Rizky
2024-02-14 21:40:12 +07:00
parent 2e6c0de9d5
commit 3b553b8349
10 changed files with 264 additions and 134 deletions
+13 -47
View File
@@ -1,5 +1,6 @@
import { gzipSync } from "bun";
import brotliPromise from "brotli-wasm"; // Import the default export
import { simpleHash } from "utils/cache";
import { g } from "utils/global";
const brotli = await brotliPromise;
const parseQueryParams = (ctx: any) => {
@@ -38,12 +39,6 @@ export const apiContext = (ctx: any) => {
};
};
const cache = {
gz: {} as Record<string, Uint8Array>,
br: {} as Record<string, Uint8Array>,
br_timeout: new Set<string>(),
};
export const createResponse = (
existingRes: any,
body: any,
@@ -57,31 +52,18 @@ export const createResponse = (
if (cache_accept) {
const content_hash = simpleHash(content);
if (cache_accept.toLowerCase().includes("br")) {
if (cache.br[content_hash]) {
content = cache.br[content_hash];
if (g.cache.br[content_hash]) {
content = g.cache.br[content_hash];
headers["content-encoding"] = "br";
} else {
if (!cache.br_timeout.has(content_hash)) {
cache.br_timeout.add(content_hash);
if (!g.cache.br_timeout.has(content_hash)) {
g.cache.br_timeout.add(content_hash);
setTimeout(() => {
cache.br[content_hash] = brotli.compress(Buffer.from(content));
cache.br_timeout.delete(content_hash);
g.cache.br[content_hash] = brotli.compress(Buffer.from(content));
g.cache.br_timeout.delete(content_hash);
});
}
}
headers["content-encoding"] = "br";
}
if (
cache_accept.toLowerCase().includes("gz") &&
!headers["content-encoding"]
) {
if (cache.gz[content_hash]) {
content = cache.gz[content_hash];
} else {
cache.gz[content_hash] = gzipSync(content);
content = cache.gz[content_hash];
}
headers["content-encoding"] = "gzip";
}
}
@@ -94,33 +76,17 @@ export const createResponse = (
: undefined
);
if (typeof body === "object") {
res.headers.append("content-type", "application/json");
}
for (const [k, v] of Object.entries(headers)) {
res.headers.append(k, v);
}
const cur = existingRes as Response;
cur.headers.forEach((value, key) => {
res.headers.append(key, value);
});
if (typeof body === "object" && !res.headers.has("content-type")) {
res.headers.append("content-type", "application/json");
}
return res;
};
export const simpleHash = (str: string, seed = 0) => {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString();
};
+53 -2
View File
@@ -1,15 +1,20 @@
import { file } from "bun";
import { inspectAsync, listAsync } from "fs-jetpack";
import { existsAsync, inspectAsync, listAsync } from "fs-jetpack";
import { join } from "path";
import { createRouter } from "radix3";
import { dir } from "../utils/dir";
import { g } from "../utils/global";
import { parseArgs } from "./parse-args";
import { serveAPI } from "./serve-api";
import { serveWeb } from "./serve-web";
export const createServer = async () => {
g.router = createRouter({ strictTrailingSlash: true });
g.api = {};
g.cache = {
br: {},
br_timeout: new Set(),
};
const scan = async (path: string, root?: string) => {
const apis = await listAsync(path);
if (apis) {
@@ -64,13 +69,59 @@ export const createServer = async () => {
return api;
}
if (g.deploy.gz && g.deploy.index) {
const core = g.deploy.gz.code.core;
const site = g.deploy.gz.code.site;
let pathname = url.pathname;
if (url.pathname[0] === "/") pathname = pathname.substring(1);
if (
!pathname ||
pathname === "index.html" ||
pathname === "index.htm"
) {
return await serveWeb({
content: g.deploy.index.render(),
pathname: "index.html",
});
}
let content = "";
if (core[pathname]) content = core[pathname];
else if (site[pathname]) content = site[pathname];
if (content) {
return await serveWeb({ content, pathname });
}
}
return new Response(`404 Not Found`, {
status: 404,
statusText: "Not Found",
});
};
if (g.deploy.gz?.code.server) {
if (!url.pathname.startsWith("/_deploy")) {
if (
!g.deploy.server &&
(await existsAsync(dir(`app/web/server/index.js`)))
) {
const res = require(dir(`app/web/server/index.js`));
if (res && res.server) {
g.deploy.server = res.server;
}
}
if (g.deploy.server && g.deploy.index) {
return await g.deploy.server.http({
handle,
mode: "prod",
req,
server: g.server,
url: { pathname: url.pathname, raw: url },
index: g.deploy.index,
});
}
}
return handle(req);
-25
View File
@@ -1,25 +0,0 @@
const decoder = new TextDecoder();
function humanFileSize(bytes: any, si = false, dp = 1) {
const thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + " B";
}
const units = si
? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
: ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
let u = -1;
const r = 10 ** dp;
do {
bytes /= thresh;
++u;
} while (
Math.round(Math.abs(bytes) * r) / r >= thresh &&
u < units.length - 1
);
return bytes.toFixed(dp) + " " + units[u];
}
+7 -26
View File
@@ -1,30 +1,11 @@
import { statSync } from "fs";
import { join } from "path";
import { dir } from "utils/dir";
import mime from "mime";
export const serveWeb = async (url: URL, req: Request) => {
return {};
};
export const generateIndexHtml = (base_url: string, site_id: string) => {
const base = base_url.endsWith("/")
? base_url.substring(0, base_url.length - 1)
: base_url;
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="${base}/index.css?fresh">
</head>
<body class="flex-col flex-1 w-full min-h-screen flex opacity-0">
<div style="position:absolute;opacity:0.1;top:0;left:0">&nbsp;</div>
<div id="root"></div>
<script src="${base}/site.js" type="module"></script>
<script>window.id_site = "${site_id}";</script>
</body>
</html>`;
export const serveWeb = async (arg: { pathname: string; content: string }) => {
const type = mime.getType(arg.pathname);
return new Response(arg.content, {
headers: !type ? undefined : { "content-type": type },
});
};