prostgles-server 4.2.278 → 4.2.280
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Auth/AuthHandler.d.ts +12 -12
- package/dist/Auth/AuthHandler.d.ts.map +1 -1
- package/dist/Auth/AuthHandler.js +6 -46
- package/dist/Auth/AuthHandler.js.map +1 -1
- package/dist/Auth/AuthTypes.d.ts +3 -3
- package/dist/Auth/AuthTypes.d.ts.map +1 -1
- package/dist/Auth/getClientAuth.d.ts.map +1 -1
- package/dist/Auth/getClientAuth.js +1 -0
- package/dist/Auth/getClientAuth.js.map +1 -1
- package/dist/Auth/utils/getSidAndUserFromRequest.d.ts +0 -5
- package/dist/Auth/utils/getSidAndUserFromRequest.d.ts.map +1 -1
- package/dist/Auth/utils/getSidAndUserFromRequest.js +0 -58
- package/dist/Auth/utils/getSidAndUserFromRequest.js.map +1 -1
- package/dist/Auth/utils/handleGetUser.d.ts +5 -0
- package/dist/Auth/utils/handleGetUser.d.ts.map +1 -1
- package/dist/Auth/utils/handleGetUser.js +30 -2
- package/dist/Auth/utils/handleGetUser.js.map +1 -1
- package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.d.ts +18 -0
- package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.d.ts.map +1 -0
- package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.js +52 -0
- package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.js.map +1 -0
- package/dist/DboBuilder/DboBuilderTypes.d.ts +8 -6
- package/dist/DboBuilder/DboBuilderTypes.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +2 -2
- package/dist/DboBuilder/ViewHandler/parseFieldFilter.d.ts +1 -1
- package/dist/DboBuilder/ViewHandler/parseFieldFilter.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/parseFieldFilter.js.map +1 -1
- package/dist/Filtering.d.ts +1 -1
- package/dist/Prostgles.d.ts +2 -4
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +34 -136
- package/dist/Prostgles.js.map +1 -1
- package/dist/ProstglesTypes.d.ts.map +1 -1
- package/dist/ProstglesTypes.js.map +1 -1
- package/dist/PubSubManager/notifListener.d.ts.map +1 -1
- package/dist/PubSubManager/notifListener.js +1 -1
- package/dist/PubSubManager/notifListener.js.map +1 -1
- package/dist/PublishParser/getFileTableRules.js +3 -3
- package/dist/PublishParser/getFileTableRules.js.map +1 -1
- package/dist/TableConfig/getSchemaDiffQueries.d.ts +2 -2
- package/dist/WebsocketAPI/getClientSchema.d.ts +5 -0
- package/dist/WebsocketAPI/getClientSchema.d.ts.map +1 -0
- package/dist/WebsocketAPI/getClientSchema.js +105 -0
- package/dist/WebsocketAPI/getClientSchema.js.map +1 -0
- package/dist/{onSocketConnected.d.ts → WebsocketAPI/onSocketConnected.d.ts} +2 -2
- package/dist/WebsocketAPI/onSocketConnected.d.ts.map +1 -0
- package/dist/{onSocketConnected.js → WebsocketAPI/onSocketConnected.js} +4 -4
- package/dist/WebsocketAPI/onSocketConnected.js.map +1 -0
- package/dist/WebsocketAPI/pushSocketSchema.d.ts +4 -0
- package/dist/WebsocketAPI/pushSocketSchema.d.ts.map +1 -0
- package/dist/WebsocketAPI/pushSocketSchema.js +40 -0
- package/dist/WebsocketAPI/pushSocketSchema.js.map +1 -0
- package/dist/initProstgles.d.ts.map +1 -1
- package/dist/initProstgles.js +5 -3
- package/dist/initProstgles.js.map +1 -1
- package/dist/runClientRequest.d.ts +1 -1
- package/dist/runClientRequest.d.ts.map +1 -1
- package/dist/runClientRequest.js +3 -1
- package/dist/runClientRequest.js.map +1 -1
- package/lib/Auth/AuthHandler.ts +10 -60
- package/lib/Auth/AuthTypes.ts +3 -3
- package/lib/Auth/getClientAuth.ts +1 -0
- package/lib/Auth/utils/getSidAndUserFromRequest.ts +2 -66
- package/lib/Auth/utils/handleGetUser.ts +31 -2
- package/lib/Auth/utils/setCookieAndGoToReturnURLIFSet.ts +62 -0
- package/lib/DboBuilder/DboBuilderTypes.ts +10 -7
- package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +5 -5
- package/lib/Prostgles.ts +43 -167
- package/lib/ProstglesTypes.ts +0 -1
- package/lib/PubSubManager/notifListener.ts +4 -3
- package/lib/PublishParser/getFileTableRules.ts +4 -4
- package/lib/WebsocketAPI/getClientSchema.ts +111 -0
- package/lib/{onSocketConnected.ts → WebsocketAPI/onSocketConnected.ts} +9 -8
- package/lib/WebsocketAPI/pushSocketSchema.ts +42 -0
- package/lib/initProstgles.ts +6 -3
- package/lib/runClientRequest.ts +4 -2
- package/package.json +1 -1
- package/dist/onSocketConnected.d.ts.map +0 -1
- package/dist/onSocketConnected.js.map +0 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { HTTP_FAIL_CODES, type AuthHandler } from "../AuthHandler";
|
|
2
|
+
import type { ExpressReq } from "../AuthTypes";
|
|
3
|
+
import type { LoginResponseHandler } from "../endpoints/setLoginRequestHandler";
|
|
4
|
+
import { getBasicSessionErrorCode } from "../login";
|
|
5
|
+
import { getReturnUrl } from "./getReturnUrl";
|
|
6
|
+
|
|
7
|
+
export function validateSessionAndSetCookie(
|
|
8
|
+
this: AuthHandler,
|
|
9
|
+
cookie: { sid: string; expires: number },
|
|
10
|
+
requestHandler: { req: ExpressReq; res: LoginResponseHandler }
|
|
11
|
+
) {
|
|
12
|
+
const sessionErrorCode = getBasicSessionErrorCode(cookie);
|
|
13
|
+
if (sessionErrorCode) {
|
|
14
|
+
const { res } = requestHandler;
|
|
15
|
+
res.status(HTTP_FAIL_CODES.UNAUTHORIZED).json({
|
|
16
|
+
success: false,
|
|
17
|
+
code: sessionErrorCode,
|
|
18
|
+
});
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
return this.setCookieAndGoToReturnURLIFSet(cookie, requestHandler);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function setCookieAndGoToReturnURLIFSet(
|
|
25
|
+
this: AuthHandler,
|
|
26
|
+
cookie: { sid: string; expires: number },
|
|
27
|
+
requestHandler: { req: ExpressReq; res: LoginResponseHandler }
|
|
28
|
+
) {
|
|
29
|
+
const { sid, expires } = cookie;
|
|
30
|
+
const { res, req } = requestHandler;
|
|
31
|
+
if (!sid) {
|
|
32
|
+
throw "no sid";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const maxAgeOneDay = 60 * 60 * 24; // 24 hours;
|
|
36
|
+
type CookieExpirationOptions = { maxAge: number } | { expires: Date };
|
|
37
|
+
let cookieDuration: CookieExpirationOptions = {
|
|
38
|
+
maxAge: maxAgeOneDay,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (expires && Number.isFinite(expires) && !isNaN(+new Date(expires))) {
|
|
42
|
+
cookieDuration = { expires: new Date(expires) };
|
|
43
|
+
const days = (+cookieDuration.expires - Date.now()) / (24 * 60 * 60e3);
|
|
44
|
+
if (days >= 400) {
|
|
45
|
+
console.warn(`Cookie expiration is higher than the Chrome 400 day limit: ${days}days`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const cookieOpts = {
|
|
50
|
+
...cookieDuration,
|
|
51
|
+
// The cookie only accessible by the web server
|
|
52
|
+
httpOnly: true,
|
|
53
|
+
//signed: true
|
|
54
|
+
secure: true,
|
|
55
|
+
sameSite: "strict" as const,
|
|
56
|
+
...(this.opts.loginSignupConfig?.cookieOptions ?? {}),
|
|
57
|
+
};
|
|
58
|
+
const cookieData = sid;
|
|
59
|
+
res.cookie(this.sidKeyName, cookieData, cookieOpts);
|
|
60
|
+
const successURL = getReturnUrl(req) || "/";
|
|
61
|
+
res.redirect(successURL);
|
|
62
|
+
}
|
|
@@ -139,6 +139,15 @@ export type TableOrViewInfo = TableInfo &
|
|
|
139
139
|
is_view: boolean;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
+
export type CachedSessionData = {
|
|
143
|
+
userData: Omit<SessionUser, "session">;
|
|
144
|
+
session: BasicSession;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export type CachedSession = {
|
|
148
|
+
__prglCache?: CachedSessionData;
|
|
149
|
+
};
|
|
150
|
+
|
|
142
151
|
export type PRGLIOSocket = {
|
|
143
152
|
readonly id: string;
|
|
144
153
|
|
|
@@ -167,17 +176,11 @@ export type PRGLIOSocket = {
|
|
|
167
176
|
connection: { remoteAddress?: string };
|
|
168
177
|
};
|
|
169
178
|
|
|
170
|
-
/** Used for session caching */
|
|
171
|
-
__prglCache?: {
|
|
172
|
-
userData: Omit<SessionUser, "session">;
|
|
173
|
-
session: BasicSession;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
179
|
_user?: AnyObject;
|
|
177
180
|
|
|
178
181
|
/** Used for publish error caching */
|
|
179
182
|
prostgles?: ClientSchema;
|
|
180
|
-
};
|
|
183
|
+
} & CachedSession;
|
|
181
184
|
|
|
182
185
|
export type LocalParams = {
|
|
183
186
|
// httpReq?: ExpressReq;
|
|
@@ -7,11 +7,11 @@ import { isPlainObject } from "../DboBuilder";
|
|
|
7
7
|
* @param {boolean} allow_empty - allow empty select. defaults to true
|
|
8
8
|
*/
|
|
9
9
|
export const parseFieldFilter = <AllowedKeys extends string[]>(
|
|
10
|
-
fieldParams: FieldFilter<Record<AllowedKeys[number],
|
|
10
|
+
fieldParams: FieldFilter<Record<AllowedKeys[number] | string, 1>> = "*",
|
|
11
11
|
allow_empty = true,
|
|
12
12
|
all_cols: AllowedKeys
|
|
13
13
|
): AllowedKeys | [""] => {
|
|
14
|
-
let colNames:
|
|
14
|
+
let colNames: string[] = [];
|
|
15
15
|
const initialParams = JSON.stringify(fieldParams);
|
|
16
16
|
|
|
17
17
|
if (fieldParams) {
|
|
@@ -82,7 +82,7 @@ export const parseFieldFilter = <AllowedKeys extends string[]>(
|
|
|
82
82
|
if (disallowed.length) {
|
|
83
83
|
return all_cols.filter((col) => !disallowed.includes(col)) as typeof all_cols;
|
|
84
84
|
} else {
|
|
85
|
-
return [...allowed] as
|
|
85
|
+
return [...allowed] as AllowedKeys | [""];
|
|
86
86
|
}
|
|
87
87
|
} else {
|
|
88
88
|
throw (
|
|
@@ -93,9 +93,9 @@ export const parseFieldFilter = <AllowedKeys extends string[]>(
|
|
|
93
93
|
|
|
94
94
|
validate(colNames);
|
|
95
95
|
}
|
|
96
|
-
return colNames as
|
|
96
|
+
return colNames as AllowedKeys | [""];
|
|
97
97
|
|
|
98
|
-
function validate(cols:
|
|
98
|
+
function validate(cols: string[]) {
|
|
99
99
|
const bad_keys = cols.filter((col) => !all_cols.includes(col));
|
|
100
100
|
if (bad_keys.length) {
|
|
101
101
|
throw "\nUnrecognised or illegal fields: " + bad_keys.join(", ");
|
package/lib/Prostgles.ts
CHANGED
|
@@ -6,39 +6,26 @@
|
|
|
6
6
|
import * as pgPromise from "pg-promise";
|
|
7
7
|
import { AuthHandler } from "./Auth/AuthHandler";
|
|
8
8
|
import { FileManager } from "./FileManager/FileManager";
|
|
9
|
-
import { SchemaWatch } from "./SchemaWatch/SchemaWatch";
|
|
10
9
|
import { OnInitReason, initProstgles } from "./initProstgles";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { SchemaWatch } from "./SchemaWatch/SchemaWatch";
|
|
11
|
+
import { getClientSchema } from "./WebsocketAPI/getClientSchema";
|
|
12
|
+
import { onSocketConnected } from "./WebsocketAPI/onSocketConnected";
|
|
13
13
|
import pg = require("pg-promise/typescript/pg-subset");
|
|
14
|
-
const version = (require("../package.json") as { version: string }).version;
|
|
15
14
|
|
|
16
15
|
import type { ProstglesInitOptions } from "./ProstglesTypes";
|
|
17
16
|
import { RestApi } from "./RestApi";
|
|
18
17
|
import TableConfigurator from "./TableConfig/TableConfig";
|
|
19
18
|
|
|
20
|
-
import {
|
|
21
|
-
DBHandlerServer,
|
|
22
|
-
DboBuilder,
|
|
23
|
-
PRGLIOSocket,
|
|
24
|
-
getErrorAsObject,
|
|
25
|
-
} from "./DboBuilder/DboBuilder";
|
|
19
|
+
import { DBHandlerServer, DboBuilder, PRGLIOSocket } from "./DboBuilder/DboBuilder";
|
|
26
20
|
export { DBHandlerServer };
|
|
27
21
|
export type PGP = pgPromise.IMain<{}, pg.IClient>;
|
|
28
22
|
export { getEmailSender, getOrSetTransporter, verifySMTPConfig } from "./Auth/sendEmail";
|
|
29
23
|
export { applyTableConfig } from "./TableConfig/applyTableConfig";
|
|
30
24
|
|
|
31
|
-
import {
|
|
32
|
-
CHANNELS,
|
|
33
|
-
ClientSchema,
|
|
34
|
-
SQLRequest,
|
|
35
|
-
includes,
|
|
36
|
-
isObject,
|
|
37
|
-
omitKeys,
|
|
38
|
-
tryCatchV2,
|
|
39
|
-
} from "prostgles-types";
|
|
25
|
+
import { CHANNELS, tryCatchV2 } from "prostgles-types";
|
|
40
26
|
import { DBEventsManager } from "./DBEventsManager";
|
|
41
27
|
import { PublishParser } from "./PublishParser/PublishParser";
|
|
28
|
+
import { pushSocketSchema } from "./WebsocketAPI/pushSocketSchema";
|
|
42
29
|
|
|
43
30
|
export type DB = pgPromise.IDatabase<{}, pg.IClient>;
|
|
44
31
|
export type DBorTx = DB | pgPromise.ITask<{}>;
|
|
@@ -66,8 +53,8 @@ const DEFAULT_KEYWORDS = {
|
|
|
66
53
|
};
|
|
67
54
|
|
|
68
55
|
import { randomUUID } from "crypto";
|
|
56
|
+
import type { RequestHandler } from "express";
|
|
69
57
|
import * as fs from "fs";
|
|
70
|
-
import { AuthClientRequest } from "./Auth/AuthTypes";
|
|
71
58
|
|
|
72
59
|
export class Prostgles {
|
|
73
60
|
/**
|
|
@@ -332,12 +319,16 @@ export class Prostgles {
|
|
|
332
319
|
setSocketEvents() {
|
|
333
320
|
this.checkDb();
|
|
334
321
|
|
|
335
|
-
|
|
322
|
+
const {
|
|
323
|
+
dbo,
|
|
324
|
+
opts: { io },
|
|
325
|
+
} = this;
|
|
326
|
+
if (!dbo) throw "dbo missing";
|
|
336
327
|
|
|
337
328
|
const publishParser = new PublishParser(this);
|
|
338
329
|
this.publishParser = publishParser;
|
|
339
330
|
|
|
340
|
-
if (!
|
|
331
|
+
if (!io) return;
|
|
341
332
|
|
|
342
333
|
/* Already initialised. Only reconnect sockets */
|
|
343
334
|
if (this.connectedSockets.length) {
|
|
@@ -348,158 +339,43 @@ export class Prostgles {
|
|
|
348
339
|
return;
|
|
349
340
|
}
|
|
350
341
|
|
|
342
|
+
const { authHandler } = this;
|
|
343
|
+
if (authHandler) {
|
|
344
|
+
let redirected = false;
|
|
345
|
+
io.engine.use(((req, res, next) => {
|
|
346
|
+
console.log(req.cookies);
|
|
347
|
+
if (!redirected) {
|
|
348
|
+
redirected = true;
|
|
349
|
+
// this.authHandler?.setCookieAndGoToReturnURLIFSet(
|
|
350
|
+
// { expires: Date.now() + 221000, sid: "heehe" },
|
|
351
|
+
// { req, res }
|
|
352
|
+
// );
|
|
353
|
+
// Set cookie manually on raw HTTP response
|
|
354
|
+
const cookieStr = `${this.authHandler?.sidKeyName}=hehehe; Path=/; Expires=${new Date(Date.now() + 221000).toUTCString()}; HttpOnly`;
|
|
355
|
+
res.setHeader("Set-Cookie", cookieStr);
|
|
356
|
+
|
|
357
|
+
// Handle redirection
|
|
358
|
+
res.statusCode = 302;
|
|
359
|
+
res.setHeader("Location", "/");
|
|
360
|
+
res.end();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
next();
|
|
364
|
+
}) satisfies RequestHandler);
|
|
365
|
+
}
|
|
366
|
+
|
|
351
367
|
/* Initialise */
|
|
352
|
-
|
|
353
|
-
|
|
368
|
+
io.removeAllListeners("connection");
|
|
369
|
+
io.on("connection", this.onSocketConnected);
|
|
354
370
|
/** In some cases io will re-init with already connected sockets */
|
|
355
|
-
|
|
371
|
+
io.sockets.sockets.forEach((socket) => {
|
|
356
372
|
void this.onSocketConnected(socket);
|
|
357
373
|
});
|
|
358
374
|
}
|
|
359
375
|
|
|
360
376
|
onSocketConnected = onSocketConnected.bind(this);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const result = await tryCatchV2(async () => {
|
|
364
|
-
const clientInfo =
|
|
365
|
-
clientReq.socket ?
|
|
366
|
-
{ type: "socket" as const, ...clientReq }
|
|
367
|
-
: { type: "http" as const, ...clientReq };
|
|
368
|
-
|
|
369
|
-
const userData = await this.authHandler?.getSidAndUserFromRequest(clientInfo);
|
|
370
|
-
if (userData === "new-session-redirect") {
|
|
371
|
-
throw "new-session-redirect";
|
|
372
|
-
}
|
|
373
|
-
const { publishParser } = this;
|
|
374
|
-
let fullSchema: Awaited<ReturnType<PublishParser["getSchemaFromPublish"]>> | undefined;
|
|
375
|
-
let publishValidationError;
|
|
376
|
-
|
|
377
|
-
try {
|
|
378
|
-
if (!publishParser) throw "publishParser undefined";
|
|
379
|
-
fullSchema = await publishParser.getSchemaFromPublish({
|
|
380
|
-
...clientInfo,
|
|
381
|
-
userData,
|
|
382
|
-
});
|
|
383
|
-
} catch (e) {
|
|
384
|
-
publishValidationError = e;
|
|
385
|
-
console.error(`\nProstgles Publish validation failed (after socket connected):\n ->`, e);
|
|
386
|
-
}
|
|
387
|
-
let rawSQL = false;
|
|
388
|
-
if (this.opts.publishRawSQL && typeof this.opts.publishRawSQL === "function") {
|
|
389
|
-
const { allowed } = await clientCanRunSqlRequest.bind(this)(clientInfo);
|
|
390
|
-
rawSQL = allowed;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const { schema, tables, tableSchemaErrors } = fullSchema ?? {
|
|
394
|
-
schema: {},
|
|
395
|
-
tables: [],
|
|
396
|
-
tableSchemaErrors: {},
|
|
397
|
-
};
|
|
398
|
-
const joinTables2: string[][] = [];
|
|
399
|
-
if (this.opts.joins) {
|
|
400
|
-
const _joinTables2 = this.dboBuilder
|
|
401
|
-
.getAllJoinPaths()
|
|
402
|
-
.filter((jp) => ![jp.t1, jp.t2].find((t) => !schema[t] || !schema[t]?.findOne))
|
|
403
|
-
.map((jp) => [jp.t1, jp.t2].sort());
|
|
404
|
-
_joinTables2.map((jt) => {
|
|
405
|
-
if (!joinTables2.find((_jt) => _jt.join() === jt.join())) {
|
|
406
|
-
joinTables2.push(jt);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const methods = await publishParser?.getAllowedMethods(clientInfo, userData);
|
|
412
|
-
|
|
413
|
-
const methodSchema: ClientSchema["methods"] =
|
|
414
|
-
!methods ?
|
|
415
|
-
[]
|
|
416
|
-
: Object.entries(methods)
|
|
417
|
-
.map(([methodName, method]) => {
|
|
418
|
-
if (isObject(method) && "run" in method) {
|
|
419
|
-
return {
|
|
420
|
-
name: methodName,
|
|
421
|
-
...omitKeys(method, ["run"]),
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
return methodName;
|
|
425
|
-
})
|
|
426
|
-
.sort((a, b) => {
|
|
427
|
-
const aName = isObject(a) ? a.name : a;
|
|
428
|
-
const bName = isObject(b) ? b.name : b;
|
|
429
|
-
return aName.localeCompare(bName);
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
const authInfo = await this.authHandler?.getClientAuth(clientReq);
|
|
433
|
-
if (authInfo === "new-session-redirect") {
|
|
434
|
-
throw "new-session-redirect";
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const clientSchema: ClientSchema = {
|
|
438
|
-
schema,
|
|
439
|
-
methods: methodSchema,
|
|
440
|
-
tableSchema: tables,
|
|
441
|
-
rawSQL,
|
|
442
|
-
joinTables: joinTables2,
|
|
443
|
-
tableSchemaErrors,
|
|
444
|
-
auth: authInfo?.auth,
|
|
445
|
-
version,
|
|
446
|
-
err: publishValidationError ? "Server Error: User publish validation failed." : undefined,
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
return {
|
|
450
|
-
publishValidationError,
|
|
451
|
-
clientSchema,
|
|
452
|
-
userData,
|
|
453
|
-
};
|
|
454
|
-
});
|
|
455
|
-
const sid = this.authHandler?.getSIDNoError(clientReq);
|
|
456
|
-
await this.opts.onLog?.({
|
|
457
|
-
type: "connect.getClientSchema",
|
|
458
|
-
duration: result.duration,
|
|
459
|
-
sid,
|
|
460
|
-
socketId: clientReq.socket?.id,
|
|
461
|
-
error: result.error || result.data?.publishValidationError,
|
|
462
|
-
});
|
|
463
|
-
if (result.hasError) throw result.error;
|
|
464
|
-
return result.data.clientSchema;
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
pushSocketSchema = async (socket: PRGLIOSocket) => {
|
|
468
|
-
try {
|
|
469
|
-
const clientSchema = await this.getClientSchema({ socket });
|
|
470
|
-
socket.prostgles = clientSchema;
|
|
471
|
-
if (clientSchema.rawSQL) {
|
|
472
|
-
socket.removeAllListeners(CHANNELS.SQL);
|
|
473
|
-
socket.on(
|
|
474
|
-
CHANNELS.SQL,
|
|
475
|
-
(
|
|
476
|
-
sqlRequestData: SQLRequest,
|
|
477
|
-
cb = (..._callback: any) => {
|
|
478
|
-
/* Empty */
|
|
479
|
-
}
|
|
480
|
-
) => {
|
|
481
|
-
runClientSqlRequest
|
|
482
|
-
.bind(this)(sqlRequestData, { socket })
|
|
483
|
-
.then((res) => {
|
|
484
|
-
cb(null, res);
|
|
485
|
-
})
|
|
486
|
-
.catch((err) => {
|
|
487
|
-
makeSocketError(cb, err);
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
await this.dboBuilder.prostgles.opts.onLog?.({
|
|
493
|
-
type: "debug",
|
|
494
|
-
command: "pushSocketSchema",
|
|
495
|
-
duration: -1,
|
|
496
|
-
data: { socketId: socket.id, clientSchema },
|
|
497
|
-
});
|
|
498
|
-
socket.emit(CHANNELS.SCHEMA, clientSchema);
|
|
499
|
-
} catch (err: any) {
|
|
500
|
-
socket.emit(CHANNELS.SCHEMA, { err: getErrorAsObject(err) });
|
|
501
|
-
}
|
|
502
|
-
};
|
|
377
|
+
getClientSchema = getClientSchema.bind(this);
|
|
378
|
+
pushSocketSchema = pushSocketSchema.bind(this);
|
|
503
379
|
}
|
|
504
380
|
|
|
505
381
|
export async function getIsSuperUser(db: DBorTx): Promise<boolean> {
|
package/lib/ProstglesTypes.ts
CHANGED
|
@@ -15,7 +15,6 @@ import pgPromise from "pg-promise";
|
|
|
15
15
|
import pg from "pg-promise/typescript/pg-subset";
|
|
16
16
|
import { AnyObject } from "prostgles-types";
|
|
17
17
|
import type { Server } from "socket.io";
|
|
18
|
-
import { DB } from "./Prostgles";
|
|
19
18
|
import { Awaitable, Publish, PublishMethods, PublishParams } from "./PublishParser/PublishParser";
|
|
20
19
|
|
|
21
20
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pickKeys } from "prostgles-types";
|
|
1
|
+
import { includes, pickKeys } from "prostgles-types";
|
|
2
2
|
import { parseFieldFilter } from "../DboBuilder/ViewHandler/parseFieldFilter";
|
|
3
3
|
import { PubSubManager } from "./PubSubManager";
|
|
4
4
|
import { DELIMITER, log, NOTIF_TYPE, type NotifTypeName } from "./PubSubManagerUtils";
|
|
@@ -139,8 +139,9 @@ export async function notifListener(this: PubSubManager, data: { payload: string
|
|
|
139
139
|
|
|
140
140
|
const actionIsIgnored =
|
|
141
141
|
actions &&
|
|
142
|
-
!
|
|
143
|
-
|
|
142
|
+
!includes(
|
|
143
|
+
parseFieldFilter(actions, false, ["insert", "update", "delete"]),
|
|
144
|
+
commandLowerCase
|
|
144
145
|
);
|
|
145
146
|
if (actionIsIgnored) {
|
|
146
147
|
return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyObject, FullFilter, isDefined } from "prostgles-types";
|
|
1
|
+
import { AnyObject, FullFilter, includes, isDefined } from "prostgles-types";
|
|
2
2
|
import { AuthClientRequest, AuthResultWithSID } from "../Auth/AuthTypes";
|
|
3
3
|
import { parseFieldFilter } from "../DboBuilder/ViewHandler/parseFieldFilter";
|
|
4
4
|
import { PublishParser } from "./PublishParser";
|
|
@@ -54,7 +54,7 @@ export async function getFileTableRules(
|
|
|
54
54
|
if (tableRules.select) {
|
|
55
55
|
const parsedFields = parseFieldFilter(tableRules.select.fields, false, allColumns);
|
|
56
56
|
/** Must be allowed to view this column */
|
|
57
|
-
if (
|
|
57
|
+
if (includes(parsedFields, column)) {
|
|
58
58
|
forcedSelectFilters.push({
|
|
59
59
|
$existsJoined: {
|
|
60
60
|
path,
|
|
@@ -66,14 +66,14 @@ export async function getFileTableRules(
|
|
|
66
66
|
if (tableRules.insert) {
|
|
67
67
|
const parsedFields = parseFieldFilter(tableRules.insert.fields, false, allColumns);
|
|
68
68
|
/** Must be allowed to view this column */
|
|
69
|
-
if (
|
|
69
|
+
if (includes(parsedFields, column)) {
|
|
70
70
|
allowedNestedInserts.push({ table: tableName, column });
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
if (tableRules.update) {
|
|
74
74
|
const parsedFields = parseFieldFilter(tableRules.update.fields, false, allColumns);
|
|
75
75
|
/** Must be allowed to view this column */
|
|
76
|
-
if (
|
|
76
|
+
if (includes(parsedFields, column)) {
|
|
77
77
|
forcedUpdateFilters.push({
|
|
78
78
|
$existsJoined: {
|
|
79
79
|
path,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { isObject, omitKeys, tryCatchV2, type ClientSchema } from "prostgles-types";
|
|
2
|
+
import type { AuthClientRequest } from "../Auth/AuthTypes";
|
|
3
|
+
import { Prostgles } from "../Prostgles";
|
|
4
|
+
import type { PublishParser } from "../PublishParser/PublishParser";
|
|
5
|
+
import { clientCanRunSqlRequest } from "../runClientRequest";
|
|
6
|
+
const version = (require("../../package.json") as { version: string }).version;
|
|
7
|
+
|
|
8
|
+
export async function getClientSchema(this: Prostgles, clientReq: AuthClientRequest) {
|
|
9
|
+
const result = await tryCatchV2(async () => {
|
|
10
|
+
const clientInfo =
|
|
11
|
+
clientReq.socket ?
|
|
12
|
+
{ type: "socket" as const, ...clientReq }
|
|
13
|
+
: { type: "http" as const, ...clientReq };
|
|
14
|
+
|
|
15
|
+
const userData = await this.authHandler?.getSidAndUserFromRequest(clientInfo);
|
|
16
|
+
if (userData === "new-session-redirect") {
|
|
17
|
+
throw "new-session-redirect";
|
|
18
|
+
}
|
|
19
|
+
const { publishParser } = this;
|
|
20
|
+
let fullSchema: Awaited<ReturnType<PublishParser["getSchemaFromPublish"]>> | undefined;
|
|
21
|
+
let publishValidationError;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
if (!publishParser) throw "publishParser undefined";
|
|
25
|
+
fullSchema = await publishParser.getSchemaFromPublish({
|
|
26
|
+
...clientInfo,
|
|
27
|
+
userData,
|
|
28
|
+
});
|
|
29
|
+
} catch (e) {
|
|
30
|
+
publishValidationError = e;
|
|
31
|
+
console.error(`\nProstgles Publish validation failed (after socket connected):\n ->`, e);
|
|
32
|
+
}
|
|
33
|
+
let rawSQL = false;
|
|
34
|
+
if (this.opts.publishRawSQL && typeof this.opts.publishRawSQL === "function") {
|
|
35
|
+
const { allowed } = await clientCanRunSqlRequest.bind(this)(clientInfo);
|
|
36
|
+
rawSQL = allowed;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { schema, tables, tableSchemaErrors } = fullSchema ?? {
|
|
40
|
+
schema: {},
|
|
41
|
+
tables: [],
|
|
42
|
+
tableSchemaErrors: {},
|
|
43
|
+
};
|
|
44
|
+
const joinTables2: string[][] = [];
|
|
45
|
+
if (this.opts.joins) {
|
|
46
|
+
const _joinTables2 = this.dboBuilder
|
|
47
|
+
.getAllJoinPaths()
|
|
48
|
+
.filter((jp) => ![jp.t1, jp.t2].find((t) => !schema[t] || !schema[t]?.findOne))
|
|
49
|
+
.map((jp) => [jp.t1, jp.t2].sort());
|
|
50
|
+
_joinTables2.map((jt) => {
|
|
51
|
+
if (!joinTables2.find((_jt) => _jt.join() === jt.join())) {
|
|
52
|
+
joinTables2.push(jt);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const methods = await publishParser?.getAllowedMethods(clientInfo, userData);
|
|
58
|
+
|
|
59
|
+
const methodSchema: ClientSchema["methods"] =
|
|
60
|
+
!methods ?
|
|
61
|
+
[]
|
|
62
|
+
: Object.entries(methods)
|
|
63
|
+
.map(([methodName, method]) => {
|
|
64
|
+
if (isObject(method) && "run" in method) {
|
|
65
|
+
return {
|
|
66
|
+
name: methodName,
|
|
67
|
+
...omitKeys(method, ["run"]),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return methodName;
|
|
71
|
+
})
|
|
72
|
+
.sort((a, b) => {
|
|
73
|
+
const aName = isObject(a) ? a.name : a;
|
|
74
|
+
const bName = isObject(b) ? b.name : b;
|
|
75
|
+
return aName.localeCompare(bName);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const authInfo = await this.authHandler?.getClientAuth(clientReq);
|
|
79
|
+
if (authInfo === "new-session-redirect") {
|
|
80
|
+
throw "new-session-redirect";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const clientSchema: ClientSchema = {
|
|
84
|
+
schema,
|
|
85
|
+
methods: methodSchema,
|
|
86
|
+
tableSchema: tables,
|
|
87
|
+
rawSQL,
|
|
88
|
+
joinTables: joinTables2,
|
|
89
|
+
tableSchemaErrors,
|
|
90
|
+
auth: authInfo?.auth,
|
|
91
|
+
version,
|
|
92
|
+
err: publishValidationError ? "Server Error: User publish validation failed." : undefined,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
publishValidationError,
|
|
97
|
+
clientSchema,
|
|
98
|
+
userData,
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
const sid = this.authHandler?.getSIDNoError(clientReq);
|
|
102
|
+
await this.opts.onLog?.({
|
|
103
|
+
type: "connect.getClientSchema",
|
|
104
|
+
duration: result.duration,
|
|
105
|
+
sid,
|
|
106
|
+
socketId: clientReq.socket?.id,
|
|
107
|
+
error: result.error || result.data?.publishValidationError,
|
|
108
|
+
});
|
|
109
|
+
if (result.hasError) throw result.error;
|
|
110
|
+
return result.data.clientSchema;
|
|
111
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { AnyObject, CHANNELS } from "prostgles-types";
|
|
2
|
-
import type { Prostgles, TABLE_METHODS } from "
|
|
3
|
-
import { PRGLIOSocket } from "
|
|
4
|
-
import { runClientMethod, runClientRequest } from "
|
|
5
|
-
import { getErrorAsObject } from "
|
|
6
|
-
import { DBOFullyTyped } from "
|
|
7
|
-
import { getClientRequestIPsInfo } from "
|
|
2
|
+
import type { Prostgles, TABLE_METHODS } from "../Prostgles";
|
|
3
|
+
import { PRGLIOSocket } from "../DboBuilder/DboBuilderTypes";
|
|
4
|
+
import { runClientMethod, runClientRequest } from "../runClientRequest";
|
|
5
|
+
import { getErrorAsObject } from "../DboBuilder/dboBuilderUtils";
|
|
6
|
+
import { DBOFullyTyped } from "../DBSchemaBuilder";
|
|
7
|
+
import { getClientRequestIPsInfo } from "../Auth/AuthHandler";
|
|
8
|
+
import type { AuthResultWithSID, SessionUser } from "../Auth/AuthTypes";
|
|
8
9
|
|
|
9
10
|
export async function onSocketConnected(this: Prostgles, socket: PRGLIOSocket) {
|
|
10
11
|
if (!this.db || !this.dbo) throw new Error("db/dbo missing");
|
|
@@ -18,8 +19,8 @@ export async function onSocketConnected(this: Prostgles, socket: PRGLIOSocket) {
|
|
|
18
19
|
this.connectedSockets.push(socket);
|
|
19
20
|
|
|
20
21
|
try {
|
|
21
|
-
const getUser = async () => {
|
|
22
|
-
if (!this.authHandler)
|
|
22
|
+
const getUser = async (): Promise<AuthResultWithSID<SessionUser>> => {
|
|
23
|
+
if (!this.authHandler) return { sid: undefined, user: undefined };
|
|
23
24
|
const res = await this.authHandler.getSidAndUserFromRequest({ socket });
|
|
24
25
|
if (res === "new-session-redirect") {
|
|
25
26
|
socket.emit(CHANNELS.AUTHGUARD, {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CHANNELS, type SQLRequest } from "prostgles-types";
|
|
2
|
+
import type { PRGLIOSocket } from "../DboBuilder/DboBuilderTypes";
|
|
3
|
+
import { Prostgles } from "../Prostgles";
|
|
4
|
+
import { runClientSqlRequest } from "../runClientRequest";
|
|
5
|
+
import { makeSocketError } from "./onSocketConnected";
|
|
6
|
+
import { getErrorAsObject } from "../DboBuilder/dboBuilderUtils";
|
|
7
|
+
export async function pushSocketSchema(this: Prostgles, socket: PRGLIOSocket) {
|
|
8
|
+
try {
|
|
9
|
+
const clientSchema = await this.getClientSchema({ socket });
|
|
10
|
+
socket.prostgles = clientSchema;
|
|
11
|
+
if (clientSchema.rawSQL) {
|
|
12
|
+
socket.removeAllListeners(CHANNELS.SQL);
|
|
13
|
+
socket.on(
|
|
14
|
+
CHANNELS.SQL,
|
|
15
|
+
(
|
|
16
|
+
sqlRequestData: SQLRequest,
|
|
17
|
+
cb = (..._callback: any) => {
|
|
18
|
+
/* Empty */
|
|
19
|
+
}
|
|
20
|
+
) => {
|
|
21
|
+
runClientSqlRequest
|
|
22
|
+
.bind(this)(sqlRequestData, { socket })
|
|
23
|
+
.then((res) => {
|
|
24
|
+
cb(null, res);
|
|
25
|
+
})
|
|
26
|
+
.catch((err) => {
|
|
27
|
+
makeSocketError(cb, err);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
await this.dboBuilder.prostgles.opts.onLog?.({
|
|
33
|
+
type: "debug",
|
|
34
|
+
command: "pushSocketSchema",
|
|
35
|
+
duration: -1,
|
|
36
|
+
data: { socketId: socket.id, clientSchema },
|
|
37
|
+
});
|
|
38
|
+
socket.emit(CHANNELS.SCHEMA, clientSchema);
|
|
39
|
+
} catch (err: any) {
|
|
40
|
+
socket.emit(CHANNELS.SCHEMA, { err: getErrorAsObject(err) });
|
|
41
|
+
}
|
|
42
|
+
}
|
package/lib/initProstgles.ts
CHANGED
|
@@ -86,7 +86,9 @@ export const initProstgles = async function (
|
|
|
86
86
|
this.opts.auth?.loginSignupConfig?.app;
|
|
87
87
|
|
|
88
88
|
/** Crucial in ensuring the runtime version of express works as expected */
|
|
89
|
-
if (expressApp)
|
|
89
|
+
if (expressApp) {
|
|
90
|
+
await removeExpressRoutesTest(expressApp);
|
|
91
|
+
}
|
|
90
92
|
|
|
91
93
|
if (!this.db) {
|
|
92
94
|
let existingAppName = "";
|
|
@@ -104,7 +106,7 @@ export const initProstgles = async function (
|
|
|
104
106
|
const url = new URL(connString);
|
|
105
107
|
existingAppName =
|
|
106
108
|
url.searchParams.get("application_name") ?? url.searchParams.get("ApplicationName") ?? "";
|
|
107
|
-
} catch
|
|
109
|
+
} catch {}
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
const conObj =
|
|
@@ -115,7 +117,8 @@ export const initProstgles = async function (
|
|
|
115
117
|
|
|
116
118
|
/* 1. Connect to db */
|
|
117
119
|
const { db, pgp } = getDbConnection({
|
|
118
|
-
|
|
120
|
+
onQuery: this.opts.onQuery,
|
|
121
|
+
DEBUG_MODE: this.opts.DEBUG_MODE,
|
|
119
122
|
dbConnection: { ...conObj, application_name },
|
|
120
123
|
onNotice: (notice) => {
|
|
121
124
|
if (this.opts.onNotice) this.opts.onNotice(notice);
|