Skip to content

Commit

Permalink
remove auth from onReady updates
Browse files Browse the repository at this point in the history
  • Loading branch information
prostgles committed Nov 25, 2024
1 parent 4b88b5b commit f8fa8a7
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 110 deletions.
4 changes: 2 additions & 2 deletions lib/Auth/AuthHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ export class AuthHandler {

destroy = () => {
const app = this.opts?.expressConfig?.app;
const { login, logoutGetPath, magicLinksExpressRoute, catchAll, loginWithProvider } = AUTH_ROUTES_AND_PARAMS;
removeExpressRoute(app, [login, logoutGetPath, magicLinksExpressRoute, catchAll, loginWithProvider]);
const { login, logoutGetPath, magicLinksExpressRoute, catchAll, loginWithProvider, emailSignup, magicLinksRoute } = AUTH_ROUTES_AND_PARAMS;
removeExpressRoute(app, [login, logoutGetPath, magicLinksExpressRoute, catchAll, loginWithProvider, emailSignup, magicLinksRoute]);
}

throttledFunc = <T>(func: () => Promise<T>, throttle = 500): Promise<T> => {
Expand Down
13 changes: 12 additions & 1 deletion lib/Auth/setAuthProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import { Strategy as MicrosoftStrategy } from "passport-microsoft";
import { Strategy as FacebookStrategy } from "passport-facebook";
import { AuthSocketSchema, getKeys, isDefined, isEmpty } from "prostgles-types";
import { AUTH_ROUTES_AND_PARAMS, AuthHandler } from "./AuthHandler";
import type e from "express";
import { RequestHandler } from "express";
import { removeExpressRouteByName } from "../FileManager/FileManager";


export const upsertNamedExpressMiddleware = (app: e.Express, handler: RequestHandler, name: string) => {
const funcName = name;
Object.defineProperty(handler, "name", { value: funcName });
removeExpressRouteByName(app, name);
app.use(handler);
}

export function setAuthProviders (this: AuthHandler, { registrations, app }: Required<Auth>["expressConfig"]) {
if(!registrations) return;
Expand All @@ -23,7 +34,7 @@ export function setAuthProviders (this: AuthHandler, { registrations, app }: Req
}

if(!isEmpty(providers)){
app.use(passport.initialize());
upsertNamedExpressMiddleware(app, passport.initialize(), "prostglesPassportMiddleware");
}

([
Expand Down
8 changes: 5 additions & 3 deletions lib/Auth/setupAuthRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { RequestHandler } from "express";
import { DBOFullyTyped } from "../DBSchemaBuilder";
import { AUTH_ROUTES_AND_PARAMS, AuthHandler, getLoginClientInfo, HTTPCODES } from "./AuthHandler";
import { AuthClientRequest, ExpressReq, ExpressRes } from "./AuthTypes";
import { setAuthProviders } from "./setAuthProviders";
import { setAuthProviders, upsertNamedExpressMiddleware } from "./setAuthProviders";

export async function setupAuthRoutes(this: AuthHandler) {
if (!this.opts) return;
Expand Down Expand Up @@ -29,7 +30,7 @@ export async function setupAuthRoutes(this: AuthHandler) {
setAuthProviders.bind(this)(expressConfig);

if(use){
app.use((req, res, next) => {
const prostglesUseMiddleware: RequestHandler = (req, res, next) => {
use({
req,
res,
Expand All @@ -38,7 +39,8 @@ export async function setupAuthRoutes(this: AuthHandler) {
dbo: this.dbo as DBOFullyTyped,
db: this.db,
})
})
};
upsertNamedExpressMiddleware(app, prostglesUseMiddleware, "prostglesUseMiddleware");
}

if (magicLinks) {
Expand Down
11 changes: 11 additions & 0 deletions lib/FileManager/FileManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,17 @@ export const removeExpressRoute = (app: ExpressApp | undefined, routePaths: (str
}
}

export const removeExpressRouteByName = (app: ExpressApp | undefined, name: string) => {
const routes = app?._router?.stack;
if(routes){
routes.forEach((route, i) => {
if(route.name === name){
routes.splice(i, 1);
}
})
}
}

export const getFileTypeFromFilename = (fileName: string): { mime: ALLOWED_CONTENT_TYPE; ext: ALLOWED_EXTENSION | string } | undefined => {

const nameParts = fileName.split(".");
Expand Down
100 changes: 3 additions & 97 deletions lib/Prostgles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { AuthHandler } from "./Auth/AuthHandler";
import { FileManager } from "./FileManager/FileManager";
import { SchemaWatch } from "./SchemaWatch/SchemaWatch";
import { OnInitReason, initProstgles } from "./initProstgles";
import { clientCanRunSqlRequest, runClientMethod, runClientRequest, runClientSqlRequest } from "./runClientRequest";
import { makeSocketError, onSocketConnected } from "./onSocketConnected";
import { clientCanRunSqlRequest, runClientSqlRequest } from "./runClientRequest";
import pg = require('pg-promise/typescript/pg-subset');
const { version } = require('../package.json');

Expand All @@ -21,7 +22,6 @@ export { DBHandlerServer };
export type PGP = pgPromise.IMain<{}, pg.IClient>;

import {
AnyObject,
CHANNELS,
ClientSchema,
DBSchemaTable,
Expand Down Expand Up @@ -357,85 +357,7 @@ export class Prostgles {
this.opts.io?.sockets.sockets.forEach(socket => this.onSocketConnected(socket))
}

onSocketConnected = async (socket: PRGLIOSocket) => {
if (this.destroyed) {
console.log("Socket connected to destroyed instance");
socket.disconnect();
return
}
this.connectedSockets.push(socket);

try {
await this.opts.onLog?.({
type: "connect",
sid: this.authHandler?.getSID({ socket }),
socketId: socket.id,
connectedSocketIds: this.connectedSockets.map(s => s.id)
});

if (!this.db || !this.dbo) throw new Error("db/dbo missing");
const { dbo, db } = this;

if (this.opts.onSocketConnect) {
try {
const getUser = async () => { return await this.authHandler?.getClientInfo({ socket }); }
await this.opts.onSocketConnect({ socket, dbo: dbo as any, db, getUser });
} catch(error) {
const connectionError = error instanceof Error? error.message : typeof error === "string"? error : JSON.stringify(error);
socket.emit(CHANNELS.CONNECTION, { connectionError });
socket.disconnect();
return;
}
}

socket.removeAllListeners(CHANNELS.DEFAULT)
socket.on(CHANNELS.DEFAULT, async (args: SocketRequestParams, cb = (..._callback: any[]) => { /* Empty */}) => {
runClientRequest.bind(this)({ ...args, type: "socket", socket })
.then(res => {
cb(null, res)
}).catch(err => {
cb(err);
});
});

socket.on("disconnect", () => {

this.dbEventsManager?.removeNotice(socket);
this.dbEventsManager?.removeNotify(undefined, socket);
this.connectedSockets = this.connectedSockets.filter(s => s.id !== socket.id);
this.dboBuilder.queryStreamer.onDisconnect(socket.id);
this.opts.onLog?.({
type: "disconnect",
sid: this.authHandler?.getSID({ socket }),
socketId: socket.id,
connectedSocketIds: this.connectedSockets.map(s => s.id)
});

if (this.opts.onSocketDisconnect) {
const getUser = async () => { return await this.authHandler?.getClientInfo({ socket }); }
this.opts.onSocketDisconnect({ socket, dbo: dbo as any, db, getUser });
}
});

socket.removeAllListeners(CHANNELS.METHOD)
socket.on(CHANNELS.METHOD, async ({ method, params }: SocketMethodRequest, cb = (..._callback: any) => { /* Empty */ }) => {
runClientMethod.bind(this)({
type: "socket",
socket,
method,
params
}).then(res => {
cb(null, res)
}).catch(err => {
makeSocketError(cb, err)
});
});

this.pushSocketSchema(socket);
} catch (e) {
console.trace("setSocketEvents: ", e)
}
}
onSocketConnected = onSocketConnected.bind(this);

getClientSchema = async (clientReq: Pick<LocalParams, "socket" | "httpReq">) => {

Expand Down Expand Up @@ -551,22 +473,6 @@ export class Prostgles {
}
}

function makeSocketError(cb: (err: AnyObject) => void, err: any) {
cb(getErrorAsObject(err));
}

type SocketRequestParams = {
tableName: string;
command: typeof TABLE_METHODS[number];
param1: any;
param2: any;
param3: any;
}
type SocketMethodRequest = {
method: string;
params: any;
}


export async function getIsSuperUser(db: DB): Promise<boolean> {
return db.oneOrNone("select usesuper from pg_user where usename = CURRENT_USER;").then(r => r.usesuper);
Expand Down
7 changes: 7 additions & 0 deletions lib/RestApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ const jsonParser = bodyParser.json();
export type ExpressApp = {
_router?: {
stack?: {
name: string;
handle: VoidFunction;
path: undefined,
keys?: any[];
route?: {
path?: string;
methods?: {
get?: boolean;
post?: boolean;
put?: boolean;
delete?: boolean;
};
}
}[]
}
Expand Down
20 changes: 17 additions & 3 deletions lib/initProstgles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type OnInitReason =
}
| {
type: "prgl.update";
newOpts: UpdateableOptions;
newOpts: Omit<UpdateableOptions, typeof clientOnlyUpdateKeys[number]>;
}
| {
type: "init" | "prgl.restart" | "TableConfig"
Expand Down Expand Up @@ -61,6 +61,8 @@ export type InitResult = {
options: ProstglesInitOptions;
}

const clientOnlyUpdateKeys = ["auth"] as const satisfies (keyof UpdateableOptions)[];

export const initProstgles = async function(this: Prostgles, onReady: OnReadyCallbackBasic, reason: OnInitReason): Promise<InitResult> {
this.loaded = false;

Expand Down Expand Up @@ -123,7 +125,9 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal

if (this.opts.publish) {

if (!this.opts.io) console.warn("IO missing. Publish has no effect without io");
if (!this.opts.io) {
console.warn("IO missing. Publish has no effect without io");
}

/* 3.9 Check auth config */
this.authHandler = new AuthHandler(this as any);
Expand Down Expand Up @@ -174,6 +178,7 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal
this.opts[k] = newOpts[k];
});


if("fileTable" in newOpts){
await this.initFileTable();
}
Expand All @@ -193,7 +198,16 @@ export const initProstgles = async function(this: Prostgles, onReady: OnReadyCal
this.authHandler = new AuthHandler(this as any);
await this.authHandler.init();
}
if(!isEmpty(newOpts)){

if(isEmpty(newOpts)) return;

/**
* Some of these changes require clients to reconnect
* While others also affect the server and onReady should be called
*/
if(getKeys(newOpts).every(updatedKey => clientOnlyUpdateKeys.includes(updatedKey as any))){
await this.setSocketEvents();
} else {
await this.init(onReady, { type: "prgl.update", newOpts });
}
},
Expand Down
102 changes: 102 additions & 0 deletions lib/onSocketConnected.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { AnyObject, CHANNELS } from "prostgles-types";
import type { Prostgles, TABLE_METHODS } from "./Prostgles";
import { PRGLIOSocket } from "./DboBuilder/DboBuilderTypes";
import { runClientMethod, runClientRequest } from "./runClientRequest";
import { getErrorAsObject } from "./DboBuilder/dboBuilderUtils";

export async function onSocketConnected(this: Prostgles, socket: PRGLIOSocket) {
if (this.destroyed) {
console.log("Socket connected to destroyed instance");
socket.disconnect();
return
}
this.connectedSockets.push(socket);

try {
await this.opts.onLog?.({
type: "connect",
sid: this.authHandler?.getSID({ socket }),
socketId: socket.id,
connectedSocketIds: this.connectedSockets.map(s => s.id)
});

if (!this.db || !this.dbo) throw new Error("db/dbo missing");
const { dbo, db } = this;

if (this.opts.onSocketConnect) {
try {
const getUser = async () => { return await this.authHandler?.getClientInfo({ socket }); }
await this.opts.onSocketConnect({ socket, dbo: dbo as any, db, getUser });
} catch(error) {
const connectionError = error instanceof Error? error.message : typeof error === "string"? error : JSON.stringify(error);
socket.emit(CHANNELS.CONNECTION, { connectionError });
socket.disconnect();
return;
}
}

socket.removeAllListeners(CHANNELS.DEFAULT)
socket.on(CHANNELS.DEFAULT, async (args: SocketRequestParams, cb = (..._callback: any[]) => { /* Empty */}) => {
runClientRequest.bind(this)({ ...args, type: "socket", socket })
.then(res => {
cb(null, res)
}).catch(err => {
cb(err);
});
});

socket.on("disconnect", () => {

this.dbEventsManager?.removeNotice(socket);
this.dbEventsManager?.removeNotify(undefined, socket);
this.connectedSockets = this.connectedSockets.filter(s => s.id !== socket.id);
this.dboBuilder.queryStreamer.onDisconnect(socket.id);
this.opts.onLog?.({
type: "disconnect",
sid: this.authHandler?.getSID({ socket }),
socketId: socket.id,
connectedSocketIds: this.connectedSockets.map(s => s.id)
});

if (this.opts.onSocketDisconnect) {
const getUser = async () => { return await this.authHandler?.getClientInfo({ socket }); }
this.opts.onSocketDisconnect({ socket, dbo: dbo as any, db, getUser });
}
});

socket.removeAllListeners(CHANNELS.METHOD)
socket.on(CHANNELS.METHOD, async ({ method, params }: SocketMethodRequest, cb = (..._callback: any) => { /* Empty */ }) => {
runClientMethod.bind(this)({
type: "socket",
socket,
method,
params
}).then(res => {
cb(null, res)
}).catch(err => {
makeSocketError(cb, err)
});
});

this.pushSocketSchema(socket);
} catch (e) {
console.trace("setSocketEvents: ", e)
}
}


export function makeSocketError(cb: (err: AnyObject) => void, err: any) {
cb(getErrorAsObject(err));
}

type SocketRequestParams = {
tableName: string;
command: typeof TABLE_METHODS[number];
param1: any;
param2: any;
param3: any;
}
type SocketMethodRequest = {
method: string;
params: any;
}
Loading

0 comments on commit f8fa8a7

Please sign in to comment.