This commit is contained in:
Rizky
2024-01-23 23:03:13 +07:00
commit 1bd21cf6a7
44 changed files with 2920 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
import { dirAsync, readAsync } from "fs-jetpack";
import { dir } from "./dir";
import { g } from "./global";
const _internal = { config: {} as any, writeTimeout: null as any };
export const config = {
init: async () => {
await dirAsync(dir(`${g.datadir}/config`));
await dirAsync(dir(`${g.datadir}/files`));
_internal.config =
(await readAsync(dir(`${g.datadir}/config/conf.json`), "json")) || {};
},
get all() {
return _internal.config;
},
get(key: string) {
if (key.endsWith("url")) {
if (!(_internal.config[key] instanceof URL)) {
_internal.config[key] = new URL(_internal.config[key] || "");
}
}
return _internal.config[key];
},
set(key: string, value: any) {
_internal.config[key] = value;
clearTimeout(_internal.writeTimeout);
_internal.writeTimeout = setTimeout(() => {
Bun.write(dir(`${g.datadir}/config/conf.json`), _internal.config);
}, 100);
},
};
+26
View File
@@ -0,0 +1,26 @@
import { file } from "bun";
import { watch } from "fs";
import { dir } from "./dir";
import { dirAsync } from "fs-jetpack";
export const startDevWatcher = async () => {
await dirAsync(dir(`app/srv/api`));
watch(dir(`app/srv/api`), async (event, filename) => {
const s = file(dir(`app/srv/api/${filename}`));
if (s.size === 0) {
await Bun.write(
`app/srv/api/${filename}`,
`\
import { apiContext } from "service-srv";
export const _ = {
url: "/${filename?.substring(0, filename.length - 3)})}",
async api() {
const { req, res } = apiContext(this);
return "This is ${filename}";
}
}`
);
}
});
};
+5
View File
@@ -0,0 +1,5 @@
import { join } from "path";
export const dir = (path: string) => {
return join(process.cwd(), path);
};
+37
View File
@@ -0,0 +1,37 @@
import { connect } from "bun";
import { g } from "./global";
export const ensureNotRunning = async () => {
await new Promise<void>(async (resolve) => {
const checkPort = () => {
return new Promise<boolean>(async (done) => {
try {
const s = await connect({
hostname: "0.0.0.0",
port: g.port,
socket: {
open(socket) {},
data(socket, data) {},
close(socket) {},
drain(socket) {},
error(socket, error) {},
},
});
s.end();
done(false);
} catch (e) {
done(true);
}
});
};
if (!(await checkPort())) {
g.log.warn(`Port ${process.env.PORT} is used, waiting...`);
setInterval(async () => {
if (await checkPort()) resolve();
}, 500);
} else {
resolve();
}
});
};
+88
View File
@@ -0,0 +1,88 @@
import { Server } from "bun";
import { Logger } from "pino";
import { RadixRouter } from "radix3";
import { PrismaClient } from "../../app/db/db";
import admin from "firebase-admin";
import { Database } from "bun:sqlite";
type SingleRoute = {
url: string;
args: string[];
fn: (...arg: any[]) => Promise<any>;
path: string;
};
export const g = global as unknown as {
db: PrismaClient;
dburl: string;
datadir: string;
mode: "dev" | "prod";
server: Server;
log: Logger;
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;
}[];
};
}
>;
router: RadixRouter<SingleRoute>;
port: number;
frm: {
js: string;
etag: string;
};
};
+25
View File
@@ -0,0 +1,25 @@
import { gzip, gunzip } from "zlib";
export const gzipAsync = (bin: Uint8Array) => {
return new Promise<Buffer>((resolve, reject) => {
gzip(bin, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
};
export const gunzipAsync = (bin: Uint8Array) => {
return new Promise<Buffer>((resolve, reject) => {
gunzip(bin, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};
+10
View File
@@ -0,0 +1,10 @@
import { pino } from "pino";
import { g } from "./global";
export const createLogger = async () => {
g.log = pino({
transport: {
target: "pino-pretty",
},
});
};
+17
View File
@@ -0,0 +1,17 @@
import { existsAsync } from "fs-jetpack";
import { dir } from "./dir";
import { $ } from "execa";
import { g } from "./global";
export const preparePrisma = async () => {
if (await existsAsync(dir("app/db/.env"))) {
if (!(await existsAsync(dir("node_modules/.prisma")))) {
await $({ cwd: dir(`app/db`) })`bun prisma generate`;
}
const { PrismaClient } = await import("../../app/db/db");
g.db = new PrismaClient();
}
g.dburl = process.env.DATABASE_URL || "";
g.port = parseInt(process.env.PORT || "3000");
};
+43
View File
@@ -0,0 +1,43 @@
export type DBArg = {
db: string;
table: string;
action: string;
params: any[];
};
export const execQuery = async (args: DBArg, prisma: any) => {
const { table, action, params } = args;
const tableInstance = prisma[table];
if (tableInstance) {
if (action === "query" && table.startsWith("$query")) {
try {
const q = params.shift();
q.sql = true;
Object.freeze(q);
return await tableInstance.bind(prisma)(q, ...params);
} catch (e) {
console.log(e);
return e;
}
}
const method = tableInstance[action];
if (method) {
try {
const result = await method(...params);
if (!result) {
return JSON.stringify(result);
}
return result;
} catch (e: any) {
throw new Error(e.message);
}
}
}
};
+10
View File
@@ -0,0 +1,10 @@
import { $ } from "execa";
import { g } from "./global";
export const restartServer = () => {
if (g.mode === "dev") {
$`bun ${g.mode}`;
}
process.exit(0);
};
+33
View File
@@ -0,0 +1,33 @@
export const waitUntil = (
condition: number | (() => any),
timeout?: number
) => {
return new Promise<void>(async (resolve) => {
if (typeof condition === "function") {
let tout = null as any;
if (timeout) {
tout = setTimeout(resolve, timeout);
}
if (await condition()) {
clearTimeout(tout);
resolve();
return;
}
let count = 0;
const c = setInterval(async () => {
if (await condition()) {
if (tout) clearTimeout(tout);
clearInterval(c);
resolve();
}
if (count > 100) {
clearInterval(c);
}
}, 10);
} else if (typeof condition === "number") {
setTimeout(() => {
resolve();
}, condition);
}
});
};