fix lib
This commit is contained in:
parent
fef9da2ab0
commit
46d5d1d777
|
|
@ -1,4 +1,4 @@
|
||||||
import { ServerContext, SessionContext } from "./server-route";
|
import type { ServerContext, SessionContext } from "lib/session/type";
|
||||||
|
|
||||||
export const sessionContext = <T>(sf: any) => {
|
export const sessionContext = <T>(sf: any) => {
|
||||||
return sf as unknown as SessionContext<T>;
|
return sf as unknown as SessionContext<T>;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { _post } from "lib/utils/post";
|
import { _post } from "lib/utils/post";
|
||||||
import { addRoute, createRouter, findRoute } from "rou3";
|
import { addRoute, createRouter, findRoute } from "rou3";
|
||||||
import { ServerContext, SessionContext } from "./session/type";
|
import { ServerContext, SessionContext } from "../session/type";
|
||||||
|
|
||||||
type RouteFn = (...arg: any[]) => Promise<any>;
|
type RouteFn = (...arg: any[]) => Promise<any>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
import { newClientRouter } from "../server-route";
|
|
||||||
import { sessionRouter } from "./router/session-router";
|
|
||||||
import { sessionClientStore } from "./store/client-store";
|
|
||||||
import { ClientSession } from "./type";
|
|
||||||
|
|
||||||
export const newClientSession = <T>(arg?: {
|
|
||||||
on: Partial<{
|
|
||||||
messageReceived: (session: ClientSession<T>) => Promise<void>;
|
|
||||||
afterLogin: (session: ClientSession<T>) => Promise<void>;
|
|
||||||
afterLogout: (session: ClientSession<T>) => Promise<void>;
|
|
||||||
afterRecheck: (session: ClientSession<T>) => Promise<void>;
|
|
||||||
}>;
|
|
||||||
}) => {
|
|
||||||
const store = sessionClientStore<T>();
|
|
||||||
const client = newClientRouter(sessionRouter);
|
|
||||||
|
|
||||||
const session: ClientSession<T> = {
|
|
||||||
status: "checking",
|
|
||||||
current: null,
|
|
||||||
async connect() {
|
|
||||||
const url = new URL(location.href);
|
|
||||||
url.protocol = "wss:";
|
|
||||||
const ws = new WebSocket(url);
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
|
||||||
ws.send("ok");
|
|
||||||
};
|
|
||||||
ws.onmessage = (m) => {
|
|
||||||
console.log(m);
|
|
||||||
};
|
|
||||||
// const current = await store.load();
|
|
||||||
// if (!current) {
|
|
||||||
// this.status = "guest";
|
|
||||||
// } else {
|
|
||||||
// this.status = await client.check(current.uid, current.sid);
|
|
||||||
// }
|
|
||||||
return { status: this.status };
|
|
||||||
},
|
|
||||||
async login(arg: {
|
|
||||||
method: "user-pass";
|
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
}) {},
|
|
||||||
async logout() {},
|
|
||||||
};
|
|
||||||
|
|
||||||
session.connect();
|
|
||||||
return session;
|
|
||||||
};
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
/// <reference types="bun-types" />
|
|
||||||
|
|
||||||
import { useServerRouter } from "../server-route";
|
|
||||||
import { sessionRouter } from "./router/session-router";
|
|
||||||
import { newSessionStore } from "./store/session-store";
|
|
||||||
import { ServerContext } from "./type";
|
|
||||||
|
|
||||||
type SessionServerHandler = {
|
|
||||||
cleanup: () => Promise<void>;
|
|
||||||
handle: (arg: ServerContext) => Promise<Response>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createSessionServer = <T>(arg: {
|
|
||||||
encrypt?: boolean;
|
|
||||||
server_router?: ReturnType<typeof useServerRouter>;
|
|
||||||
site_id?: string;
|
|
||||||
}): SessionServerHandler => {
|
|
||||||
const session_store = newSessionStore<T>(arg.site_id);
|
|
||||||
|
|
||||||
const session_router = useServerRouter(sessionRouter);
|
|
||||||
|
|
||||||
const server_handler: SessionServerHandler = {
|
|
||||||
async cleanup() {},
|
|
||||||
async handle(server_arg) {
|
|
||||||
const { req, handle, url } = server_arg;
|
|
||||||
|
|
||||||
const route_arg = {
|
|
||||||
...server_arg,
|
|
||||||
session: {
|
|
||||||
...session_store,
|
|
||||||
current: undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (url.pathname.startsWith("/_session/")) {
|
|
||||||
const res = await session_router.handle(route_arg);
|
|
||||||
if (res) return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.server_router) {
|
|
||||||
const res = await arg.server_router.handle(route_arg);
|
|
||||||
if (res) return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle(req);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return server_handler;
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
import { ConsoleLogWriter } from "drizzle-orm";
|
||||||
|
import { newClientRouter } from "../server/server-route";
|
||||||
|
import { sessionRouter } from "./router/session-router";
|
||||||
|
import { sessionClientStore } from "./store/client-store";
|
||||||
|
import { ClientSession, SessionAuth, SessionData } from "./type";
|
||||||
|
|
||||||
|
export const newClientSession = <T>(arg?: {
|
||||||
|
on?: Partial<{
|
||||||
|
messageReceived: (session: ClientSession<T>) => Promise<void>;
|
||||||
|
afterLogin: (session: ClientSession<T>) => Promise<void>;
|
||||||
|
afterLogout: (session: ClientSession<T>) => Promise<void>;
|
||||||
|
afterInit: (session: ClientSession<T>) => Promise<void>;
|
||||||
|
}>;
|
||||||
|
}) => {
|
||||||
|
const store = sessionClientStore<T>();
|
||||||
|
const client = newClientRouter(sessionRouter);
|
||||||
|
|
||||||
|
const login_promise = { resolve: null as any, reject: null as any };
|
||||||
|
const logout_promise = { resolve: null as any, reject: null as any };
|
||||||
|
|
||||||
|
const session: ClientSession<T> = {
|
||||||
|
status: "checking",
|
||||||
|
wsid: "",
|
||||||
|
current: null,
|
||||||
|
connected: false,
|
||||||
|
get connectURL() {
|
||||||
|
const url = new URL(location.href);
|
||||||
|
url.protocol = "wss:";
|
||||||
|
url.hash = "";
|
||||||
|
return url;
|
||||||
|
},
|
||||||
|
connect(auth?: SessionAuth) {
|
||||||
|
return new Promise<void>(async (done, reject) => {
|
||||||
|
const current = this.current;
|
||||||
|
if (current || auth) {
|
||||||
|
if (this.ws) {
|
||||||
|
await wsReady(this.ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ws = new WebSocket(this.connectURL);
|
||||||
|
this.ws = ws;
|
||||||
|
ws.onclose = () => {
|
||||||
|
session.connected = false;
|
||||||
|
console.log(this.status);
|
||||||
|
if (this.status === "logout") {
|
||||||
|
store.clear();
|
||||||
|
this.current = null;
|
||||||
|
if (arg?.on?.afterLogout) {
|
||||||
|
arg.on.afterLogout(session);
|
||||||
|
}
|
||||||
|
logout_promise.resolve();
|
||||||
|
logout_promise.resolve = null;
|
||||||
|
logout_promise.reject = null;
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.warn("Reconnecting Session WS...");
|
||||||
|
this.connect();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onopen = () => {
|
||||||
|
if (session.current) {
|
||||||
|
ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
uid: session.current.uid,
|
||||||
|
sid: session.current.sid,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (auth) {
|
||||||
|
ws.send(JSON.stringify(auth));
|
||||||
|
} else {
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onmessage = async (m) => {
|
||||||
|
if (!session.connected) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(m.data) as
|
||||||
|
| {
|
||||||
|
status: "ok";
|
||||||
|
wsid: string;
|
||||||
|
session: SessionData<T>;
|
||||||
|
}
|
||||||
|
| { status: "failed" };
|
||||||
|
|
||||||
|
if (parsed.status === "ok") {
|
||||||
|
session.wsid = parsed.wsid;
|
||||||
|
session.current = parsed.session;
|
||||||
|
if (login_promise.resolve) {
|
||||||
|
session.connected = true;
|
||||||
|
login_promise.resolve(session.current);
|
||||||
|
await store.save(session.current);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (login_promise.reject) login_promise.reject();
|
||||||
|
}
|
||||||
|
login_promise.resolve = null;
|
||||||
|
login_promise.reject = null;
|
||||||
|
if (arg?.on?.afterLogin) {
|
||||||
|
arg.on.afterLogin(session);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async init() {
|
||||||
|
const current = await store.load();
|
||||||
|
if (!current) {
|
||||||
|
this.status = "guest";
|
||||||
|
} else {
|
||||||
|
this.current = current;
|
||||||
|
this.status = await client.check(current.uid, current.sid);
|
||||||
|
if (this.status !== "active") {
|
||||||
|
await store.clear();
|
||||||
|
this.current = null;
|
||||||
|
} else {
|
||||||
|
await this.connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arg?.on?.afterInit) {
|
||||||
|
arg.on.afterInit(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: this.status };
|
||||||
|
},
|
||||||
|
login(auth: SessionAuth) {
|
||||||
|
return new Promise<SessionData<T>>(async (resolve, reject) => {
|
||||||
|
if (isEditor) {
|
||||||
|
resolve({} as any);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
login_promise.resolve = resolve;
|
||||||
|
login_promise.reject = reject;
|
||||||
|
if (this.status === "checking") {
|
||||||
|
await new Promise<void>((done) => {
|
||||||
|
const ival = setInterval(() => {
|
||||||
|
if (this.status !== "checking") {
|
||||||
|
clearInterval(ival);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.status === "guest") {
|
||||||
|
this.connect(auth);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
`\
|
||||||
|
Session login failed, current status is: ${this.status}.
|
||||||
|
Login is prevented, please logout first before re-login!`
|
||||||
|
);
|
||||||
|
if (this.current) {
|
||||||
|
resolve(this.current);
|
||||||
|
} else {
|
||||||
|
if (auth) {
|
||||||
|
this.connect(auth);
|
||||||
|
} else {
|
||||||
|
reject("Current session not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
if (isEditor) {
|
||||||
|
resolve({} as any);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logout_promise.resolve = resolve;
|
||||||
|
logout_promise.reject = reject;
|
||||||
|
if (this.status === "active" && this.ws) {
|
||||||
|
await wsReady(this.ws);
|
||||||
|
this.status = "logout";
|
||||||
|
|
||||||
|
this.ws.send(JSON.stringify({ action: "logout" }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isEditor) {
|
||||||
|
session.init();
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
|
||||||
|
const wsReady = async (ws: WebSocket) => {
|
||||||
|
if (ws) {
|
||||||
|
if (ws.readyState === ws.OPEN) return;
|
||||||
|
else {
|
||||||
|
ws.close();
|
||||||
|
await new Promise<void>((done) => {
|
||||||
|
const ival = setInterval(() => {
|
||||||
|
if (ws.readyState === ws.CLOSED) {
|
||||||
|
clearInterval(ival);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -4,8 +4,15 @@ import { ClientSessionStatus } from "../type";
|
||||||
|
|
||||||
export default async function (this: any, uid: string, sid: string) {
|
export default async function (this: any, uid: string, sid: string) {
|
||||||
const ctx = sessionContext<EsensiSession>(this);
|
const ctx = sessionContext<EsensiSession>(this);
|
||||||
|
|
||||||
let result = "invalid" as ClientSessionStatus;
|
let result = "invalid" as ClientSessionStatus;
|
||||||
|
const session = ctx.session.findFirst({ uid, sid });
|
||||||
|
if (session) {
|
||||||
|
if (!session.expired_at || session.expired_at > Date.now()) {
|
||||||
|
result = "active";
|
||||||
|
} else {
|
||||||
|
result = "expired";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
|
import { ServerWebSocket } from "bun";
|
||||||
|
import { useServerRouter } from "../server/server-route";
|
||||||
|
import { sessionRouter } from "./router/session-router";
|
||||||
|
import { newSessionStore } from "./store/session-store";
|
||||||
|
import {
|
||||||
|
ServerContext,
|
||||||
|
SessionAuth,
|
||||||
|
SessionStore,
|
||||||
|
SingleSession,
|
||||||
|
} from "./type";
|
||||||
|
import { createId } from "@paralleldrive/cuid2";
|
||||||
|
import { ConsoleLogWriter } from "drizzle-orm";
|
||||||
|
|
||||||
|
type WS = ServerWebSocket<{ url: string }>;
|
||||||
|
type SessionServerHandler = {
|
||||||
|
cleanup: () => Promise<void>;
|
||||||
|
handle: (arg: ServerContext) => Promise<Response>;
|
||||||
|
ws: {
|
||||||
|
sids: Record<string, WS[]>;
|
||||||
|
index: Record<string, WS>;
|
||||||
|
conns: Map<WS, { sid: string; uid: string; wsid: string }>;
|
||||||
|
init: () => PrasiServer["ws"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initSessionServer = <T>(
|
||||||
|
server: PrasiServer,
|
||||||
|
arg: {
|
||||||
|
encrypt?: boolean;
|
||||||
|
router?: ReturnType<typeof useServerRouter>;
|
||||||
|
login: (
|
||||||
|
session: SessionStore<T>,
|
||||||
|
arg: SessionAuth
|
||||||
|
) => Promise<SingleSession<T> | false>;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const session_store = newSessionStore<T>(server.site_id);
|
||||||
|
const session_router = useServerRouter(sessionRouter);
|
||||||
|
const server_handler: SessionServerHandler = {
|
||||||
|
async cleanup() {},
|
||||||
|
ws: {
|
||||||
|
sids: {},
|
||||||
|
index: {},
|
||||||
|
conns: new Map(),
|
||||||
|
init() {
|
||||||
|
return {
|
||||||
|
async message(ws, message) {
|
||||||
|
const sids = server_handler.ws.sids;
|
||||||
|
const conns = server_handler.ws.conns;
|
||||||
|
const index = server_handler.ws.index;
|
||||||
|
const conn = conns.get(ws);
|
||||||
|
if (conn) {
|
||||||
|
try {
|
||||||
|
if (typeof message === "string") {
|
||||||
|
const parsed = JSON.parse(message) as { action: "logout" };
|
||||||
|
|
||||||
|
if (parsed.action === "logout") {
|
||||||
|
const sid = `${conn.uid}-${conn.sid}`;
|
||||||
|
|
||||||
|
if (!sids[sid]) {
|
||||||
|
sids[sid] = [];
|
||||||
|
}
|
||||||
|
sids[sid].forEach((e) => {
|
||||||
|
conns.delete(e);
|
||||||
|
e.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
session_store.update(
|
||||||
|
{ sid: conn.sid, uid: conn.uid },
|
||||||
|
{
|
||||||
|
active: false,
|
||||||
|
wsid: [],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
if (typeof message === "string") {
|
||||||
|
const activateSession = (result: SingleSession<T>) => {
|
||||||
|
try {
|
||||||
|
const wsid = createId();
|
||||||
|
conns.set(ws, {
|
||||||
|
wsid,
|
||||||
|
uid: result.uid,
|
||||||
|
sid: result.sid,
|
||||||
|
});
|
||||||
|
|
||||||
|
index[wsid] = ws;
|
||||||
|
const sid = `${result.uid}-${result.sid}`;
|
||||||
|
|
||||||
|
if (!sids[sid]) {
|
||||||
|
sids[sid] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
sids[sid].push(ws);
|
||||||
|
|
||||||
|
const wsids = Object.values(sids[sid]).map(
|
||||||
|
(e) => server_handler.ws.conns.get(e)?.sid || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
session_store.update(
|
||||||
|
{ sid: result.sid, uid: result.uid },
|
||||||
|
{
|
||||||
|
wsid: wsids,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
status: "ok",
|
||||||
|
wsid,
|
||||||
|
session: result,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsed = JSON.parse(message) as
|
||||||
|
| {
|
||||||
|
method: undefined;
|
||||||
|
uid: string;
|
||||||
|
sid: string;
|
||||||
|
}
|
||||||
|
| SessionAuth;
|
||||||
|
|
||||||
|
if (parsed) {
|
||||||
|
if (parsed.method) {
|
||||||
|
if (parsed.method === "user-pass") {
|
||||||
|
const result = await arg.login(session_store, parsed);
|
||||||
|
if (result) {
|
||||||
|
activateSession(result);
|
||||||
|
} else {
|
||||||
|
ws.send(JSON.stringify({ status: "failed" }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const result = session_store.findFirst({
|
||||||
|
uid: parsed.uid,
|
||||||
|
sid: parsed.sid,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
if (
|
||||||
|
result.active &&
|
||||||
|
(!result.expired_at ||
|
||||||
|
(result.expired_at &&
|
||||||
|
result.expired_at > Date.now()))
|
||||||
|
) {
|
||||||
|
activateSession(result);
|
||||||
|
} else {
|
||||||
|
session_store.update(
|
||||||
|
{
|
||||||
|
uid: parsed.uid,
|
||||||
|
sid: parsed.sid,
|
||||||
|
},
|
||||||
|
{ active: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({ status: "expired" }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close(ws, code, reason) {
|
||||||
|
const result = server_handler.ws.conns.get(ws);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
const sid = `${result.uid}-${result.sid}`;
|
||||||
|
if (server_handler.ws.sids[sid]) {
|
||||||
|
server_handler.ws.sids[sid] = server_handler.ws.sids[
|
||||||
|
sid
|
||||||
|
].filter((e) => e !== ws);
|
||||||
|
|
||||||
|
session_store.update(
|
||||||
|
{ sid: result.sid },
|
||||||
|
{
|
||||||
|
wsid: Object.values(server_handler.ws.sids[sid]).map(
|
||||||
|
(e) => server_handler.ws.conns.get(e)?.sid || ""
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete server_handler.ws.index[result.wsid];
|
||||||
|
server_handler.ws.conns.delete(ws);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async handle(server_arg) {
|
||||||
|
const { req, handle, url } = server_arg;
|
||||||
|
|
||||||
|
const route_arg = {
|
||||||
|
...server_arg,
|
||||||
|
session: {
|
||||||
|
...session_store,
|
||||||
|
current: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (url.pathname.startsWith("/_session/")) {
|
||||||
|
const res = await session_router.handle(route_arg);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.router) {
|
||||||
|
const res = await arg.router.handle(route_arg);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle(req);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
server.ws = server_handler.ws.init();
|
||||||
|
server.session = server_handler;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -12,4 +12,14 @@ export const sessionClientStore = <T>() => ({
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
async save(arg: SessionData<T>): Promise<void> {
|
||||||
|
if (w._prasi?.site_id) {
|
||||||
|
localStorage.setItem(`sid-${w._prasi?.site_id}`, JSON.stringify(arg));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async clear(): Promise<void> {
|
||||||
|
if (w._prasi?.site_id) {
|
||||||
|
localStorage.removeItem(`sid-${w._prasi?.site_id}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -14,6 +14,7 @@ export const session = sqliteTable(
|
||||||
),
|
),
|
||||||
active: integer("active", { mode: "boolean" }),
|
active: integer("active", { mode: "boolean" }),
|
||||||
data: text("data", { mode: "json" }).notNull(),
|
data: text("data", { mode: "json" }).notNull(),
|
||||||
|
wsid: text("wsid", { mode: "json" }).default([]),
|
||||||
expired_at: integer("expired_at", { mode: "timestamp_ms" }),
|
expired_at: integer("expired_at", { mode: "timestamp_ms" }),
|
||||||
},
|
},
|
||||||
(table) => {
|
(table) => {
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
/// <reference types="bun-types" />
|
/// <reference types="bun-types" />
|
||||||
import Database from "bun:sqlite";
|
import Database from "bun:sqlite";
|
||||||
import { and, eq, sql } from "drizzle-orm";
|
import { and, ConsoleLogWriter, eq, sql } from "drizzle-orm";
|
||||||
import { BunSQLiteDatabase, drizzle } from "drizzle-orm/bun-sqlite";
|
import { BunSQLiteDatabase, drizzle } from "drizzle-orm/bun-sqlite";
|
||||||
import { mkdirSync } from "fs";
|
import { mkdirSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { dir } from "../../utils/dir";
|
import { dir } from "../../server/utils/dir";
|
||||||
import { SessionData, SessionStore, SingleSession } from "../type";
|
import { SessionData, SessionStore, SingleSession } from "../type";
|
||||||
import { session } from "./schema";
|
import { session } from "./schema";
|
||||||
|
|
||||||
|
|
@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS session (
|
||||||
created_at integer NOT NULL DEFAULT current_timestamp,
|
created_at integer NOT NULL DEFAULT current_timestamp,
|
||||||
active integer,
|
active integer,
|
||||||
data text NOT NULL,
|
data text NOT NULL,
|
||||||
|
wsid text default \`[]\`,
|
||||||
expired_at integer
|
expired_at integer
|
||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS expired_at_idx ON session (expired_at);
|
CREATE INDEX IF NOT EXISTS expired_at_idx ON session (expired_at);
|
||||||
|
|
@ -68,6 +69,7 @@ CREATE INDEX IF NOT EXISTS expired_at_idx ON session (expired_at);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
create(data) {
|
create(data) {
|
||||||
|
try {
|
||||||
return createSingleStore(
|
return createSingleStore(
|
||||||
db.session
|
db.session
|
||||||
.insert(session)
|
.insert(session)
|
||||||
|
|
@ -82,8 +84,29 @@ CREATE INDEX IF NOT EXISTS expired_at_idx ON session (expired_at);
|
||||||
.returning()
|
.returning()
|
||||||
.get()
|
.get()
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Session Create Error:\n", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update(where, data) {
|
||||||
|
try {
|
||||||
|
db.session
|
||||||
|
.update(session)
|
||||||
|
.set(data)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
...Object.entries(where || {}).map(([k, v]) => {
|
||||||
|
return eq(k as any, v);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Session Update Error:\n", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
findFirst(arg) {
|
findFirst(arg) {
|
||||||
|
try {
|
||||||
return createSingleStore(
|
return createSingleStore(
|
||||||
db.session
|
db.session
|
||||||
.select()
|
.select()
|
||||||
|
|
@ -97,8 +120,12 @@ CREATE INDEX IF NOT EXISTS expired_at_idx ON session (expired_at);
|
||||||
)
|
)
|
||||||
.get()
|
.get()
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Session FindFirst Error:\n", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
findMany(arg) {
|
findMany(arg) {
|
||||||
|
try {
|
||||||
return db.session
|
return db.session
|
||||||
.select()
|
.select()
|
||||||
.from(session)
|
.from(session)
|
||||||
|
|
@ -111,6 +138,9 @@ CREATE INDEX IF NOT EXISTS expired_at_idx ON session (expired_at);
|
||||||
)
|
)
|
||||||
.all()
|
.all()
|
||||||
.map((e) => createSingleStore(e));
|
.map((e) => createSingleStore(e));
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Session FindMany Error:\n", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
} as SessionStore<T>;
|
} as SessionStore<T>;
|
||||||
};
|
};
|
||||||
|
|
@ -32,6 +32,10 @@ export type SessionStore<T> = {
|
||||||
data?: T;
|
data?: T;
|
||||||
expired_at?: number;
|
expired_at?: number;
|
||||||
}) => SingleSession<T>;
|
}) => SingleSession<T>;
|
||||||
|
update: (
|
||||||
|
where: Partial<FilterSessionArg>,
|
||||||
|
data: { active?: boolean; wsid?: string[] }
|
||||||
|
) => SingleSession<T>[];
|
||||||
findMany: (arg?: Partial<FilterSessionArg>) => SingleSession<T>[];
|
findMany: (arg?: Partial<FilterSessionArg>) => SingleSession<T>[];
|
||||||
findFirst: (arg?: Partial<FilterSessionArg>) => null | SingleSession<T>;
|
findFirst: (arg?: Partial<FilterSessionArg>) => null | SingleSession<T>;
|
||||||
};
|
};
|
||||||
|
|
@ -40,16 +44,22 @@ export type ServerSession<T> = SessionStore<T> & {
|
||||||
current?: SingleSession<T>;
|
current?: SingleSession<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ClientSessionStatus = "checking" | "guest" | "expired" | "active";
|
export type ClientSessionStatus =
|
||||||
|
| "checking"
|
||||||
|
| "guest"
|
||||||
|
| "expired"
|
||||||
|
| "active"
|
||||||
|
| "logout";
|
||||||
export type ClientSession<T> = {
|
export type ClientSession<T> = {
|
||||||
status: ClientSessionStatus;
|
status: ClientSessionStatus;
|
||||||
current: null | SessionData<T>;
|
current: null | SessionData<T>;
|
||||||
connect(): Promise<{ status: ClientSessionStatus }>;
|
wsid: string;
|
||||||
login(arg: {
|
connectURL: URL;
|
||||||
method: "user-pass";
|
ws?: WebSocket;
|
||||||
username: string;
|
init(): Promise<{ status: ClientSessionStatus }>;
|
||||||
password: string;
|
connect(auth?: SessionAuth): Promise<void>;
|
||||||
}): Promise<void>;
|
connected: boolean;
|
||||||
|
login(auth: SessionAuth): Promise<SessionData<T>>;
|
||||||
logout(): Promise<void>;
|
logout(): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -66,3 +76,9 @@ export type ServerContext = {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SessionAuth = {
|
||||||
|
method: "user-pass";
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import { SessionData } from "lib/server/session/server-session";
|
|
||||||
|
|
||||||
const w = window as unknown as {
|
|
||||||
_prasi_session: any;
|
|
||||||
_prasi: { site_id: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
type SessionResponse<T> =
|
|
||||||
| { active: false; reason: string }
|
|
||||||
| { active: true; data: T; token: string };
|
|
||||||
|
|
||||||
export const sessionClient = async <T extends SessionData<any>>(arg: {
|
|
||||||
editorSampleData: T;
|
|
||||||
auth: {
|
|
||||||
mode: "user-pass";
|
|
||||||
};
|
|
||||||
on?: Partial<{
|
|
||||||
active: (arg: { token: string; data: T }) => any;
|
|
||||||
expired: () => any;
|
|
||||||
logout: () => any;
|
|
||||||
broadcast: (arg: { data: any }) => any;
|
|
||||||
}>;
|
|
||||||
}): Promise<Session<T>> => {
|
|
||||||
const session: Session<T> = {
|
|
||||||
active: false,
|
|
||||||
id_site: isEditor ? "" : w._prasi.site_id,
|
|
||||||
async login() {},
|
|
||||||
async logout() {},
|
|
||||||
token: "",
|
|
||||||
data: {} as T,
|
|
||||||
};
|
|
||||||
if (isEditor) {
|
|
||||||
session.active = true;
|
|
||||||
session.data = arg.editorSampleData;
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
if (w._prasi_session) return w._prasi_session as Session<T>;
|
|
||||||
|
|
||||||
const s = localStorage.getItem(`prss-${session.id_site}`);
|
|
||||||
if (!s) {
|
|
||||||
session.active = false;
|
|
||||||
} else {
|
|
||||||
let url = siteurl("/_session");
|
|
||||||
if (
|
|
||||||
location.hostname === "prasi.avolut.com" ||
|
|
||||||
location.host === "localhost:4550"
|
|
||||||
) {
|
|
||||||
const newurl = new URL(location.href);
|
|
||||||
newurl.pathname = `/_proxy/${url}`;
|
|
||||||
url = newurl.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const prss = await fetch(url, { method: "POST", body: s });
|
|
||||||
try {
|
|
||||||
const resp = (await prss.json()) as SessionResponse<T>;
|
|
||||||
if (resp) {
|
|
||||||
if (resp.active) {
|
|
||||||
session.data = resp.data;
|
|
||||||
session.active = true;
|
|
||||||
session.token = resp.token;
|
|
||||||
} else {
|
|
||||||
console.warn("Session inactive, reason:" + resp.reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Failed to activate session");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Session<T extends SessionData<any>> = {
|
|
||||||
active: boolean;
|
|
||||||
id_site: string;
|
|
||||||
login: (arg: { username: string; password: string }) => Promise<void>;
|
|
||||||
logout: () => Promise<void>;
|
|
||||||
token: string;
|
|
||||||
data: T;
|
|
||||||
};
|
|
||||||
Loading…
Reference in New Issue