This commit is contained in:
Rizky 2024-02-10 15:58:04 +07:00
parent 18d389b73e
commit aa6cdbf539
9 changed files with 127 additions and 251 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -26,26 +26,10 @@ export const _ = {
) {
const { res } = apiContext(this);
if (!g.web[action.id_site]) {
g.web[action.id_site] = {
current: 0,
domains: [],
deploying: null,
router: null,
deploys: [],
site_id: action.id_site,
cacheKey: 0,
cache: null,
};
}
const path = dir(`app/web/${action.id_site}`);
const path = dir(`app/web/`);
await dirAsync(path);
const web = g.web[action.id_site];
if (!web.domains) {
web.domains = [];
}
const web = g.web;
switch (action.type) {
case "check":
@ -53,7 +37,6 @@ export const _ = {
now: Date.now(),
current: web.current,
deploys: web.deploys,
domains: web.domains,
db: {
url: g.dburl || "-",
},
@ -100,23 +83,6 @@ datasource db {
}, 300);
}
break;
case "domain-add":
{
web.domains.push(action.domain);
await Bun.write(`${path}/domains.json`, JSON.stringify(web.domains));
g.domains = null;
res.send("ok");
}
break;
case "domain-del":
{
web.domains = web.domains.filter((e) => e !== action.domain);
await Bun.write(`${path}/domains.json`, web.domains);
g.domains = null;
res.send("ok");
}
break;
case "deploy-del":
{
web.deploys = web.deploys.filter((e) => e !== parseInt(action.ts));

View File

@ -3,9 +3,10 @@ import { apiContext } from "service-srv";
import { g } from "utils/global";
import { dir } from "utils/dir";
const cache = {
dev: "",
prod: "",
const generated = {
"load.json": "",
"load.js.dev": "",
"load.js.prod": "",
};
export const _ = {
@ -21,15 +22,8 @@ export const _ = {
"load.json": async () => {
res.setHeader("content-type", "application/json");
res.send(
JSON.stringify({
apiEntry: getApiEntry(),
apiTypes: (await getApiTypes()) || "",
prismaTypes: {
"prisma.d.ts": await getPrisma("prisma"),
"runtime/index.d.ts": await getPrisma("runtime"),
"runtime/library.d.ts": await getPrisma("library"),
},
})
await getContent("load.json"),
req.headers.get("accept-encoding") || ""
);
},
"load.js": async () => {
@ -39,46 +33,16 @@ export const _ = {
? JSON.stringify(req.query_parameters["url"])
: "undefined";
if (!cache.dev) {
cache.dev = `\
(() => {
const baseurl = new URL(location.href);
baseurl.pathname = '';
const url = ${url} || baseurl.toString();
const w = window;
if (!w.prasiApi) {
w.prasiApi = {};
}
w.prasiApi[url] = {
apiEntry: ${JSON.stringify(getApiEntry())},
apiTypes: ${JSON.stringify((await getApiTypes()) || "")},
prismaTypes: {
"prisma.d.ts": ${await getPrisma("prisma")},
"runtime/index.d.ts": ${await getPrisma("runtime")},
"runtime/library.d.ts": ${await getPrisma("library")},
},
};
})();`;
cache.prod = `\
(() => {
const baseurl = new URL(location.href);
baseurl.pathname = '';
const url = ${url} || baseurl.toString();
const w = window;
if (!w.prasiApi) {
w.prasiApi = {};
}
w.prasiApi[url] = {
apiEntry: ${JSON.stringify(getApiEntry())},
};
})();`;
}
if (req.query_parameters["dev"]) {
res.send(cache.dev);
res.send(
await getContent("load.js.dev", url),
req.headers.get("accept-encoding") || ""
);
} else {
res.send(cache.prod);
res.send(
await getContent("load.js.prod", url),
req.headers.get("accept-encoding") || ""
);
}
},
};
@ -102,6 +66,58 @@ export const getApiEntry = () => {
return res;
};
const getContent = async (type: keyof typeof generated, url?: string) => {
if (type === "load.json") {
if (!generated[type])
generated[type] = JSON.stringify({
apiEntry: getApiEntry(),
apiTypes: (await getApiTypes()) || "",
prismaTypes: {
"prisma.d.ts": await getPrisma("prisma"),
"runtime/index.d.ts": await getPrisma("runtime"),
"runtime/library.d.ts": await getPrisma("library"),
},
});
} else if (type === "load.js.dev") {
if (!generated[type])
generated[type] = `\
(() => {
const baseurl = new URL(location.href);
baseurl.pathname = '';
const url = ${url} || baseurl.toString();
const w = window;
if (!w.prasiApi) {
w.prasiApi = {};
}
w.prasiApi[url] = {
apiEntry: ${JSON.stringify(getApiEntry())},
apiTypes: ${JSON.stringify((await getApiTypes()) || "")},
prismaTypes: {
"prisma.d.ts": ${await getPrisma("prisma")},
"runtime/index.d.ts": ${await getPrisma("runtime")},
"runtime/library.d.ts": ${await getPrisma("library")},
},
};
})();`;
} else if (type === "load.js.prod") {
if (!generated[type])
generated[type] = `\
(() => {
const baseurl = new URL(location.href);
baseurl.pathname = '';
const url = ${url} || baseurl.toString();
const w = window;
if (!w.prasiApi) {
w.prasiApi = {};
}
w.prasiApi[url] = {
apiEntry: ${JSON.stringify(getApiEntry())},
});
})();`;
}
return generated[type];
};
const getApiTypes = async () => {
return (
`\

View File

@ -1,4 +1,4 @@
import { g } from "../utils/global";
import { gzipSync } from "bun";
const parseQueryParams = (ctx: any) => {
const pageHref = ctx.req.url;
@ -19,8 +19,8 @@ export const apiContext = (ctx: any) => {
req: ctx.req as Request & { params: any; query_parameters: any },
res: {
...ctx.res,
send: (body) => {
ctx.res = createResponse(ctx.res, body);
send: (body, cache_accept?: string) => {
ctx.res = createResponse(ctx.res, body, cache_accept);
},
sendStatus: (code: number) => {
ctx.res._status = code;
@ -29,19 +29,38 @@ export const apiContext = (ctx: any) => {
ctx.res.headers.append(key, value);
},
} as Response & {
send: (body?: string | object) => void;
send: (body?: string | object, cache_accept?: string) => void;
setHeader: (key: string, value: string) => void;
sendStatus: (code: number) => void;
},
};
};
export const createResponse = (existingRes: any, body: any) => {
const cache = { gz: {} as Record<string, Uint8Array> };
export const createResponse = (
existingRes: any,
body: any,
cache_accept?: string
) => {
const status =
typeof existingRes._status === "number" ? existingRes._status : undefined;
let content: any = typeof body === "string" ? body : JSON.stringify(body);
const headers = {} as Record<string, string>;
if (cache_accept && cache_accept.toLowerCase().includes("gz")) {
const content_hash = simpleHash(content);
if (content_hash) {
content = cache.gz[content_hash];
} else {
cache.gz[content_hash] = gzipSync(content);
content = cache.gz[content_hash];
}
headers["content-encoding"] = "gz";
}
let res = new Response(
typeof body === "string" ? body : JSON.stringify(body),
content,
status
? {
status,
@ -52,11 +71,22 @@ export const createResponse = (existingRes: any, body: any) => {
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;
for (const [key, value] of cur.headers.entries()) {
cur.headers.forEach((value, key) => {
res.headers.append(key, value);
}
});
return res;
};
function simpleHash(str: string) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0;
}
return (hash >>> 0).toString(36);
}

View File

@ -60,56 +60,19 @@ export const createServer = async () => {
async fetch(req) {
const url = new URL(req.url);
const web = await serveWeb(url, req);
let index = ["", ""];
if (web) {
if (Array.isArray(web)) index = web;
else {
return web;
}
}
const api = await serveAPI(url, req);
if (api) {
return api;
}
if (index) {
let status: any = {};
if (!["", "index.html"].includes(trim(url.pathname, " /"))) {
status = {
status: 404,
statusText: "Not Found",
};
const handle = async (req: Request) => {
const api = await serveAPI(url, req);
if (api) {
return api;
}
const [site_id, body] = index;
if (g.web[site_id]) {
const router = g.web[site_id].router;
if (router) {
let found = router.lookup(url.pathname);
if (!found) {
found = router.lookup(url.pathname + "/");
}
if (found) {
status = {};
}
}
}
return new Response(body, {
...status,
headers: {
"content-type": "text/html",
},
return new Response(`404 Not Found`, {
status: 404,
statusText: "Not Found",
});
}
};
return new Response(`404 Not Found`, {
status: 404,
statusText: "Not Found",
});
return handle(req);
},
});

View File

@ -8,15 +8,12 @@ import {
removeAsync,
writeAsync,
} from "fs-jetpack";
import { createRouter } from "radix3";
import { gunzipSync } from "zlib";
import { downloadFile } from "../api/_deploy";
import { dir } from "../utils/dir";
import { g } from "../utils/global";
export const loadWeb = async () => {
g.web = {};
await dirAsync(dir(`app/static`));
const siteZip = `${
g.mode === "dev" ? "http://localhost:4550" : "https://prasi.app"
@ -53,39 +50,14 @@ export const loadWeb = async () => {
if (!deploy) {
await dirAsync(dir(`app/web/${web.name}/deploys`));
}
g.web[web.name] = {
current: parseInt(
(await readAsync(dir(`app/web/${web.name}/current`))) || "0"
),
deploys: deploy ? deploy.children.map((e) => parseInt(e.name)) : [],
domains:
(await readAsync(dir(`app/web/${web.name}/domains.json`), "json")) ||
[],
site_id: web.name,
deploying: null,
cacheKey: 0,
router: null,
cache: null,
};
const cur = g.web[web.name];
if (!cur.deploys.includes(cur.current)) {
cur.current = 0;
}
if (cur.current) {
await loadWebCache(cur.site_id, cur.current);
}
}
};
const decoder = new TextDecoder();
export const loadWebCache = async (site_id: string, ts: number | string) => {
const web = g.web[site_id];
const web = g.web;
if (web) {
const path = dir(`app/web/${site_id}/deploys/${ts}`);
const path = dir(`app/web/deploys/${ts}`);
if (await existsAsync(path)) {
const fileContent = await readAsync(path, "buffer");
if (fileContent) {
@ -94,11 +66,6 @@ export const loadWebCache = async (site_id: string, ts: number | string) => {
);
const res = gunzipSync(fileContent);
web.cache = JSON.parse(decoder.decode(res));
web.router = createRouter();
for (const p of web.cache?.pages || []) {
web.router.insert(p.url, p);
}
}
}
}

View File

@ -1,6 +1,5 @@
import { statSync } from "fs";
import { join } from "path";
import { g } from "../utils/global";
import { dir } from "utils/dir";
const index = {
@ -13,28 +12,13 @@ const index = {
};
export const serveWeb = async (url: URL, req: Request) => {
const domain = url.hostname;
let site_id = "";
if (!g.domains) {
g.domains = {};
for (const web of Object.values(g.web)) {
for (const d of web.domains) {
const durl = new URL(d);
g.domains[durl.hostname] = web.site_id;
}
}
}
if (typeof g.domains[domain] === "undefined") {
g.domains[domain] = "";
}
site_id = g.domains[domain];
if (!site_id) {
return false;
}
const base = dir(`app/static/site`);
// const base = `/Users/r/Developer/prasi/.output/app/srv/site`;
let path = join(base, url.pathname);
@ -73,7 +57,7 @@ export const serveWeb = async (url: URL, req: Request) => {
index.html = generateIndexHtml("", site_id);
}
return [site_id, index.html];
return { site_id, index: index.html };
};
export const generateIndexHtml = (base_url: string, site_id: string) => {
@ -95,10 +79,6 @@ export const generateIndexHtml = (base_url: string, site_id: string) => {
<div id="root"></div>
<script src="${base}/site.js" type="module"></script>
<script>window.id_site = "${site_id}";</script>
<script
src="https://js.sentry-cdn.com/a2dfb8b1128f4018b83bdc9c08d18da2.min.js"
crossorigin="anonymous"
></script>
</body>
</html>`;
};

View File

@ -20,65 +20,19 @@ export const g = global as unknown as {
mode: "dev" | "prod";
server: Server;
log: Logger;
firebaseInit: boolean,
firebaseInit: boolean;
firebase: admin.app.App;
notif: {
db: Database;
};
api: Record<string, SingleRoute>;
domains: null | Record<string, string>;
web: Record<
string,
{
site_id: string;
current: number;
deploying: null | { status: string; received: number; total: number };
deploys: number[];
domains: string[];
router: null | RadixRouter<{ id: string }>;
cacheKey: number;
cache: null | {
site: {
id: string;
name: string;
favicon: string;
domain: string;
id_user: string;
created_at: Date | null;
id_org: string | null;
updated_at: Date | null;
responsive: string;
} | null;
pages: {
id: string;
name: string;
url: string;
content_tree: any;
id_site: string;
created_at: Date | null;
js_compiled: string | null;
js: string | null;
updated_at: Date | null;
id_folder: string | null;
is_deleted: boolean;
}[];
npm: {
site: Record<string, string>;
pages: Record<string, Record<string, string>>;
};
comps: {
id: string;
name: string;
content_tree: any;
created_at: Date | null;
updated_at: Date | null;
type: string;
id_component_group: string | null;
props: any;
}[];
};
}
>;
web: {
site_id: string;
current: number;
deploying: null | { status: string; received: number; total: number };
deploys: number[];
router: RadixRouter<SingleRoute>;
};
router: RadixRouter<SingleRoute>;
port: number;
frm: {

View File

@ -1,6 +1,6 @@
import { gzip, gunzip } from "zlib";
export const gzipAsync = (bin: Uint8Array) => {
export const gzipAsync = (bin: Uint8Array | string) => {
return new Promise<Buffer>((resolve, reject) => {
gzip(bin, (err, res) => {
if (err) {