prostgles-server 4.2.279 → 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.
Files changed (79) hide show
  1. package/dist/Auth/AuthHandler.d.ts +11 -12
  2. package/dist/Auth/AuthHandler.d.ts.map +1 -1
  3. package/dist/Auth/AuthHandler.js +5 -46
  4. package/dist/Auth/AuthHandler.js.map +1 -1
  5. package/dist/Auth/AuthTypes.d.ts +3 -3
  6. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  7. package/dist/Auth/getClientAuth.d.ts.map +1 -1
  8. package/dist/Auth/getClientAuth.js +1 -0
  9. package/dist/Auth/getClientAuth.js.map +1 -1
  10. package/dist/Auth/utils/getSidAndUserFromRequest.d.ts +0 -5
  11. package/dist/Auth/utils/getSidAndUserFromRequest.d.ts.map +1 -1
  12. package/dist/Auth/utils/getSidAndUserFromRequest.js +0 -58
  13. package/dist/Auth/utils/getSidAndUserFromRequest.js.map +1 -1
  14. package/dist/Auth/utils/handleGetUser.d.ts +5 -0
  15. package/dist/Auth/utils/handleGetUser.d.ts.map +1 -1
  16. package/dist/Auth/utils/handleGetUser.js +30 -2
  17. package/dist/Auth/utils/handleGetUser.js.map +1 -1
  18. package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.d.ts +18 -0
  19. package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.d.ts.map +1 -0
  20. package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.js +52 -0
  21. package/dist/Auth/utils/setCookieAndGoToReturnURLIFSet.js.map +1 -0
  22. package/dist/DboBuilder/DboBuilderTypes.d.ts +8 -6
  23. package/dist/DboBuilder/DboBuilderTypes.d.ts.map +1 -1
  24. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +2 -2
  25. package/dist/DboBuilder/ViewHandler/parseFieldFilter.d.ts +1 -1
  26. package/dist/DboBuilder/ViewHandler/parseFieldFilter.d.ts.map +1 -1
  27. package/dist/DboBuilder/ViewHandler/parseFieldFilter.js.map +1 -1
  28. package/dist/Filtering.d.ts +1 -1
  29. package/dist/Prostgles.d.ts +2 -4
  30. package/dist/Prostgles.d.ts.map +1 -1
  31. package/dist/Prostgles.js +34 -136
  32. package/dist/Prostgles.js.map +1 -1
  33. package/dist/ProstglesTypes.d.ts.map +1 -1
  34. package/dist/ProstglesTypes.js.map +1 -1
  35. package/dist/PubSubManager/notifListener.d.ts.map +1 -1
  36. package/dist/PubSubManager/notifListener.js +1 -1
  37. package/dist/PubSubManager/notifListener.js.map +1 -1
  38. package/dist/PublishParser/getFileTableRules.js +3 -3
  39. package/dist/PublishParser/getFileTableRules.js.map +1 -1
  40. package/dist/TableConfig/getSchemaDiffQueries.d.ts +2 -2
  41. package/dist/WebsocketAPI/getClientSchema.d.ts +5 -0
  42. package/dist/WebsocketAPI/getClientSchema.d.ts.map +1 -0
  43. package/dist/WebsocketAPI/getClientSchema.js +105 -0
  44. package/dist/WebsocketAPI/getClientSchema.js.map +1 -0
  45. package/dist/{onSocketConnected.d.ts → WebsocketAPI/onSocketConnected.d.ts} +2 -2
  46. package/dist/WebsocketAPI/onSocketConnected.d.ts.map +1 -0
  47. package/dist/{onSocketConnected.js → WebsocketAPI/onSocketConnected.js} +4 -4
  48. package/dist/WebsocketAPI/onSocketConnected.js.map +1 -0
  49. package/dist/WebsocketAPI/pushSocketSchema.d.ts +4 -0
  50. package/dist/WebsocketAPI/pushSocketSchema.d.ts.map +1 -0
  51. package/dist/WebsocketAPI/pushSocketSchema.js +40 -0
  52. package/dist/WebsocketAPI/pushSocketSchema.js.map +1 -0
  53. package/dist/initProstgles.d.ts.map +1 -1
  54. package/dist/initProstgles.js +5 -3
  55. package/dist/initProstgles.js.map +1 -1
  56. package/dist/runClientRequest.d.ts +1 -1
  57. package/dist/runClientRequest.d.ts.map +1 -1
  58. package/dist/runClientRequest.js +3 -1
  59. package/dist/runClientRequest.js.map +1 -1
  60. package/lib/Auth/AuthHandler.ts +9 -59
  61. package/lib/Auth/AuthTypes.ts +3 -3
  62. package/lib/Auth/getClientAuth.ts +1 -0
  63. package/lib/Auth/utils/getSidAndUserFromRequest.ts +2 -66
  64. package/lib/Auth/utils/handleGetUser.ts +31 -2
  65. package/lib/Auth/utils/setCookieAndGoToReturnURLIFSet.ts +62 -0
  66. package/lib/DboBuilder/DboBuilderTypes.ts +10 -7
  67. package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +5 -5
  68. package/lib/Prostgles.ts +43 -167
  69. package/lib/ProstglesTypes.ts +0 -1
  70. package/lib/PubSubManager/notifListener.ts +4 -3
  71. package/lib/PublishParser/getFileTableRules.ts +4 -4
  72. package/lib/WebsocketAPI/getClientSchema.ts +111 -0
  73. package/lib/{onSocketConnected.ts → WebsocketAPI/onSocketConnected.ts} +9 -8
  74. package/lib/WebsocketAPI/pushSocketSchema.ts +42 -0
  75. package/lib/initProstgles.ts +6 -3
  76. package/lib/runClientRequest.ts +4 -2
  77. package/package.json +1 -1
  78. package/dist/onSocketConnected.d.ts.map +0 -1
  79. 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], any>> = "*",
10
+ fieldParams: FieldFilter<Record<AllowedKeys[number] | string, 1>> = "*",
11
11
  allow_empty = true,
12
12
  all_cols: AllowedKeys
13
13
  ): AllowedKeys | [""] => {
14
- let colNames: AllowedKeys = [] as any;
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 any;
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 any;
96
+ return colNames as AllowedKeys | [""];
97
97
 
98
- function validate(cols: AllowedKeys) {
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 { makeSocketError, onSocketConnected } from "./onSocketConnected";
12
- import { clientCanRunSqlRequest, runClientSqlRequest } from "./runClientRequest";
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
- if (!this.dbo) throw "dbo missing";
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 (!this.opts.io) return;
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
- this.opts.io.removeAllListeners("connection");
353
- this.opts.io.on("connection", this.onSocketConnected);
368
+ io.removeAllListeners("connection");
369
+ io.on("connection", this.onSocketConnected);
354
370
  /** In some cases io will re-init with already connected sockets */
355
- this.opts.io.sockets.sockets.forEach((socket) => {
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
- getClientSchema = async (clientReq: AuthClientRequest) => {
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> {
@@ -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
- !parseFieldFilter(actions, false, ["insert", "update", "delete"]).includes(
143
- commandLowerCase as any
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 (parsedFields.includes(column as any)) {
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 (parsedFields.includes(column as any)) {
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 (parsedFields.includes(column as any)) {
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 "./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";
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) throw "authHandler missing";
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
+ }
@@ -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) await removeExpressRoutesTest(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 (e) {}
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
- ...this.opts,
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);