This commit is contained in:
Rizky 2023-10-14 23:08:09 +07:00
parent 415f42cefc
commit 3cb7c393ec
10 changed files with 179 additions and 247 deletions

View File

@ -1,15 +1,19 @@
import { Websocket } from "hyper-express"; import { ServerWebSocket } from "bun";
import { compress } from "lz-string"; import { compress } from "lz-string";
import { syncronize } from "y-pojo"; import { syncronize } from "y-pojo";
import * as Y from "yjs"; import * as Y from "yjs";
import { WSData } from "../../../../../pkgs/core/server/create";
import { SingleComp, eg } from "../edit-global";
import { import {
WS_MSG_GET_COMP, WS_MSG_GET_COMP,
WS_MSG_SET_COMP, WS_MSG_SET_COMP,
WS_MSG_SV_LOCAL, WS_MSG_SV_LOCAL,
} from "../../../web/src/utils/types/ws"; } from "../../../../web/src/utils/types/ws";
import { SingleComp, eg } from "../edit-global";
export const getComp = async (ws: Websocket, msg: WS_MSG_GET_COMP) => { export const getComp = async (
ws: ServerWebSocket<WSData>,
msg: WS_MSG_GET_COMP
) => {
const comp_id = msg.comp_id; const comp_id = msg.comp_id;
if (!eg.edit.comp[comp_id]) { if (!eg.edit.comp[comp_id]) {
@ -17,6 +21,15 @@ export const getComp = async (ws: Websocket, msg: WS_MSG_GET_COMP) => {
where: { where: {
id: comp_id, id: comp_id,
}, },
select: {
component_group: true,
content_tree: true,
id: true,
id_component_group: true,
name: true,
props: true,
type: true,
},
}); });
if (!rawComp) { if (!rawComp) {
@ -34,7 +47,7 @@ export const getComp = async (ws: Websocket, msg: WS_MSG_GET_COMP) => {
const map = ydoc.getMap("map"); const map = ydoc.getMap("map");
syncronize(map as any, rawComp); syncronize(map as any, rawComp);
const ws = new Set<Websocket>(); const ws = new Set<ServerWebSocket<WSData>>();
const um = new Y.UndoManager(map, { ignoreRemoteMapChanges: true }); const um = new Y.UndoManager(map, { ignoreRemoteMapChanges: true });
const broadcast = () => { const broadcast = () => {
const sv_local = compress(Y.encodeStateVector(ydoc as any).toString()); const sv_local = compress(Y.encodeStateVector(ydoc as any).toString());

View File

@ -1,54 +0,0 @@
import { Websocket } from "hyper-express";
import { syncronize } from "y-pojo";
import * as Y from "yjs";
import { fillID } from "../../../web/src/utils/page/tools/fill-id";
import { IContent, MContent } from "../../../web/src/utils/types/general";
import { IItem } from "../../../web/src/utils/types/item";
import { IRoot } from "../../../web/src/utils/types/root";
import { getComp } from "../action/get-comp";
import { eg } from "../edit-global";
const MAX_STRING_LENGTH = 15000;
export const validateTreeMap = async (
ws: Websocket,
item: MContent,
changed?: boolean
) => {
let _changed = changed;
const type = item.get("type") as IContent["type"] | "root";
if (type !== "root") {
item.forEach((val, key, map) => {
if (typeof val === "string") {
if (val.length > MAX_STRING_LENGTH) {
map.set(key, "");
_changed = true;
}
} else {
if (typeof val === "object" && val instanceof Y.Map) {
val._map.forEach((ival, ikey, imap) => {
if (typeof ival === "string") {
if ((ival as string).length > MAX_STRING_LENGTH) {
imap.set(ikey, "" as any);
_changed = true;
}
}
});
}
}
});
}
if (item) {
if (type !== "text") {
const childs = item.get("childs");
if (childs) {
for (const c of childs) {
if (await validateTreeMap(ws, c)) {
_changed = true;
}
}
}
}
}
return !!_changed;
};

View File

@ -1,19 +1,19 @@
import { Suspense, lazy, useEffect } from "react"; import { FC, useEffect } from "react";
import { page, useLocal } from "web-utils"; import { page, useGlobal, useLocal } from "web-utils";
import { EditorGlobal } from "../../render/editor/logic/global";
import { Loading } from "../../utils/ui/loading"; import { Loading } from "../../utils/ui/loading";
const Editor = lazy(async () => ({
default: (await import("../../render/editor/editor")).Editor,
}));
export default page({ export default page({
url: "/editor/:site_id/:page_id", url: "/editor/:site_id/:page_id",
component: ({}) => { component: ({}) => {
const p = useGlobal(EditorGlobal, "EDITOR");
const local = useLocal({ const local = useLocal({
loading: true, loading: true,
session: null as any, session: null as any,
notfound: false, notfound: false,
init: false, init: false,
Editor: null as null | FC<any>,
}); });
const site_id = params.site_id === "_" ? "" : params.site_id; const site_id = params.site_id === "_" ? "" : params.site_id;
const page_id = params.page_id === "_" ? "" : params.page_id; const page_id = params.page_id === "_" ? "" : params.page_id;
@ -21,6 +21,10 @@ export default page({
useEffect(() => { useEffect(() => {
if (!local.init) { if (!local.init) {
(async () => { (async () => {
if (!local.Editor) {
local.Editor = (await import("../../render/editor/editor")).Editor;
}
let ses: any = null; let ses: any = null;
try { try {
ses = JSON.parse(localStorage.getItem("prasi-session") || ""); ses = JSON.parse(localStorage.getItem("prasi-session") || "");
@ -129,12 +133,11 @@ export default page({
} }
}, [local.init]); }, [local.init]);
if (local.loading) return <Loading note="base-page" />; const Editor = local.Editor;
if (local.loading || !Editor) return <Loading note="base-page" />;
return ( return (
<Suspense fallback={<Loading note="editor-init" />}> <Editor session={local.session} site_id={site_id} page_id={page_id} />
<Editor session={local.session} site_id={site_id} page_id={page_id} />
</Suspense>
); );
}, },
}); });

View File

@ -27,6 +27,7 @@ export const Root: FC<{}> = ({}) => {
); );
prasiContext.render = local.render; prasiContext.render = local.render;
const Provider = GlobalContext.Provider as FC<{ value: any; children: any }>; const Provider = GlobalContext.Provider as FC<{ value: any; children: any }>;
const found = local.router.lookup(location.pathname); const found = local.router.lookup(location.pathname);

View File

@ -79,7 +79,7 @@ export const Editor: FC<{ site_id: string; page_id: string; session: any }> = ({
useEffect(() => { useEffect(() => {
if (p.status !== "init" && w.prasiApi) { if (p.status !== "init" && w.prasiApi) {
for (const [k, v] of Object.entries(deepClone(EditorGlobal))) { for (const [k, v] of Object.entries(deepClone(EditorGlobal))) {
if (k === "session" || k === "site") continue; if (k === "session" || k === "site" || "status") continue;
(p as any)[k] = v; (p as any)[k] = v;
} }
@ -99,6 +99,7 @@ export const Editor: FC<{ site_id: string; page_id: string; session: any }> = ({
} }
if (p.status === "init") { if (p.status === "init") {
(window as any).mok = ((window as any).mok || 0) + 1;
p.ui.loading = <Loading note="load-page" />; p.ui.loading = <Loading note="load-page" />;
p.ui.preload = <Loading note="preload-root" backdrop={false} />; p.ui.preload = <Loading note="preload-root" backdrop={false} />;
p.ui.notfound = ( p.ui.notfound = (
@ -109,6 +110,7 @@ export const Editor: FC<{ site_id: string; page_id: string; session: any }> = ({
PREVIEW ERROR PREVIEW ERROR
</div> </div>
); );
p.status = "loading";
initEditor(p, site_id); initEditor(p, site_id);
} }

View File

@ -32,119 +32,119 @@ export const w = window as unknown as {
}; };
export const initEditor = async (p: PG, site_id: string) => { export const initEditor = async (p: PG, site_id: string) => {
if (p.status === "init") {
p.status = "loading";
w.isEditor = true; w.isEditor = true;
if (typeof w.isLayout === "undefined") { if (typeof w.isLayout === "undefined") {
w.isLayout = false; w.isLayout = false;
} }
w.isMobile = p.mode === "mobile"; w.isMobile = p.mode === "mobile";
w.isDesktop = p.mode === "desktop"; w.isDesktop = p.mode === "desktop";
w.apiHeaders = {}; w.apiHeaders = {};
w.preload = () => {}; w.preload = () => {};
w.navigateOverride = (_href) => { w.navigateOverride = (_href) => {
if (_href.startsWith("/ed")) return _href; if (_href.startsWith("/ed")) return _href;
return ""; return "";
}; };
p.item.active = localStorage.getItem("prasi-item-active-id") || ""; p.item.active = localStorage.getItem("prasi-item-active-id") || "";
p.item.activeOriginalId = p.item.activeOriginalId = localStorage.getItem("prasi-item-active-oid") || "";
localStorage.getItem("prasi-item-active-oid") || ""; const comp: any = {
const comp: any = { id: localStorage.getItem("prasi-comp-active-id"),
id: localStorage.getItem("prasi-comp-active-id"), instance_id: localStorage.getItem("prasi-comp-instance-id"),
instance_id: localStorage.getItem("prasi-comp-instance-id"), last: localStorage.getItem("prasi-comp-active-last"),
last: localStorage.getItem("prasi-comp-active-last"), props: localStorage.getItem("prasi-comp-active-props"),
props: localStorage.getItem("prasi-comp-active-props"), };
}; if (comp.last) {
if (comp.last) { comp.last = JSON.parse(comp.last);
comp.last = JSON.parse(comp.last); }
if (comp.props) {
comp.props = JSON.parse(comp.props);
}
if (comp.id) {
p.comp = comp;
}
let site = null as any;
try {
site = JSON.parse(localStorage.getItem(`prasi-site-${site_id}`) || "");
} catch (e) {}
const querySite = async () => {
const site = await defaultLoader.site(p as any, { id: site_id });
localStorage.setItem(`prasi-site-${site_id}`, JSON.stringify(site));
return site;
};
const processSite = async (site: LSite) => {
if (!w.exports) {
w.exports = {};
} }
if (comp.props) { if (site.cgroup_ids) {
comp.props = JSON.parse(comp.props); for (const id of site.cgroup_ids) {
} await importModule(`${serverurl}/npm/site/${id}/site.js`);
if (comp.id) { }
p.comp = comp;
} }
let site = null as any; await importModule(`${serverurl}/npm/site/${site.id}/site.js`);
try { p.lsite = site;
site = JSON.parse(localStorage.getItem(`prasi-site-${site_id}`) || ""); p.site.id = site.id;
} catch (e) {} p.site.js = site.js || "";
p.site.js_compiled = site.js_compiled || "";
p.site.name = site.name;
p.site.domain = site.domain;
p.site.responsive = site.responsive as any;
p.site.layout = site.layout;
p.site.layout_id = site.layout_id;
const querySite = async () => { await validateLayout(p);
const site = await defaultLoader.site(p as any, { id: site_id });
localStorage.setItem(`prasi-site-${site_id}`, JSON.stringify(site)); w.externalAPI = {
return site; mode: (localStorage.getItem(`prasi-ext-api-mode-${p.site.id}`) ||
"prod") as any,
devUrl: localStorage.getItem(`prasi-ext-dev-url-${p.site.id}`) || "",
prodUrl: localStorage.getItem(`prasi-ext-prod-url-${p.site.id}`) || "",
}; };
const processSite = async (site: LSite) => {
if (!w.exports) {
w.exports = {};
}
if (site.cgroup_ids) {
for (const id of site.cgroup_ids) {
await importModule(`${serverurl}/npm/site/${id}/site.js`);
}
}
await importModule(`${serverurl}/npm/site/${site.id}/site.js`); p.site.api_url = await initApi(site.config);
p.lsite = site;
p.site.id = site.id;
p.site.js = site.js || "";
p.site.js_compiled = site.js_compiled || "";
p.site.name = site.name;
p.site.domain = site.domain;
p.site.responsive = site.responsive as any;
p.site.layout = site.layout;
p.site.layout_id = site.layout_id;
await validateLayout(p); if (w.externalAPI.prodUrl !== p.site.api_url) {
w.externalAPI.prodUrl = p.site.api_url;
localStorage.setItem(`prasi-ext-prod-url-${p.site.id}`, p.site.api_url);
}
if (w.externalAPI.mode === "dev" && w.externalAPI.devUrl) {
p.site.api_url = w.externalAPI.devUrl;
await reloadDBAPI(w.externalAPI.devUrl);
}
w.externalAPI = { w.apiurl = p.site.api_url;
mode: (localStorage.getItem(`prasi-ext-api-mode-${p.site.id}`) || api.site_dts(p.site.id).then((e: any) => {
"prod") as any, p.site_dts = e || "";
devUrl: localStorage.getItem(`prasi-ext-dev-url-${p.site.id}`) || "", p.render();
prodUrl: localStorage.getItem(`prasi-ext-prod-url-${p.site.id}`) || "", });
}; const configLocal: any = get(site, "config.prasi");
if (configLocal) {
p.site.api_prasi.db = configLocal.dburl ? configLocal.dburl : "";
p.site.api_prasi.port = configLocal.port ? configLocal.port : "";
}
execSiteJS(p);
};
p.site.api_url = await initApi(site.config); if (!site || (site && !site.id)) {
const site = await querySite();
if (w.externalAPI.prodUrl !== p.site.api_url) { if (site) {
w.externalAPI.prodUrl = p.site.api_url;
localStorage.setItem(`prasi-ext-prod-url-${p.site.id}`, p.site.api_url);
}
if (w.externalAPI.mode === "dev" && w.externalAPI.devUrl) {
p.site.api_url = w.externalAPI.devUrl;
await reloadDBAPI(w.externalAPI.devUrl);
}
w.apiurl = p.site.api_url;
api.site_dts(p.site.id).then((e) => {
p.site_dts = e || "";
p.render();
});
const configLocal: any = get(site, "config.prasi");
if (configLocal) {
p.site.api_prasi.db = configLocal.dburl ? configLocal.dburl : "";
p.site.api_prasi.port = configLocal.port ? configLocal.port : "";
}
execSiteJS(p);
};
if (!site || (site && !site.id)) {
const site = await querySite();
await processSite(site); await processSite(site);
} else {
await processSite(site);
querySite();
} }
} else {
await processSite(site);
querySite();
}
p.status = "ready"; p.status = "ready";
p.render(); p.render();
if (!jscript.build) { if (!jscript.build) {
jscript.init(); jscript.init();
}
} }
}; };

View File

@ -18,6 +18,10 @@ export const defaultLoader: Loader = {
}, },
})) as unknown as LSite; })) as unknown as LSite;
if (!site) {
return null;
}
const cgroups = await db.site_use_comp.findMany({ const cgroups = await db.site_use_comp.findMany({
where: { id_site: site.id }, where: { id_site: site.id },
select: { use_id_site: true }, select: { use_id_site: true },

View File

@ -83,9 +83,8 @@ export const liveWS = async (p: PG) => {
console.clear(); console.clear();
console.log( console.log(
`🔥 Page updated: ${ `🔥 Page updated: ${p.page
p.page?.url ?.url} ${new Date().toLocaleString()}`
} ${new Date().toLocaleString()}`
); );
} }
}) })
@ -179,49 +178,6 @@ export const liveWS = async (p: PG) => {
} }
} }
break; break;
case "sitejs_reload":
if (msg.js) {
p.site.js = msg.js;
const exec = (fn: string, scopes: any) => {
if (p) {
if (!p.script.api) p.script.api = createAPI(p.site.api_url);
if (!p.script.db) p.script.db = createDB(p.site.api_url);
scopes["db"] = p.script.db;
scopes["api"] = p.script.api;
const f = new Function(...Object.keys(scopes), fn);
const res = f(...Object.values(scopes));
return res;
}
return null;
};
const w = window as any;
const scope = {
types: {},
exports: w.exports,
load: importModule,
render: p.render,
module: {
exports: {} as any,
},
};
p.status = "init";
console.log(
`🔥 Site JS Reloaded: ${new Date().toLocaleString()}`
);
exec(p.site.js, scope);
if (scope.module.exports) {
for (const [k, v] of Object.entries(scope.module.exports)) {
w.exports[k] = v;
}
}
p.render();
}
break;
case "undo": case "undo":
case "redo": case "redo":
case "new_comp": case "new_comp":

View File

@ -3,6 +3,7 @@ import { dir } from "./dir";
import { g } from "./global"; import { g } from "./global";
import { spawn } from "bun"; import { spawn } from "bun";
const decoder = new TextDecoder();
export const parcelBuild = async () => { export const parcelBuild = async () => {
await dirAsync("app/static"); await dirAsync("app/static");
const args = [ const args = [
@ -20,53 +21,60 @@ export const parcelBuild = async () => {
await removeAsync(dir.path("app/web/.parcel-cache")); await removeAsync(dir.path("app/web/.parcel-cache"));
const parcel = spawn({ const parcel = spawn({
cmd: args, cmd: args,
cwd: dir.path("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({ await new Promise<void>((resolve) => {
cmd: args, const parcel = spawn({
cwd: dir.path("app/web"), cmd: args,
stdio: ["ignore", "pipe", "pipe"], cwd: dir.path("app/web"),
}); stdio: ["ignore", "pipe", "pipe"],
});
g.parcel = parcel; g.parcel = parcel;
let output = true; let output = true;
(async () => { let decoded = false;
if (parcel.stdout) { (async () => {
for await (const chunk of parcel.stdout) { if (parcel.stdout) {
if (output) process.stdout.write(chunk); for await (const chunk of parcel.stdout) {
if (!decoded && decoder.decode(chunk).includes("✨")) {
resolve();
decoded = true;
}
if (output) process.stdout.write(chunk);
}
} }
} })();
})();
(async () => { (async () => {
if (parcel.stderr) { if (parcel.stderr) {
for await (const chunk of parcel.stderr) { for await (const chunk of parcel.stderr) {
if (output) process.stderr.write(chunk); if (output) process.stderr.write(chunk);
}
} }
} })();
})();
const cleanup = async () => { const cleanup = async () => {
output = false; output = false;
}; };
process.on("SIGINT", async () => { process.on("SIGINT", async () => {
await cleanup(); await cleanup();
process.exit(); process.exit();
}); });
process.on("SIGTERM", async () => { process.on("SIGTERM", async () => {
await cleanup(); await cleanup();
process.exit(); process.exit();
}); });
process.on("beforeExit", async () => { process.on("beforeExit", async () => {
await cleanup(); await cleanup();
});
}); });
} }
}; };

View File

@ -39,7 +39,6 @@ export const useGlobal = <T extends object>(
if (!_id) { if (!_id) {
_id = "GLOBAL_DEFAULT"; _id = "GLOBAL_DEFAULT";
} }
const ctx = useContext(GlobalContext); const ctx = useContext(GlobalContext);
const { global, render } = ctx; const { global, render } = ctx;