This commit is contained in:
Rizky 2023-10-20 14:08:31 +07:00
parent 2fddcf1a78
commit 7052dad65b
13 changed files with 102 additions and 147 deletions

View File

@ -8,6 +8,7 @@
"@paralleldrive/cuid2": "^2.2.2",
"@types/mime-types": "^2.1.2",
"esbuild": "^0.19.4",
"lmdb": "^2.8.5",
"mime-types": "^2.1.35",
"msgpackr": "^1.9.9",
"radix3": "^1.1.0"

View File

@ -1,5 +1,6 @@
import { component, site, page } from "dbgen";
export const SyncActions = {
site: {
all: () =>

View File

@ -0,0 +1,31 @@
import { user } from "../user";
export const syncEditorLoad = async (user_id: string) => {
const conf = user.conf.get(user_id);
if (conf) {
if (!conf.site_id) {
const site = await db.site.findFirst({
where: {
id_user: user_id,
is_deleted: false,
},
select: { id: true },
});
if (site) conf.site_id = site.id;
}
if (conf.site_id && !conf.page_id) {
const page = await db.page.findFirst({
select: { id: true },
where: {
id_site: conf.site_id,
is_deleted: false,
},
});
if (page) {
conf.page_id = page.id;
}
}
}
};

View File

@ -2,6 +2,7 @@ import { createId } from "@paralleldrive/cuid2";
import { ServerWebSocket, WebSocketHandler } from "bun";
import { Packr } from "msgpackr";
import { WSData } from "../../../../pkgs/core/server/create";
import { SyncType } from "./type";
const packr = new Packr({ structuredClone: true });
const conns = new Map<
@ -25,7 +26,7 @@ export const syncHandler: WebSocketHandler<WSData> = {
msg: { pending: {}, resolve: {} },
});
wconns.set(ws, client_id);
ws.sendBinary(packr.pack({ type: "client_id", client_id }));
ws.sendBinary(packr.pack({ type: SyncType.ClientID, client_id }));
},
close(ws, code, reason) {
const conn_id = wconns.get(ws);
@ -40,7 +41,7 @@ export const syncHandler: WebSocketHandler<WSData> = {
const conn = conns.get(conn_id);
if (conn) {
const msg = packr.unpack(Buffer.from(raw));
if (msg.type === "user_id") {
if (msg.type === SyncType.UserID) {
const { user_id } = msg;
conn.user_id = user_id;
}

5
app/srv/ws/sync/type.ts Normal file
View File

@ -0,0 +1,5 @@
export enum SyncType {
ClientID,
UserID,
Event
}

33
app/srv/ws/sync/user.ts Normal file
View File

@ -0,0 +1,33 @@
import { dir } from "dir";
import { open } from "lmdb";
import { g } from "utils/global";
const defaultConf = {
site_id: "",
page_id: "",
};
type UserConf = typeof defaultConf;
const db = open<UserConf, string>({
name: "user-conf",
path: dir.path(`${g.datadir}/lmdb/user-conf.lmdb`),
});
export const user = {
conf: {
get(user_id: string) {
return db.get(user_id);
},
set<T extends keyof UserConf>(user_id: string, key: T, value: UserConf[T]) {
let current = this.get(user_id);
if (!current) {
db.put(user_id, structuredClone(defaultConf));
current = this.get(user_id);
}
if (current) {
db.put(user_id, { ...current, [key]: value });
}
},
},
};

View File

@ -6,13 +6,17 @@ export default page({
url: "**",
component: ({}) => {
useEffect(() => {
if (localStorage.getItem("prasi-session")) {
navigate("/editor/");
if (location.pathname.startsWith("/ed/")) {
navigate("/ed/_/_");
} else {
navigate("/editor/_/_");
}
} else {
navigate("/login");
}
}, []);
return <Loading />;
},
});

View File

@ -1,130 +0,0 @@
import { FC, useEffect } from "react";
import { page, useGlobal, useLocal } from "web-utils";
import { EditorGlobal } from "../../render/editor/logic/global";
import { Loading } from "../../utils/ui/loading";
export default page({
url: "/editor/**",
component: ({}) => {
const p = useGlobal(EditorGlobal, "EDITOR");
const local = useLocal({
loading: true,
session: null as any,
notfound: false,
init: false,
});
const site_id = params.site_id === "_" ? "" : params.site_id;
const page_id = params.page_id === "_" ? "" : params.page_id;
useEffect(() => {
if (!local.init) {
(async () => {
let ses: any = null;
try {
ses = JSON.parse(localStorage.getItem("prasi-session") || "");
} catch (e) {}
await new Promise<void>(async (done) => {
try {
if (!!ses) {
done();
}
let e = await api.session();
if (!e) {
(window as any).redirectTo = location.pathname;
navigate("/login");
localStorage.removeItem("prasi-session");
} else {
localStorage.setItem("prasi-session", JSON.stringify(e));
}
if (!ses) {
ses = e;
done();
}
} catch (e) {
console.error(e);
}
});
if (ses) {
local.session = ses;
if (!site_id) {
const res = await db.site.findFirst({
where: {
is_deleted: false,
org: {
org_user: {
some: { id_user: ses.data.user.id },
},
},
},
select: {
id: true,
},
});
if (res) {
const page = await db.page.findFirst({
where: {
id_site: res.id,
is_deleted: false,
},
select: {
id: true,
},
});
if (page) {
local.loading = false;
local.render();
navigate(`/editor/${res.id}/${page.id}`);
return;
}
} else {
local.loading = false;
local.render();
return;
}
} else if (!page_id) {
let res = await db.page.findFirst({
where: {
id_site: site_id,
is_deleted: false,
},
select: {
id: true,
},
});
if (!res) {
res = await db.page.create({
data: {
content_tree: {
childs: [],
id: "root",
type: "root",
},
name: "home",
url: "/",
id_site: site_id,
},
});
}
if (res) {
local.loading = false;
local.render();
navigate(`/editor/${site_id}/${res.id}`);
return;
}
}
}
navigate(`/editor/${site_id}/${page_id}`);
})();
}
}, [local.init]);
return <Loading note="base-page" />;
},
});

View File

@ -2,11 +2,12 @@ import { page, useGlobal } from "web-utils";
import { EditorGlobal } from "../../render/editor/logic/global";
import { Loading } from "../../utils/ui/loading";
import { clientStartSync } from "../../utils/sync/client";
import { EDGlobal } from "../../render/ed/logic/global";
export default page({
url: "/ned/:site_id/:page_id",
url: "/ed/:site_id/:page_id",
component: ({}) => {
const p = useGlobal(EditorGlobal, "EDITOR");
const p = useGlobal(EDGlobal, "EDITOR");
const session = JSON.parse(
localStorage.getItem("prasi-session") || "null"
@ -17,9 +18,10 @@ export default page({
}
if (!p.sync) {
// p.sync = clientStartSync({
// user_id: session.data.user.id,
// });
p.sync = clientStartSync({
user_id: session.data.user.id,
events: { editor_start() {} },
});
return <Loading />;
}

View File

@ -14,10 +14,6 @@ export const all = {
url: "**",
page: () => import("./page/all"),
};
export const ed = {
url: "/editor/**",
page: () => import("./page/ed"),
};
export const editor = {
url: "/editor/:site_id/:page_id",
page: () => import("./page/editor"),
@ -27,6 +23,6 @@ export const live = {
page: () => import("./page/live"),
};
export const ned = {
url: "/ned/:site_id/:page_id",
url: "/ed/:site_id/:page_id",
page: () => import("./page/ned"),
};

View File

@ -0,0 +1,5 @@
import { clientStartSync } from "../../../utils/sync/client";
export const EDGlobal = {
sync: null as unknown as ReturnType<typeof clientStartSync>,
};

View File

@ -6,6 +6,7 @@ import { stringify } from "safe-stable-stringify";
import { SyncActions } from "../../../../srv/ws/sync/actions";
import { SyncActionDefinition } from "../../../../srv/ws/sync/actions-def";
import { initIDB } from "./idb";
import { SyncType } from "../../../../srv/ws/sync/type";
const packr = new Packr({ structuredClone: true });
const conf = {
ws: null as null | WebSocket,
@ -21,7 +22,11 @@ type User = {
export const clientStartSync = async (arg: {
user_id: string;
events: {
site_open: (arg: { site_id: string; user: User }) => void;
editor_start: (arg: {
user_id: string;
site_id?: string;
page_id?: string;
}) => void;
};
}) => {
const { user_id, events } = arg;
@ -69,15 +74,16 @@ const connect = (user_id: string) => {
const ws = new WebSocket(url.toString());
conf.ws = ws;
ws.onopen = () => {
ws.send(packr.pack({ type: "user_id", user_id }));
ws.send(packr.pack({ type: SyncType.UserID, user_id }));
};
ws.onmessage = async (e) => {
const raw = e.data as Blob;
const msg = packr.unpack(Buffer.from(await raw.arrayBuffer()));
if (msg.type === "client_id") {
if (msg.type === SyncType.ClientID) {
conf.client_id = msg.client_id;
resolve(ws);
} else if (msg.type === "event") {
}
};
}

BIN
bun.lockb

Binary file not shown.