This commit is contained in:
Rizky 2024-02-18 07:23:29 +07:00
parent 3923747602
commit 922e7f5d37
10 changed files with 247 additions and 107 deletions

File diff suppressed because one or more lines are too long

View File

@ -31,7 +31,12 @@ glb.server_hook = async (arg) => {
const site_id = arr[2];
if (arr.length >= 3 && validate(site_id)) {
return await server.http(site_id, arg);
const res = await server.http(site_id, arg);
if (res instanceof Response) {
return res;
} else {
return new Response("403: Please see server.log", { status: 403 });
}
}
}

View File

@ -128,6 +128,8 @@ declare global {
index: { head: string[]; body: string[]; render: () => string };
}) => Promise<Response>;
};
function createServer(arg: PrasiServer): PrasiServer;
}
`
);

View File

@ -23,11 +23,13 @@ export const codeBuild = async (id_site: any) => {
`\
import type {} from "./typings/global";
export const server: PrasiServer = {
export const server = createServer({
async http({ req, handle, mode, url, index, server }) {
return await handle(req);
},
};
db,
api
});
`
);
const bun_types = Bun.spawn({
@ -62,14 +64,53 @@ typeof global.server_hook === "function"
? { ...global.console }
: global.console;
const db = {};
const api = {};
let db = new Proxy({}, {
get(_, key) {
if (key === '___site_id') {
return (site_id) => { _.site_id = site_id }
}
if (_.site_id) {
const runtime = global.server_runtime[_.site_id];
if (runtime && runtime.db) {
return runtime.db[key];
}
}
}
});
let api = {};
if (typeof global.server_hook === "function") {
const log = global.console.log;
console.log = function (...arg) {
const out = "${code.path(id_site, "site", "src", "server.log")}";
_fs.appendFile(out, arg.join(" ") + "\\n");
_fs.appendFile(out, arg.map((e)=>{
const ancestors = [];
if (typeof e === 'object') return JSON.stringify(e, (key, val) => {
if (val) {
if (typeof val === 'function') {
return '[function]';
}
if (typeof val === 'object') {
while (ancestors.length > 0 && ancestors.at(-1) !== this) {
ancestors.pop();
}
if (ancestors.includes(value)) {
return "[circular]";
}
ancestors.push(value);
if (val.constructor && val.constructor.name){
return '[class] ' + val.constructor.name;
}
}
}
return val;
}, 2);
return e;
}).join(" ") + "\\n");
}.bind(console);
} else {
db = global.db;
api = global.api;
}`,
},
plugins: [

View File

@ -0,0 +1,44 @@
import { g } from "utils/global";
import { dbProxy } from "../../../../../web/src/base/load/db/db-proxy";
if (!g.createServer) {
g.server_runtime = {};
g.createServer = (arg) => {
return async (site_id: string) => {
if (!g.server_runtime[site_id]) {
const site = await _db.site.findFirst({
where: {
id: site_id,
},
select: {
config: true,
},
});
if (site?.config && (site.config as any).api_url) {
try {
new URL((site.config as any).api_url);
g.server_runtime[site_id] = {
db: dbProxy((site.config as any).api_url),
api: null as any,
};
} catch (e) {}
}
}
const runtime = g.server_runtime[site_id];
if (runtime) {
const db = runtime.db as any;
if (
!!db._ &&
typeof db._._ === "function" &&
!!arg.db &&
typeof arg.db.___site_id === "function"
) {
arg.db.___site_id(site_id);
}
}
return arg;
};
};
}

View File

@ -8,6 +8,8 @@ import { WSData } from "../../../../../../pkgs/core/server/create";
import { codeBuild } from "./build-code";
import { prodIndex } from "../../../../util/prod-index";
import "./server-create";
const serverMain = () => ({
handler: {} as Record<string, PrasiServer>,
init_timeout: null as any,
@ -27,7 +29,7 @@ const serverMain = () => ({
},
init(site_id: string) {
clearTimeout(this.init_timeout);
this.init_timeout = setTimeout(() => {
this.init_timeout = setTimeout(async () => {
try {
const server_src_path = code.path(
site_id,
@ -38,7 +40,13 @@ const serverMain = () => ({
delete require.cache[server_src_path];
const svr = require(server_src_path);
if (svr && svr.server) {
this.handler[site_id] = svr.server;
if (typeof svr.server === "function") {
this.handler[site_id] = await svr.server(site_id);
} else {
this.handler[site_id] = svr.server;
}
this.handler[site_id].site_id = site_id;
}
Bun.write(
@ -46,7 +54,7 @@ const serverMain = () => ({
""
);
} catch (e) {
console.log(`Failed to init server ${site_id}`);
console.log(`Failed to init server ${site_id}`, e);
}
}, 100);
},
@ -67,6 +75,8 @@ const serverMain = () => ({
}
const handler = this.handler[site_id];
if (handler) {
if (!handler.site_id) handler.site_id = site_id;
try {
if (
handler.ws &&
@ -108,6 +118,7 @@ const serverMain = () => ({
});
type PrasiServer = {
site_id?: string;
ws?: WebSocketHandler<{ url: string }>;
http: (arg: {
url: { raw: URL; pathname: string };

View File

@ -2,6 +2,7 @@ import hash_sum from "hash-sum";
import { fetchViaProxy } from "../proxy";
export const dbProxy = (dburl: string) => {
const name = "";
return new Proxy(
{},
{
@ -50,12 +51,12 @@ export const dbProxy = (dburl: string) => {
{},
{
get(_, action: string) {
return (...params: any[]) => {
return async (...params: any[]) => {
if (table === "query") {
table = action;
action = "query";
}
return fetchSendDb(
return await fetchSendDb(
{
name,
action,

View File

@ -1,7 +1,7 @@
(BigInt.prototype as any).toJSON = function (): string {
return `BigInt::` + this.toString();
};
let w = window as any;
let w = (typeof window !== "undefined" ? window : null) as any;
export const fetchViaProxy = async (
url: string,
@ -14,24 +14,25 @@ export const fetchViaProxy = async (
let isFile = false;
const formatSingle = async (data: any) => {
if (!(data instanceof w.FormData || data instanceof w.File)) {
headers["content-type"] = "application/json";
} else {
if (data instanceof w.File) {
isFile = true;
let ab = await new Promise<ArrayBuffer | undefined>((resolve) => {
const reader = new FileReader();
reader.addEventListener("load", (e) => {
resolve(e.target?.result as ArrayBuffer);
if (w !== null) {
if (!(data instanceof w.FormData || data instanceof w.File)) {
headers["content-type"] = "application/json";
} else {
if (data instanceof w.File) {
isFile = true;
let ab = await new Promise<ArrayBuffer | undefined>((resolve) => {
const reader = new FileReader();
reader.addEventListener("load", (e) => {
resolve(e.target?.result as ArrayBuffer);
});
reader.readAsArrayBuffer(data);
});
reader.readAsArrayBuffer(data);
});
if (ab) {
data = new File([ab], data.name);
if (ab) {
data = new File([ab], data.name);
}
}
}
}
return data;
};
@ -44,11 +45,92 @@ export const fetchViaProxy = async (
body = JSON.stringify(body);
}
const cur = new URL(location.href);
const base = new URL(url);
if (cur.host === base.host) {
if (w !== null) {
const cur = new URL(location.href);
if (cur.host === base.host) {
const res = await fetch(
base.pathname,
data
? {
method: "POST",
body,
headers,
}
: undefined
);
const raw = await res.text();
try {
return JSON.parse(raw);
} catch (e) {
return raw;
}
} else {
if (
data instanceof File ||
(Array.isArray(data) && data[0] instanceof File)
) {
const target = new URL(url);
_headers["content-type"] = "multipart/form-data";
if (data instanceof File) {
const formData = new FormData();
formData.append("file", data);
const res = await fetch(target.pathname, {
body: formData,
method: "POST",
headers: _headers,
});
return await res.text();
} else {
const formData = new FormData();
let idx = 1;
for (const file of data) {
formData.append("file-" + idx++, file);
}
const res = await fetch(target.pathname, {
body: formData,
method: "POST",
headers: _headers,
});
return await res.text();
}
} else {
const res = await fetch(`${w.basehost ? w.basehost : ""}/_proxy`, {
method: "POST",
body: JSON.stringify([
{
url,
body,
headers,
},
]),
headers: { "content-type": "application/json" },
});
let text = "";
try {
text = await res.text();
return JSON.parse(text);
} catch (e) {
let formatted_body = null;
try {
formatted_body = JSON.stringify(JSON.parse(body), null, 2);
} catch (e) {}
console.warn(
`\n\n⚡ Failed to JSON.parse fetch result of ${url}:\n\n${JSON.stringify(
text
)} \n\nwith params:\n${formatted_body}`
);
return text;
}
}
}
} else {
const res = await fetch(
base.pathname,
base,
data
? {
method: "POST",
@ -63,65 +145,5 @@ export const fetchViaProxy = async (
} catch (e) {
return raw;
}
} else {
if (
data instanceof File ||
(Array.isArray(data) && data[0] instanceof File)
) {
const target = new URL(url);
_headers["content-type"] = "multipart/form-data";
if (data instanceof File) {
const formData = new FormData();
formData.append("file", data);
const res = await fetch(target.pathname, {
body: formData,
method: "POST",
headers: _headers,
});
return await res.text();
} else {
const formData = new FormData();
let idx = 1;
for (const file of data) {
formData.append("file-" + idx++, file);
}
const res = await fetch(target.pathname, {
body: formData,
method: "POST",
headers: _headers,
});
return await res.text();
}
} else {
const res = await fetch(`${w.basehost ? w.basehost : ""}/_proxy`, {
method: "POST",
body: JSON.stringify([
{
url,
body,
headers,
},
]),
headers: { "content-type": "application/json" },
});
let text = "";
try {
text = await res.text();
return JSON.parse(text);
} catch (e) {
let formatted_body = null;
try {
formatted_body = JSON.stringify(JSON.parse(body), null, 2);
} catch (e) {}
console.warn(
`\n\n⚡ Failed to JSON.parse fetch result of ${url}:\n\n${JSON.stringify(
text
)} \n\nwith params:\n${formatted_body}`
);
return text;
}
}
}
};

View File

@ -116,7 +116,6 @@ declare global {;
${baseTypings}
const moko: {nama: string};
${propText.join("\n")}
${iftext(

View File

@ -5,6 +5,11 @@ import { syncronize } from "y-pojo";
import type * as Y from "yjs";
import { PrismaClient } from "../../../app/db/db";
import { WSData } from "../server/create";
import {
ApiProxy,
apiProxy,
} from "../../../app/web/src/base/load/api/api-proxy";
import { dbProxy } from "../../../app/web/src/base/load/db/db-proxy";
type SingleRoute = {
url: string;
@ -22,6 +27,16 @@ export const g = global as unknown as {
handle: (req: Request) => Promise<Response | undefined>;
wsHandler: Record<string, WebSocketHandler<WSData>>;
}) => Promise<Response | undefined>;
server_runtime: Record<
string,
{
api: ReturnType<typeof apiProxy>;
db: ReturnType<typeof dbProxy>;
}
>;
createServer: (
arg: PrasiServer & { api: any; db: any }
) => (site_id: string) => Promise<PrasiServer & { api: any; db: any }>;
ws_hook?: WebSocketHandler<WSData>;
_db: PrismaClient;
dburl: string;