This commit is contained in:
Rizky 2023-10-20 19:18:21 +07:00
parent b5273222ba
commit da2cb75001
20 changed files with 142 additions and 40 deletions

View File

@ -1,14 +1,21 @@
import { component, site, page } from "dbgen"; import { component, site, page } from "dbgen";
import { ESite } from "../../../web/src/render/ed/logic/ed-global";
/*
WARNING:
CHANGING FUNCTION NAME / OBJECT STRUCTURE
WILL *BREAK* OFFLINE STORAGE --
ONLY ADDITION IS ALLOWED
*/
export type SAction = typeof SyncActions;
export const SyncActions = { export const SyncActions = {
site: { site: {
list: () => list: async () =>
({}) as Promise< ({}) as Record<string, { id: string; name: string; domain: string }>,
Record<string, { id: string; name: string; domain: string }> group: async () => ({}) as Record<string, string[]>,
>, load: async (id: string) => ({}) as ESite | undefined,
group: () => ({}) as Promise<Record<string, string[]>>,
load: (id: string) => ({}) as Promise<site>,
}, },
comp: { comp: {
list: () => ({}) as Record<string, Exclude<component, "content_tree">>, list: () => ({}) as Record<string, Exclude<component, "content_tree">>,

View File

@ -1 +1,2 @@
export * from "./site_load"; export * from "./site_load";
export * from "./site_group";

View File

@ -0,0 +1,6 @@
import { ActionCtx } from "../type";
export const site_group = async function (this: ActionCtx) {
console.log(this.user);
return "gruop";
};

View File

@ -1,3 +1,23 @@
export const site_load = async () => { import { validate } from "uuid";
return "moka"; import { ActionCtx } from "../type";
import { ESite } from "../../../../web/src/render/ed/logic/ed-global";
import { SAction } from "../actions";
export const site_load: SAction["site"]["load"] = async function (
this: ActionCtx,
id: string
) {
if (validate(id)) {
const site = await db.site.findFirst({ where: { id } });
if (site) {
return {
id: site.id,
config: site.config as ESite["config"],
domain: site.domain,
js: site.js || "",
js_compiled: site.js_compiled || "",
name: site.name,
};
}
}
}; };

View File

@ -1,7 +1,7 @@
import { user } from "../user"; import { user } from "../user";
export const loadUserConf = async (user_id: string) => { export const loadDefaultSite = async (user_id: string) => {
const conf = user.conf.getOrCreate(user_id); const conf = user.conf.get(user_id);
if (conf) { if (conf) {
if (!conf.site_id) { if (!conf.site_id) {
const site = await db.site.findFirst({ const site = await db.site.findFirst({

View File

@ -3,16 +3,18 @@ import { ServerWebSocket, WebSocketHandler } from "bun";
import { Packr } from "msgpackr"; import { Packr } from "msgpackr";
import { WSData } from "../../../../pkgs/core/server/create"; import { WSData } from "../../../../pkgs/core/server/create";
import { ClientEvent } from "../../../web/src/utils/sync/client"; import { ClientEvent } from "../../../web/src/utils/sync/client";
import { loadUserConf } from "./editor/load"; import { loadDefaultSite } from "./editor/load";
import { SyncType } from "./type"; import { ActionCtx, SyncType } from "./type";
import { SyncActionPaths } from "./actions-def"; import { SyncActionPaths } from "./actions-def";
import * as actions from "./actions/index"; import * as actions from "./actions/index";
import { UserConf, user } from "./user";
const packr = new Packr({ structuredClone: true }); const packr = new Packr({ structuredClone: true });
const conns = new Map< const conns = new Map<
string, string,
{ {
user_id: string; user_id: string;
conf?: UserConf & { toJSON: () => UserConf };
ws: ServerWebSocket<WSData>; ws: ServerWebSocket<WSData>;
msg: { msg: {
pending: Record<string, Promise<any>>; pending: Record<string, Promise<any>>;
@ -51,18 +53,35 @@ export const syncHandler: WebSocketHandler<WSData> = {
if (msg.type === SyncType.UserID) { if (msg.type === SyncType.UserID) {
const { user_id } = msg; const { user_id } = msg;
conn.user_id = user_id; conn.user_id = user_id;
const conf = await loadUserConf(user_id);
const conf = user.conf.getOrCreate(user_id);
if (!conf.site_id) {
await loadDefaultSite(user_id);
}
conn.conf = new Proxy(conf, {
get(_, p) {
const conf = user.conf.get(user_id);
if (p === "toJSON") return () => conf;
if (conf) return conf[p as keyof typeof conf];
},
set(_, p, newValue) {
user.conf.set(user_id, p as keyof UserConf, newValue);
return true;
},
}) as UserConf & { toJSON: () => UserConf };
send(ws, { send(ws, {
type: SyncType.Event, type: SyncType.Event,
event: "editor_start" as ClientEvent, event: "editor_start" as ClientEvent,
data: conf, data: conn.conf.toJSON(),
}); });
} }
if (msg.type === SyncType.Action) { if (msg.type === SyncType.Action) {
const code = msg.code as keyof typeof SyncActionPaths; const code = msg.code as keyof typeof SyncActionPaths;
const actionName = SyncActionPaths[code].replace(/\./gi, "_"); const actionName = SyncActionPaths[code].replace(/\./gi, "_");
if (actionName) { if (actionName) {
const action = (actions as any)[actionName]; const action = (actions as any)[actionName].bind({
user: { id: conn.user_id, conf: conn.conf },
} as ActionCtx);
ws.sendBinary( ws.sendBinary(
packr.pack({ packr.pack({

View File

@ -1,7 +1,12 @@
import { UserConf } from "./user";
export enum SyncType { export enum SyncType {
ClientID, ClientID,
UserID, UserID,
Event, Event,
Action, Action,
ActionResult, ActionResult,
} }
export type ActionCtx = {
user: { id: string; conf: UserConf & { toJSON: () => UserConf } };
};

View File

@ -6,7 +6,7 @@ const defaultConf = {
site_id: "", site_id: "",
page_id: "", page_id: "",
}; };
type UserConf = typeof defaultConf; export type UserConf = typeof defaultConf;
const db = open<UserConf, string>({ const db = open<UserConf, string>({
name: "user-conf", name: "user-conf",
@ -16,12 +16,12 @@ const db = open<UserConf, string>({
export const user = { export const user = {
conf: { conf: {
getOrCreate(user_id: string) { getOrCreate(user_id: string) {
const res = db.get(user_id); let res = db.get(user_id);
if (!res) { if (!res) {
db.put(user_id, structuredClone(defaultConf)); db.put(user_id, structuredClone(defaultConf));
return db.get(user_id); res = db.get(user_id);
} }
return res; return res as UserConf;
}, },
get(user_id: string) { get(user_id: string) {
return db.get(user_id); return db.get(user_id);

View File

@ -1,5 +1,5 @@
import { page, useGlobal } from "web-utils"; import { page, useGlobal } from "web-utils";
import { EDGlobal } from "../../render/ed/logic/global"; import { EDGlobal } from "../../render/ed/logic/ed-global";
import { clientStartSync } from "../../utils/sync/client"; import { clientStartSync } from "../../utils/sync/client";
import { Loading } from "../../utils/ui/loading"; import { Loading } from "../../utils/ui/loading";
import { Ed } from "../../render/ed/ed"; import { Ed } from "../../render/ed/ed";

View File

@ -10,9 +10,23 @@ const start = async () => {
let react = { let react = {
root: null as null | ReactRoot, root: null as null | ReactRoot,
}; };
if (!["localhost", "127.0.0.1"].includes(location.hostname)) { if (true || !["localhost", "127.0.0.1"].includes(location.hostname)) {
const sw = await registerServiceWorker(); const sw = await registerServiceWorker();
const cacheCurrentPage = () => {
const swc = navigator.serviceWorker.controller;
if (swc) {
[location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach((url) => {
swc.postMessage({
type: "add-cache",
url: url,
});
});
}
};
cacheCurrentPage();
navigator.serviceWorker.addEventListener("message", (e) => { navigator.serviceWorker.addEventListener("message", (e) => {
cacheCurrentPage();
if (react.root) { if (react.root) {
if (e.data.type === "offline") { if (e.data.type === "offline") {
w.offline = true; w.offline = true;
@ -100,7 +114,8 @@ const start = async () => {
className="bg-green-600 text-white px-4 py-2 rounded-full text-sm" className="bg-green-600 text-white px-4 py-2 rounded-full text-sm"
onClick={click} onClick={click}
> >
App Updated, Ready to use offline App Updated{" "}
<span className="opacity-50">{e.data.version}</span>
</div> </div>
</div> </div>
</> </>

View File

@ -1,12 +1,22 @@
import { useGlobal } from "web-utils"; import { useGlobal } from "web-utils";
import { Loading } from "../../utils/ui/loading"; import { Loading } from "../../utils/ui/loading";
import { EDGlobal } from "./logic/global"; import { EDGlobal } from "./logic/ed-global";
import { edRoute } from "./logic/route"; import { edRoute } from "./logic/ed-route";
export const Ed = () => { export const Ed = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
edRoute(p); edRoute(p);
console.log(p.status);
return <Loading />; if (p.status === "loading") {
return <Loading />;
}
if (p.status === "site-not-found" || p.status === "page-not-found") {
return (
<div className="flex fixed inset-0 items-center justify-center">
{p.status === "site-not-found" ? "Site not found" : "Page not found"}
</div>
);
}
return <div>asfa</div>;
}; };

View File

@ -14,6 +14,12 @@ const EmptyPage = {
}; };
export const EDGlobal = { export const EDGlobal = {
status: "init" as
| "init"
| "loading"
| "site-not-found"
| "page-not-found"
| "ready",
sync: null as unknown as Awaited<ReturnType<typeof clientStartSync>>, sync: null as unknown as Awaited<ReturnType<typeof clientStartSync>>,
site: EmptySite, site: EmptySite,
page: EmptyPage, page: EmptyPage,

View File

@ -0,0 +1,21 @@
import { PG } from "./ed-global";
export const edRoute = async (p: PG) => {
if (p.status === "init") {
if (!p.site.domain && !p.site.name) {
p.status = "loading";
const site = await p.sync.site.load(p.site.id);
if (!site) {
p.status = "site-not-found";
p.render();
return;
}
p.site = site;
}
if (p.site) {
console.log(p.site);
}
}
};

View File

@ -1,8 +0,0 @@
import { PG } from "./global";
export const edRoute = async (p: PG) => {
if (!p.site.domain && !p.site.name) {
const res = await p.sync.site.load(p.site.id);
console.log(res);
}
};

View File

@ -8,7 +8,7 @@ import { SyncActionDefinition } from "../../../../srv/ws/sync/actions-def";
import { initIDB } from "./idb"; import { initIDB } from "./idb";
import { SyncType } from "../../../../srv/ws/sync/type"; import { SyncType } from "../../../../srv/ws/sync/type";
import { w } from "../types/general"; import { w } from "../types/general";
import { ESite } from "../../render/ed/logic/global"; import { ESite } from "../../render/ed/logic/ed-global";
const packr = new Packr({ structuredClone: true }); const packr = new Packr({ structuredClone: true });
const conf = { const conf = {
ws: null as null | WebSocket, ws: null as null | WebSocket,

View File

@ -3,16 +3,17 @@
"module": "src/index.ts", "module": "src/index.ts",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bun clean && bun run --silent --watch ./pkgs/core/index.ts dev", "dev": "bun run --silent --watch ./pkgs/core/index.ts dev",
"clean": "rm -rf app/static && rm -rf app/web/.parcel-cache", "clean": "rm -rf app/static && rm -rf app/web/.parcel-cache",
"build": "bun run --silent ./pkgs/core/build.ts", "build": "bun run --silent ./pkgs/core/build.ts",
"db-pull": "bun run ./pkgs/core/db-pull.ts", "db-pull": "bun run ./pkgs/core/db-pull.ts",
"parcel": "bun clean && bun run ./pkgs/core/parcel.ts",
"prod": "bun run --silent ./pkgs/core/index.ts", "prod": "bun run --silent ./pkgs/core/index.ts",
"local-prod": "bun run build && bun run db-pull && bun run ./pkgs/core/index.ts", "local-prod": "bun run build && bun run db-pull && bun run ./pkgs/core/index.ts",
"pull": "cd app/db && bun prisma db pull && bun prisma generate", "pull": "cd app/db && bun prisma db pull && bun prisma generate",
"pkgs-upgrade": "bun run --silent ./pkgs/core/upgrade.ts" "pkgs-upgrade": "bun run --silent ./pkgs/core/upgrade.ts"
}, },
"workspaces": [ "workspaces": [
"app/*", "app/*",
"pkgs/*" "pkgs/*"
], ],

View File

@ -31,7 +31,7 @@ if (g.db) {
g.log.error(`[DB ERROR]\n${e.message}`); g.log.error(`[DB ERROR]\n${e.message}`);
}); });
} }
await syncActionDefinition(); await syncActionDefinition();
await parcelBuild(); await parcelBuild();
await generateAPIFrm(); await generateAPIFrm();

View File

@ -3,7 +3,6 @@ import { dirAsync } from "fs-jetpack";
import { dir } from "./dir"; import { dir } from "./dir";
import { g } from "./global"; import { g } from "./global";
const decoder = new TextDecoder(); const decoder = new TextDecoder();
export const parcelBuild = async () => { export const parcelBuild = async () => {
await dirAsync("app/static"); await dirAsync("app/static");

Binary file not shown.

Binary file not shown.