fix prasi

This commit is contained in:
Rizky 2023-10-14 21:32:41 +07:00
parent 367926ae50
commit bfcbb0f7a8
33 changed files with 253 additions and 154 deletions

83
app/srv/api/npm.ts Normal file
View File

@ -0,0 +1,83 @@
import crypto from "crypto";
import { dir } from "dir";
import { readAsync } from "fs-jetpack";
import mime from "mime-types";
import { apiContext } from "service-srv";
import { glb } from "../global";
export const _ = {
url: "/npm/:mode/:id/*",
async api(mode: "site" | "page", id: string) {
const { req, res, mode: _mode } = apiContext(this);
let path = dir.path(`../npm/${mode}/${id}/${req.params._}`);
res.setHeader("Access-Control-Allow-Origin", "*");
if (!glb.npm) {
glb.npm = { page: {}, site: {} };
}
const contentType = mime.lookup(path);
if (contentType) res.setHeader("content-type", contentType);
if (path.endsWith(`${mode}.js`)) {
path = path.substring(0, path.length - `${mode}.js`.length) + `index.js`;
if (glb.npm[mode][id]) {
const npm = glb.npm[mode][id];
if (npm) {
res.setHeader("etag", npm.etag);
res.setHeader("content-length", npm.file.byteLength.toString());
if (npm.etag === req.headers.get("if-none-match")) {
res.sendStatus(304);
return;
}
res.send(npm.file);
readAsync(path, "buffer").then((file) => {
if (file && path.endsWith("index.js")) {
glb.npm[mode][id] = {
file,
etag: crypto.createHash("md5").update(file).digest("hex"),
};
}
});
return;
}
}
}
if (path.length > dir.path(`../npm/${mode}/${id}`).length) {
const file = await readAsync(path, "buffer");
if (file) {
if (path.endsWith("index.js")) {
glb.npm[mode][id] = {
file,
etag: crypto.createHash("md5").update(file).digest("hex"),
};
const npm = glb.npm[mode][id];
if (npm) {
res.setHeader("etag", npm.etag);
if (npm.etag === req.headers.get("if-none-match")) {
res.sendStatus(304);
return;
}
}
}
res.setHeader("content-length", file.byteLength.toString());
res.send(file);
return;
} else {
glb.npm[mode][id] = null;
}
}
res.setHeader("etag", "empty");
res.send("");
},
};

37
app/srv/exports.d.ts vendored
View File

@ -1,3 +1,4 @@
/// <reference types="node" />
declare module "api/auth/login" { declare module "api/auth/login" {
export const _: { export const _: {
url: string; url: string;
@ -18,6 +19,38 @@ declare module "api/session" {
api(): Promise<any>; api(): Promise<any>;
}; };
} }
declare module "global" {
import { site, user } from "dbgen";
import { ExecaChildProcess } from "execa";
export const glb: {
lastUpdate: Record<string, number>;
prasiSrv: {
status: Record<string, "unavailable" | "installing" | "starting" | "started" | "stopped" | "destroying">;
running: Record<string, ExecaChildProcess>;
};
npm: {
page: Record<string, null | {
file: Buffer;
etag: string;
}>;
site: Record<string, null | {
file: Buffer;
etag: string;
}>;
};
};
export type Session = {
user: user & {
site: site[];
};
};
}
declare module "api/npm" {
export const _: {
url: string;
api(mode: "site" | "page", id: string): Promise<void>;
};
}
declare module "exports" { declare module "exports" {
export const login: { export const login: {
name: string; name: string;
@ -33,12 +66,12 @@ declare module "exports" {
args: any[]; args: any[];
handler: Promise<typeof import("api/session")>; handler: Promise<typeof import("api/session")>;
}; };
export const _web: { export const npm: {
name: string; name: string;
url: string; url: string;
path: string; path: string;
args: string[]; args: string[];
handler: Promise<any>; handler: Promise<typeof import("api/npm")>;
}; };
export const _upload: { export const _upload: {
name: string; name: string;

View File

@ -12,12 +12,12 @@ export const session = {
args: [], args: [],
handler: import("./api/session") handler: import("./api/session")
} }
export const _web = { export const npm = {
name: "_web", name: "npm",
url: "/_web/:id/**", url: "/npm/:mode/:id/*",
path: "app/srv/api/_web.ts", path: "app/srv/api/npm.ts",
args: ["id","_"], args: ["mode","id"],
handler: import("./api/_web") handler: import("./api/npm")
} }
export const _upload = { export const _upload = {
name: "_upload", name: "_upload",
@ -28,14 +28,14 @@ export const _upload = {
} }
export const _prasi = { export const _prasi = {
name: "_prasi", name: "_prasi",
url: "/_prasi/**", url: "/_prasi/*",
path: "app/srv/api/_prasi.ts", path: "app/srv/api/_prasi.ts",
args: [], args: [],
handler: import("./api/_prasi") handler: import("./api/_prasi")
} }
export const _file = { export const _file = {
name: "_file", name: "_file",
url: "/_file/**", url: "/_file/*",
path: "app/srv/api/_file.ts", path: "app/srv/api/_file.ts",
args: [], args: [],
handler: import("./api/_file") handler: import("./api/_file")

26
app/srv/global.ts Normal file
View File

@ -0,0 +1,26 @@
import { site, user } from "dbgen";
import { ExecaChildProcess } from "execa";
export const glb = global as unknown as {
lastUpdate: Record<string, number>;
prasiSrv: {
status: Record<
string,
| "unavailable"
| "installing"
| "starting"
| "started"
| "stopped"
| "destroying"
>;
running: Record<string, ExecaChildProcess>;
};
npm: {
page: Record<string, null | { file: Buffer; etag: string }>;
site: Record<string, null | { file: Buffer; etag: string }>;
};
};
export type Session = {
user: user & { site: site[] };
};

View File

@ -1,12 +1,17 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { page } from "web-utils"; import { page } from "web-utils";
import { Loading } from "../../utils/ui/loading";
export default page({ export default page({
url: "**", url: "**",
component: ({}) => { component: ({}) => {
useEffect(() => { useEffect(() => {
navigate("/login"); if (localStorage.getItem("prasi-session")) {
navigate("/editor/_/_");
} else {
navigate("/login");
}
}, []); }, []);
return <div>Loading...</div>; return <Loading />;
}, },
}); });

View File

@ -21,7 +21,9 @@ export default page({
if (rto) { if (rto) {
navigate(rto); navigate(rto);
} else { } else {
navigate("/editor"); console.log("navigate to");
localStorage.setItem("prasi-session", JSON.stringify(s));
navigate("/editor/_/_");
} }
} else { } else {
form.init = true; form.init = true;
@ -50,7 +52,7 @@ export default page({
if (rto) { if (rto) {
navigate(rto); navigate(rto);
} else { } else {
navigate("/editor"); navigate("/editor/_/_");
} }
} }
}} }}

View File

@ -1,7 +1,6 @@
import { page } from "web-utils";
import { useLocal } from "web-utils";
import { Loading } from "../../utils/ui/loading";
import { Suspense, lazy, useEffect } from "react"; import { Suspense, lazy, useEffect } from "react";
import { page, useLocal } from "web-utils";
import { Loading } from "../../utils/ui/loading";
const Editor = lazy(async () => ({ const Editor = lazy(async () => ({
default: (await import("../../render/editor/editor")).Editor, default: (await import("../../render/editor/editor")).Editor,
@ -35,7 +34,8 @@ export default page({
let e = await api.session(); let e = await api.session();
if (!e) { if (!e) {
(window as any).redirectTo = location.pathname; (window as any).redirectTo = location.pathname;
navigate("/login"); console.log("session not found");
// navigate("/login");
localStorage.removeItem("prasi-session"); localStorage.removeItem("prasi-session");
} else { } else {
localStorage.setItem("prasi-session", JSON.stringify(e)); localStorage.setItem("prasi-session", JSON.stringify(e));

View File

@ -6,3 +6,8 @@ export const login = {
url: "/login", url: "/login",
page: () => import("./page/auth/login"), page: () => import("./page/auth/login"),
}; };
export const editor = {
url: "/editor/:site_id/:page_id",
page: () => import("./page/editor"),
};

View File

@ -2,22 +2,25 @@ import { createRouter } from "radix3";
import { FC, Suspense, lazy } from "react"; import { FC, Suspense, lazy } from "react";
import { GlobalContext, useLocal } from "web-utils"; import { GlobalContext, useLocal } from "web-utils";
import { Loading } from "../utils/ui/loading"; import { Loading } from "../utils/ui/loading";
import { w } from "../utils/types/general";
export const Root: FC<{}> = ({}) => { export const Root: FC<{}> = ({}) => {
const local = useLocal( const local = useLocal(
{ {
router: createRouter<any>({ strictTrailingSlash: true }), router: createRouter<{ url: string; Page: FC<any> }>({
strictTrailingSlash: true,
}),
Page: null as any, Page: null as any,
}, },
async () => { async () => {
const pages = await import("./pages"); const pages = await import("./pages");
for (const [_, v] of Object.entries(pages)) { for (const [_, v] of Object.entries(pages)) {
local.router.insert( local.router.insert(v.url, {
v.url, url: v.url,
lazy(async () => { Page: lazy(async () => {
return { default: (await v.page()).default.component as any }; return { default: (await v.page()).default.component as any };
}) }),
); });
} }
local.render(); local.render();
} }
@ -28,7 +31,8 @@ export const Root: FC<{}> = ({}) => {
const found = local.router.lookup(location.pathname); const found = local.router.lookup(location.pathname);
if (found) { if (found) {
local.Page = found; w.params = found.params;
local.Page = found.Page;
} }
if (!local.Page) { if (!local.Page) {

View File

@ -3,14 +3,14 @@ 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 { createAPI, createDB, reloadDBAPI } from "./utils/script/init-api";
import { w } from "./utils/types/general";
const start = async () => { const start = async () => {
registerServiceWorker(); registerServiceWorker();
defineReact(); defineReact();
await defineWindow(false); await defineWindow(false);
const w = window as any;
const base = `${location.protocol}//${location.host}`; const base = `${location.protocol}//${location.host}`;
w.serverurl = base;
await reloadDBAPI(base); await reloadDBAPI(base);
w.api = createAPI(base); w.api = createAPI(base);
w.db = createDB(base); w.db = createDB(base);

View File

@ -1,5 +1,5 @@
import { FC, ReactElement, ReactNode } from "react"; import { FC, ReactElement, ReactNode } from "react";
import { createRouter } from "web-utils"; import { createRouter } from "radix3";
import { CompDoc } from "../../../base/global/content-editor"; import { CompDoc } from "../../../base/global/content-editor";
import { IContent, MContent, MPage } from "../../../utils/types/general"; import { IContent, MContent, MPage } from "../../../utils/types/general";
import { IItem, MItem } from "../../../utils/types/item"; import { IItem, MItem } from "../../../utils/types/item";

View File

@ -91,7 +91,7 @@ export const ScriptMonacoElement: FC<{
} }
if (!w.importCache.prettier_parser) if (!w.importCache.prettier_parser)
w.importCache.prettier_parser = await import( w.importCache.prettier_parser = await import(
"prettier/parser-typescript" "prettier/plugins/typescript"
); );
if (!w.importCache.prettier) if (!w.importCache.prettier)

View File

@ -56,7 +56,7 @@ export const jsMount = async (p: PG, editor: MonacoEditor, monaco: Monaco) => {
} }
if (!w.importCache.prettier_parser) if (!w.importCache.prettier_parser)
w.importCache.prettier_parser = await import( w.importCache.prettier_parser = await import(
"prettier/parser-typescript" "prettier/plugins/typescript"
); );
if (!w.importCache.prettier) if (!w.importCache.prettier)

View File

@ -1,6 +1,5 @@
import { page as dbpage } from "dbgen"; import { page as dbpage } from "dbgen";
import { TypedDoc, TypedMap } from "yjs-types"; import { TypedDoc, TypedMap } from "yjs-types";
import { loadSite } from "../../../../srv/edit/tools/load-site";
import { IItem, MItem } from "./item"; import { IItem, MItem } from "./item";
import { IRoot, MRoot } from "./root"; import { IRoot, MRoot } from "./root";
import { ISection, MSection } from "./section"; import { ISection, MSection } from "./section";
@ -31,6 +30,9 @@ export const w = window as unknown as {
params: any; params: any;
editorGlbDefault: string; editorGlbDefault: string;
ts: number; ts: number;
serverurl: string;
api: any;
db: any;
}; };
export type Page = { export type Page = {
@ -47,7 +49,6 @@ export type MPage = TypedDoc<{
} }
>; >;
}>; }>;
export type Site = Exclude<Awaited<ReturnType<typeof loadSite>>, null>;
export type IContent = ISection | IItem | IText; export type IContent = ISection | IItem | IText;
export type MContent = MSection | MItem | MText; export type MContent = MSection | MItem | MText;

View File

@ -20,5 +20,10 @@
"isolatedModules": true, "isolatedModules": true,
// "noEmit": true, // "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"paths": {
"dbgen": [
"../../node_modules/.prisma/client/index.d.ts"
],
}
}, },
} }

BIN
bun.lockb

Binary file not shown.

View File

@ -19,5 +19,5 @@
"peerDependencies": { "peerDependencies": {
"typescript": "^5.0.0" "typescript": "^5.0.0"
}, },
"dependencies": { "@node-rs/argon2": "^1.5.2" } "dependencies": { "@node-rs/argon2": "^1.5.2", "@types/mime-types": "^2.1.2", "mime-types": "^2.1.35" }
} }

View File

@ -2,14 +2,14 @@ import { apiContext } from "../server/api-ctx";
import { dir } from "../utils/dir"; import { dir } from "../utils/dir";
export const _ = { export const _ = {
url: "/_file/**", url: "/_file/*",
async api() { async api() {
const { req } = apiContext(this); const { req } = apiContext(this);
const rpath = decodeURIComponent(req.params._); const rpath = decodeURIComponent(req.params._);
const path = dir(`../data/upload/${rpath}`); const path = dir.path(`../data/upload/${rpath}`);
try { try {
return new Response(Bun.file(path)); return new Response(Bun.file(path) as any);
} catch (e) { } catch (e) {
return new Response("NOT FOUND", { status: 404 }); return new Response("NOT FOUND", { status: 404 });
} }

View File

@ -9,7 +9,7 @@ const cache = {
}; };
export const _ = { export const _ = {
url: "/_prasi/**", url: "/_prasi/*",
async api() { async api() {
const { req, res } = apiContext(this); const { req, res } = apiContext(this);
res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Origin", "*");
@ -95,7 +95,7 @@ declare module "gen/srv/api/entry" {
export * as srv from "gen/srv/api/srv"; export * as srv from "gen/srv/api/srv";
} }
` + ` +
((await readAsync(dir("app/srv/exports.d.ts"))) || "") ((await readAsync(dir.path("app/srv/exports.d.ts"))) || "")
.replace(/\"app\/srv\/api/gi, '"srv/api') .replace(/\"app\/srv\/api/gi, '"srv/api')
.replace( .replace(
'declare module "app/srv/exports"', 'declare module "app/srv/exports"',
@ -108,20 +108,20 @@ const getPrisma = async (path: string) => {
if (path === "prisma") if (path === "prisma")
return JSON.stringify( return JSON.stringify(
( (
(await readAsync(dir("node_modules/.prisma/client/index.d.ts"))) || "" (await readAsync(dir.path("node_modules/.prisma/client/index.d.ts"))) || ""
).replace(`@prisma/client/runtime/library`, `./runtime/library`) ).replace(`@prisma/client/runtime/library`, `./runtime/library`)
); );
if (path === "runtime") if (path === "runtime")
return JSON.stringify( return JSON.stringify(
await readAsync( await readAsync(
dir("node_modules/@prisma/client/runtime/index-browser.d.ts") dir.path("node_modules/@prisma/client/runtime/index-browser.d.ts")
) )
); );
if (path === "library") if (path === "library")
return JSON.stringify( return JSON.stringify(
await readAsync(dir("node_modules/@prisma/client/runtime/library.d.ts")) await readAsync(dir.path("node_modules/@prisma/client/runtime/library.d.ts"))
); );
return JSON.stringify({}); return JSON.stringify({});

View File

@ -21,7 +21,7 @@ export const _ = {
.toLowerCase()}`; .toLowerCase()}`;
url = `/_file/${path}`; url = `/_file/${path}`;
await writeAsync(dir(`../data/upload/${path}`), part.buffer); await writeAsync(dir.path(`../data/upload/${path}`), part.buffer);
} }
return url; return url;

View File

@ -1,79 +0,0 @@
import mime from "mime";
import { apiContext } from "../server/api-ctx";
import { g } from "../utils/global";
import { getApiEntry } from "./_prasi";
export const _ = {
url: "/_web/:id/**",
async api(id: string, _: string) {
const { req, res } = apiContext(this);
const web = g.web[id];
if (web) {
const cache = web.cache;
if (cache) {
const parts = _.split("/");
switch (parts[0]) {
case "site": {
res.setHeader("content-type", "application/json");
if (req.query_parameters["prod"]) {
return {
site: cache.site,
pages: cache.pages.map((e) => {
return {
id: e.id,
url: e.url,
};
}),
api: getApiEntry(),
};
} else {
return cache.site;
}
}
case "pages": {
res.setHeader("content-type", "application/json");
return cache.pages.map((e) => {
return {
id: e.id,
url: e.url,
};
});
}
case "page": {
res.setHeader("content-type", "application/json");
return cache.pages.find((e) => e.id === parts[1]);
}
case "npm-site": {
let path = parts.slice(1).join("/");
res.setHeader("content-type", mime.getType(path) || "text/plain");
if (path === "site.js") {
path = "index.js";
}
return cache.npm.site[path];
}
case "npm-page": {
const page_id = parts[1];
if (cache.npm.pages[page_id]) {
let path = parts.slice(2).join("/");
res.setHeader("content-type", mime.getType(path) || "text/plain");
if (path === "page.js") {
path = "index.js";
}
return cache.npm.pages[page_id][path];
}
res.setHeader("content-type", "text/javascript");
}
case "comp": {
res.setHeader("content-type", "application/json");
return cache.comps.find((e) => e.id === parts[1]);
}
}
}
}
return req.params;
},
};

View File

@ -1,3 +1,4 @@
import { g } from "utils/global";
const parseQueryParams = (ctx: any) => { const parseQueryParams = (ctx: any) => {
const pageHref = ctx.req.url; const pageHref = ctx.req.url;
@ -15,6 +16,7 @@ export const apiContext = (ctx: any) => {
ctx.req.params = ctx.params; ctx.req.params = ctx.params;
ctx.req.query_parameters = parseQueryParams(ctx); ctx.req.query_parameters = parseQueryParams(ctx);
return { return {
mode: g.mode,
req: ctx.req as Request & { params: any; query_parameters: any }, req: ctx.req as Request & { params: any; query_parameters: any },
res: { res: {
...ctx.res, ...ctx.res,

View File

@ -22,7 +22,7 @@ export const prepareApiRoutes = async () => {
path: importPath.substring((root || path).length + 1), path: importPath.substring((root || path).length + 1),
}; };
g.api[filename] = route; g.api[filename] = route;
g.router.insert(route.url, g.api[filename]); g.router.insert(route.url.replace(/\*/gi, "**"), g.api[filename]);
} catch (e) { } catch (e) {
g.log.warn( g.log.warn(
`Failed to import app/srv/api${importPath.substring( `Failed to import app/srv/api${importPath.substring(
@ -46,6 +46,6 @@ export const prepareApiRoutes = async () => {
} }
} }
}; };
await scan(dir(`app/srv/api`)); await scan(dir.path(`app/srv/api`));
await scan(dir(`pkgs/core/api`)); await scan(dir.path(`pkgs/core/api`));
}; };

View File

@ -3,6 +3,7 @@ import { dir } from "../utils/dir";
import { g } from "../utils/global"; import { g } from "../utils/global";
import { serveAPI } from "./serve-api"; import { serveAPI } from "./serve-api";
const cache = { static: {} as Record<string, any> };
export const createServer = async () => { export const createServer = async () => {
g.api = {}; g.api = {};
g.router = createRouter({ strictTrailingSlash: true }); g.router = createRouter({ strictTrailingSlash: true });
@ -22,15 +23,21 @@ export const createServer = async () => {
} }
try { try {
const file = Bun.file(dir(`app/static${url.pathname}`)); if (cache.static[url.pathname]) {
if (file.type !== "application/octet-stream") { return new Response(cache.static[url.pathname]);
}
const file = Bun.file(dir.path(`app/static${url.pathname}`));
if ((await file.exists()) && file.type !== "application/octet-stream") {
cache.static[url.pathname] = file;
return new Response(file as any); return new Response(file as any);
} }
} catch (e) { } catch (e) {
g.log.error(e); g.log.error(e);
} }
try { try {
return new Response(Bun.file(dir(`app/static/index.html`)) as any); return new Response(Bun.file(dir.path(`app/static/index.html`)) as any);
} catch (e) { } catch (e) {
g.log.error(e); g.log.error(e);
return new Response("Loading..."); return new Response("Loading...");

View File

@ -16,20 +16,20 @@ export const ${name} = {
handler: import("./api/${v.path.substring(0, v.path.length - 3)}") handler: import("./api/${v.path.substring(0, v.path.length - 3)}")
}`); }`);
} }
await Bun.write(dir(`app/srv/exports.ts`), out.join(`\n`)); await Bun.write(dir.path(`app/srv/exports.ts`), out.join(`\n`));
const targetFile = dir("app/srv/exports.d.ts"); const targetFile = dir.path("app/srv/exports.d.ts");
const tsc = spawn( const tsc = spawn(
[ [
dir("node_modules/.bin/tsc"), dir.path("node_modules/.bin/tsc"),
dir("app/srv/exports.ts"), dir.path("app/srv/exports.ts"),
"--declaration", "--declaration",
"--emitDeclarationOnly", "--emitDeclarationOnly",
"--outFile", "--outFile",
targetFile, targetFile,
], ],
{ {
cwd: dir(`node_modules/.bin`), cwd: dir.path(`node_modules/.bin`),
} }
); );

View File

@ -11,15 +11,15 @@ const res = await fetch(
const data = await unzipper.Open.buffer(Buffer.from(await res.arrayBuffer())); const data = await unzipper.Open.buffer(Buffer.from(await res.arrayBuffer()));
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
await removeAsync(dir("pkgs")); await removeAsync(dir.path("pkgs"));
for (const file of data.files) { for (const file of data.files) {
if (file.type === "File") { if (file.type === "File") {
const path = file.path.split("/").slice(1).join("/"); const path = file.path.split("/").slice(1).join("/");
if (path === "tsconfig.json" || path.startsWith("pkgs")) { if (path === "tsconfig.json" || path.startsWith("pkgs")) {
promises.push( promises.push(
new Promise<void>(async (done) => { new Promise<void>(async (done) => {
await dirAsync(dirname(dir(path))); await dirAsync(dirname(dir.path(path)));
await Bun.write(dir(path), await file.buffer()); await Bun.write(dir.path(path), await file.buffer());
done(); done();
}) })
); );

View File

@ -5,11 +5,11 @@ const _internal = { config: {} as any, writeTimeout: null as any };
export const config = { export const config = {
init: async () => { init: async () => {
await dirAsync(dir("../data/config")); await dirAsync(dir.path("../data/config"));
await dirAsync(dir("../data/files")); await dirAsync(dir.path("../data/files"));
_internal.config = _internal.config =
(await readAsync(dir("../data/config/conf.json"), "json")) || {}; (await readAsync(dir.path("../data/config/conf.json"), "json")) || {};
}, },
get all() { get all() {
return _internal.config; return _internal.config;
@ -27,7 +27,7 @@ export const config = {
_internal.config[key] = value; _internal.config[key] = value;
clearTimeout(_internal.writeTimeout); clearTimeout(_internal.writeTimeout);
_internal.writeTimeout = setTimeout(() => { _internal.writeTimeout = setTimeout(() => {
Bun.write(dir("../data/config/conf.json"), _internal.config); Bun.write(dir.path("../data/config/conf.json"), _internal.config);
}, 100); }, 100);
}, },
}; };

View File

@ -4,9 +4,9 @@ import { dir } from "./dir";
import { dirAsync } from "fs-jetpack"; import { dirAsync } from "fs-jetpack";
export const startDevWatcher = async () => { export const startDevWatcher = async () => {
await dirAsync(dir(`app/srv/api`)); await dirAsync(dir.path(`app/srv/api`));
watch(dir(`app/srv/api`), async (event, filename) => { watch(dir.path(`app/srv/api`), async (event, filename) => {
const s = file(dir(`app/srv/api/${filename}`)); const s = file(dir.path(`app/srv/api/${filename}`));
if (s.size === 0) { if (s.size === 0) {
await Bun.write( await Bun.write(
`app/srv/api/${filename}`, `app/srv/api/${filename}`,

View File

@ -1,5 +1,7 @@
import { join } from "path"; import { join } from "path";
export const dir = (path: string) => { export const dir = {
return join(process.cwd(), path); path: (path: string) => {
return join(process.cwd(), path);
},
}; };

View File

@ -7,28 +7,28 @@ export const parcelBuild = async () => {
await dirAsync("app/static"); await dirAsync("app/static");
const args = [ const args = [
"node", "node",
dir("node_modules/.bin/parcel"), dir.path("node_modules/.bin/parcel"),
g.mode === "dev" ? "watch" : "build", g.mode === "dev" ? "watch" : "build",
"./src/index.tsx", "./src/index.tsx",
g.mode === "dev" ? "--no-hmr" : "--no-optimize", g.mode === "dev" ? "--no-hmr" : "--no-optimize",
"--dist-dir", "--dist-dir",
dir(`app/static`), dir.path(`app/static`),
]; ];
g.log.info(`Building web with parcel`); g.log.info(`Building web with parcel`);
if (g.mode !== "dev") { if (g.mode !== "dev") {
await removeAsync(dir("app/static")); await removeAsync(dir.path("app/static"));
await removeAsync(dir("app/web/.parcel-cache")); await removeAsync(dir.path("app/web/.parcel-cache"));
const parcel = spawn({ const parcel = spawn({
cmd: args, cmd: args,
cwd: dir("app/web"), cwd: dir.path("app/web"),
stdio: ["ignore", "inherit", "inherit"], stdio: ["ignore", "inherit", "inherit"],
}); });
await parcel.exited; await parcel.exited;
} else { } else {
const parcel = spawn({ const parcel = spawn({
cmd: args, cmd: args,
cwd: dir("app/web"), cwd: dir.path("app/web"),
stdio: ["ignore", "pipe", "pipe"], stdio: ["ignore", "pipe", "pipe"],
}); });

View File

@ -4,9 +4,9 @@ import { $ } from "execa";
import { g } from "./global"; import { g } from "./global";
export const preparePrisma = async () => { export const preparePrisma = async () => {
if (await existsAsync(dir("app/db/.env"))) { if (await existsAsync(dir.path("app/db/.env"))) {
if (!(await existsAsync(dir("node_modules/.prisma")))) { if (!(await existsAsync(dir.path("node_modules/.prisma")))) {
await $({ cwd: dir(`app/db`) })`bun prisma generate`; await $({ cwd: dir.path(`app/db`) })`bun prisma generate`;
} }
const { PrismaClient } = await import("../../../app/db/db"); const { PrismaClient } = await import("../../../app/db/db");
g.db = new PrismaClient(); g.db = new PrismaClient();

View File

@ -14,7 +14,7 @@ export const createCache = <T>() => ({
lmdb: null as unknown as RootDatabase<SessionEntry<T>>, lmdb: null as unknown as RootDatabase<SessionEntry<T>>,
cookieKey: "", cookieKey: "",
async init(arg: { cookieKey: string; dbname?: string }) { async init(arg: { cookieKey: string; dbname?: string }) {
const dbpath = dir(join(g.datadir, (arg.dbname || "session") + ".lmdb")); const dbpath = dir.path(join(g.datadir, (arg.dbname || "session") + ".lmdb"));
await dirAsync(dirname(dbpath)); await dirAsync(dirname(dbpath));
self(this).lmdb = open({ self(this).lmdb = open({
path: dbpath, path: dbpath,

View File

@ -23,6 +23,9 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"allowJs": true, "allowJs": true,
"paths": { "paths": {
"dir": [
"./pkgs/core/utils/dir.ts"
],
"dbgen": [ "dbgen": [
"./node_modules/.prisma/client/index.d.ts" "./node_modules/.prisma/client/index.d.ts"
], ],