prostgles-server 2.0.173 → 2.0.176

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 (64) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/dist/AuthHandler.d.ts +13 -12
  3. package/dist/AuthHandler.d.ts.map +1 -1
  4. package/dist/AuthHandler.js +5 -2
  5. package/dist/AuthHandler.js.map +1 -1
  6. package/dist/DBSchemaBuilder.d.ts +11 -0
  7. package/dist/DBSchemaBuilder.d.ts.map +1 -0
  8. package/dist/DBSchemaBuilder.js +56 -0
  9. package/dist/DBSchemaBuilder.js.map +1 -0
  10. package/dist/DboBuilder.d.ts +23 -22
  11. package/dist/DboBuilder.d.ts.map +1 -1
  12. package/dist/DboBuilder.js +36 -61
  13. package/dist/DboBuilder.js.map +1 -1
  14. package/dist/FileManager.d.ts +2 -2
  15. package/dist/FileManager.d.ts.map +1 -1
  16. package/dist/Filtering.d.ts.map +1 -1
  17. package/dist/Filtering.js.map +1 -1
  18. package/dist/Prostgles.d.ts +25 -257
  19. package/dist/Prostgles.d.ts.map +1 -1
  20. package/dist/Prostgles.js +12 -376
  21. package/dist/Prostgles.js.map +1 -1
  22. package/dist/PubSubManager.d.ts +6 -5
  23. package/dist/PubSubManager.d.ts.map +1 -1
  24. package/dist/PubSubManager.js.map +1 -1
  25. package/dist/PublishParser.d.ts +262 -0
  26. package/dist/PublishParser.d.ts.map +1 -0
  27. package/dist/PublishParser.js +391 -0
  28. package/dist/PublishParser.js.map +1 -0
  29. package/dist/QueryBuilder.d.ts +20 -4
  30. package/dist/QueryBuilder.d.ts.map +1 -1
  31. package/dist/QueryBuilder.js.map +1 -1
  32. package/dist/TableConfig.d.ts +6 -3
  33. package/dist/TableConfig.d.ts.map +1 -1
  34. package/dist/TableConfig.js +28 -1
  35. package/dist/TableConfig.js.map +1 -1
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +4 -0
  39. package/dist/index.js.map +1 -1
  40. package/lib/AuthHandler.ts +25 -19
  41. package/lib/DBSchemaBuilder.ts +91 -0
  42. package/lib/DboBuilder.ts +84 -98
  43. package/lib/FileManager.ts +2 -2
  44. package/lib/Filtering.ts +3 -3
  45. package/lib/Prostgles.ts +33 -704
  46. package/lib/PubSubManager.ts +6 -5
  47. package/lib/PublishParser.ts +723 -0
  48. package/lib/QueryBuilder.ts +6 -5
  49. package/lib/TableConfig.ts +36 -5
  50. package/lib/index.ts +4 -4
  51. package/package.json +2 -2
  52. package/tests/client/PID.txt +1 -1
  53. package/tests/client/index.js +2 -2
  54. package/tests/client/index.ts +2 -2
  55. package/tests/client/package-lock.json +15 -15
  56. package/tests/client/package.json +1 -1
  57. package/tests/client_only_queries.js +24 -1
  58. package/tests/client_only_queries.ts +23 -1
  59. package/tests/isomorphic_queries.js +3 -0
  60. package/tests/isomorphic_queries.ts +5 -2
  61. package/tests/server/DBoGenerated.d.ts +428 -286
  62. package/tests/server/index.js +1 -14
  63. package/tests/server/index.ts +5 -19
  64. package/tests/server/package-lock.json +3 -3
package/lib/Prostgles.ts CHANGED
@@ -16,19 +16,19 @@ console.log("Add a basic auth mode where user and sessions table are created");
16
16
  import TableConfigurator, { TableConfig } from "./TableConfig";
17
17
 
18
18
  import { get } from "./utils";
19
- import { DboBuilder, DbHandler, TableHandler, ViewHandler, isPlainObject, LocalParams, CommonTableRules, TableSchema, PRGLIOSocket } from "./DboBuilder";
19
+ import { DboBuilder, DBHandlerServer, isPlainObject, LocalParams, TableSchema, PRGLIOSocket } from "./DboBuilder";
20
20
  import { PubSubManager, DEFAULT_SYNC_BATCH_SIZE, asValue } from "./PubSubManager";
21
- export { DbHandler }
21
+ export { DBHandlerServer }
22
22
  export type PGP = pgPromise.IMain<{}, pg.IClient>;
23
23
 
24
- import { SQLRequest, TableSchemaForClient, MethodKey, CHANNELS, AnyObject, RULE_METHODS, ClientSchema, getKeys } from "prostgles-types";
25
-
24
+ import { SQLRequest, TableSchemaForClient, CHANNELS, AnyObject, ClientSchema, getKeys, DBSchemaTable, DBSchema } from "prostgles-types";
25
+ import { Publish, PublishMethods, PublishParams, PublishParser } from "./PublishParser";
26
26
  import { DBEventsManager } from "./DBEventsManager";
27
27
 
28
28
  export type DB = pgPromise.IDatabase<{}, pg.IClient>;
29
29
  type DbConnection = string | pg.IConnectionParameters<pg.IClient>;
30
30
  type DbConnectionOpts = pg.IDefaults;
31
- const TABLE_METHODS = ["update", "find", "findOne", "insert", "delete", "upsert"];
31
+ export const TABLE_METHODS = ["update", "find", "findOne", "insert", "delete", "upsert"] as const;
32
32
  function getDbConnection(dbConnection: DbConnection, options: DbConnectionOpts | undefined, debugQueries = false, onNotice: ProstglesInitOptions["onNotice"]): { db: DB, pgp: PGP } {
33
33
  let pgp: PGP = pgPromise({
34
34
 
@@ -76,238 +76,6 @@ function getDbConnection(dbConnection: DbConnection, options: DbConnectionOpts |
76
76
  };
77
77
  }
78
78
 
79
-
80
- import { Socket } from "dgram";
81
- import { FieldFilter, SelectParamsBasic as SelectParams } from "prostgles-types";
82
-
83
- export type InsertRequestData = {
84
- data: object | object[]
85
- returning: FieldFilter;
86
- }
87
- export type SelectRequestData = {
88
- filter: object;
89
- params: SelectParams;
90
- }
91
- export type DeleteRequestData = {
92
- filter: object;
93
- returning: FieldFilter;
94
- }
95
- export type UpdateRequestDataOne = {
96
- filter: object;
97
- data: object;
98
- returning: FieldFilter;
99
- }
100
- export type UpdateReq = {
101
- filter: object;
102
- data: object;
103
- }
104
- export type UpdateRequestDataBatch = {
105
- data: UpdateReq[];
106
- }
107
- export type UpdateRequestData = UpdateRequestDataOne | UpdateRequestDataBatch;
108
-
109
- export type ValidateRow = (row: AnyObject) => AnyObject | Promise<AnyObject>;
110
- export type ValidateUpdateRow = (args: { update: AnyObject, filter: AnyObject }) => AnyObject | Promise<AnyObject>;
111
-
112
- export type SelectRule = {
113
-
114
- /**
115
- * Fields allowed to be selected. Tip: Use false to exclude field
116
- */
117
- fields: FieldFilter;
118
-
119
- /**
120
- * The maximum number of rows a user can get in a select query. null by default. Unless a null or higher limit is specified 100 rows will be returned by the default
121
- */
122
- maxLimit?: number | null;
123
-
124
- /**
125
- * Filter added to every query (e.g. user_id) to restrict access
126
- */
127
- forcedFilter?: AnyObject;
128
-
129
- /**
130
- * Fields user can filter by
131
- * */
132
- filterFields?: FieldFilter;
133
-
134
- /**
135
- * Validation logic to check/update data for each request
136
- */
137
- validate?(args: SelectRequestData): SelectRequestData | Promise<SelectRequestData>;
138
-
139
- }
140
- export type InsertRule = {
141
-
142
- /**
143
- * Fields allowed to be inserted. Tip: Use false to exclude field
144
- */
145
- fields: FieldFilter;
146
-
147
- /**
148
- * Data to include/overwrite on each insert
149
- */
150
- forcedData?: AnyObject;
151
-
152
- /**
153
- * Fields user can view after inserting
154
- */
155
- returningFields?: FieldFilter;
156
-
157
- /**
158
- * Validation logic to check/update data for each request. Happens before publish rule checks (for fields, forcedData/forcedFilter)
159
- */
160
- preValidate?: ValidateRow
161
-
162
- /**
163
- * Validation logic to check/update data for each request. Happens after publish rule checks (for fields, forcedData/forcedFilter)
164
- */
165
- validate?: ValidateRow
166
- }
167
- export type UpdateRule = {
168
-
169
- /**
170
- * Fields allowed to be updated. Tip: Use false/0 to exclude field
171
- */
172
- fields: FieldFilter;
173
-
174
- /**
175
- * Row level FGAC
176
- * Used when the editable fields change based on the updated row
177
- * If specified then the fields from the first matching filter table.count({ ...filter, ...updateFilter }) > 0 will be used
178
- * If none matching then the "fields" will be used
179
- * Specify in decreasing order of specificity otherwise a more general filter will match first
180
- */
181
- dynamicFields?: {
182
- filter: AnyObject;
183
- fields: FieldFilter;
184
- }[];
185
-
186
- /**
187
- * Filter added to every query (e.g. user_id) to restrict access
188
- * This filter cannot be updated
189
- */
190
- forcedFilter?: AnyObject;
191
-
192
- /**
193
- * Data to include/overwrite on each updatDBe
194
- */
195
- forcedData?: AnyObject;
196
-
197
- /**
198
- * Fields user can use to find the updates
199
- */
200
- filterFields?: FieldFilter;
201
-
202
- /**
203
- * Fields user can view after updating
204
- */
205
- returningFields?: FieldFilter;
206
-
207
- /**
208
- * Validation logic to check/update data for each request
209
- */
210
- validate?: ValidateUpdateRow;
211
-
212
- };
213
-
214
- export type DeleteRule = {
215
-
216
- /**
217
- * Filter added to every query (e.g. user_id) to restrict access
218
- */
219
- forcedFilter?: AnyObject;
220
-
221
- /**
222
- * Fields user can filter by
223
- */
224
- filterFields?: FieldFilter;
225
-
226
- /**
227
- * Fields user can view after deleting
228
- */
229
- returningFields?: FieldFilter;
230
-
231
- /**
232
- * Validation logic to check/update data for each request
233
- */
234
- validate?(...args: any[]): UpdateRequestData
235
- }
236
- export type SyncRule = {
237
-
238
- /**
239
- * Primary keys used in updating data
240
- */
241
- id_fields: string[];
242
-
243
- /**
244
- * Numerical incrementing fieldname (last updated timestamp) used to sync items
245
- */
246
- synced_field: string;
247
-
248
- /**
249
- * EXPERIMENTAL. Disabled by default. If true then server will attempt to delete any records missing from client.
250
- */
251
- allow_delete?: boolean;
252
-
253
- /**
254
- * Throttle replication transmission in milliseconds. Defaults to 100
255
- */
256
- throttle?: number;
257
-
258
- /**
259
- * Number of rows to send per trip. Defaults to 50
260
- */
261
- batch_size?: number;
262
- }
263
- export type SubscribeRule = {
264
- throttle?: number;
265
- }
266
-
267
- export type TableRule = CommonTableRules & {
268
- select?: SelectRule;
269
- insert?: InsertRule;
270
- update?: UpdateRule;
271
- delete?: DeleteRule;
272
- sync?: SyncRule;
273
- subscribe?: SubscribeRule;
274
- };
275
- export type ViewRule = {
276
- select: SelectRule;
277
- };
278
- export type PublishTableRule = {
279
- select?: SelectRule | "*" | false | null;
280
- insert?: InsertRule | "*" | false | null;
281
- update?: UpdateRule | "*" | false | null;
282
- delete?: DeleteRule | "*" | false | null;
283
- sync?: SyncRule;
284
- subscribe?: SubscribeRule | "*";
285
- };
286
- export type PublishViewRule = {
287
- select: SelectRule | "*" | false | null;
288
- };
289
- // export type Publish = {
290
- // tablesOrViews: {[key:string]: TableRule | ViewRule | "*" }
291
- // }
292
- export type RequestParams = { dbo?: DbHandler, socket?: any };
293
- export type PublishAllOrNothing = "*" | false | null;
294
- export type PublishObject = {
295
- [table_name: string]: (PublishTableRule | PublishViewRule | PublishAllOrNothing )
296
- };
297
- export type PublishTable = {
298
- [table_name: string]: (PublishTableRule | PublishViewRule)
299
- };
300
- export type PublishedResult = PublishAllOrNothing | PublishObject ;
301
- export type PublishParams<DBO = DbHandler> = {
302
- sid?: string;
303
- dbo?: DBO;
304
- db?: DB;
305
- user?: AnyObject;
306
- socket: PRGLIOSocket
307
- }
308
- export type Publish<DBO> = PublishedResult | ((params: PublishParams<DBO>) => (PublishedResult | Promise<PublishedResult>));
309
-
310
- export type Method = (...args: any) => ( any | Promise<any> );
311
79
  export const JOIN_TYPES = ["one-many", "many-one", "one-one", "many-many"] as const;
312
80
  export type Join = {
313
81
  tables: [string, string];
@@ -316,7 +84,6 @@ export type Join = {
316
84
  };
317
85
  export type Joins = Join[] | "inferred";
318
86
 
319
- export type PublishMethods<DBO> = (params: PublishParams<DBO>) => { [key:string]: Method } | Promise<{ [key:string]: Method }>;
320
87
 
321
88
 
322
89
  type Keywords = {
@@ -398,23 +165,23 @@ export type FileTableConfig = {
398
165
  imageOptions?: ImageOptions
399
166
  };
400
167
 
401
- export type ProstglesInitOptions<DBO = DbHandler> = {
168
+ export type ProstglesInitOptions<S extends DBSchema = never> = {
402
169
  dbConnection: DbConnection;
403
170
  dbOptions?: DbConnectionOpts;
404
171
  tsGeneratedTypesDir?: string;
405
172
  io?: any;
406
- publish?: Publish<DBO>;
407
- publishMethods?: PublishMethods<DBO>;
408
- publishRawSQL?(params: PublishParams<DBO>): ( (boolean | "*") | Promise<(boolean | "*")>);
173
+ publish?: Publish<S>;
174
+ publishMethods?: PublishMethods<S>;
175
+ publishRawSQL?(params: PublishParams<S>): ( (boolean | "*") | Promise<(boolean | "*")>);
409
176
  joins?: Joins;
410
177
  schema?: string;
411
178
  sqlFilePath?: string;
412
- onReady(dbo: DBO, db: DB): void;
179
+ onReady(dbo: DBOFullyTyped<S>, db: DB): void;
413
180
  transactions?: string | boolean;
414
181
  wsChannelNamePrefix?: string;
415
- onSocketConnect?(socket: PRGLIOSocket, dbo: DBO, db?: DB): any;
416
- onSocketDisconnect?(socket: PRGLIOSocket, dbo: DBO, db?: DB): any;
417
- auth?: Auth<DBO>;
182
+ onSocketConnect?(socket: PRGLIOSocket, dbo: DBOFullyTyped<S>, db?: DB): any;
183
+ onSocketDisconnect?(socket: PRGLIOSocket, dbo: DBOFullyTyped<S>, db?: DB): any;
184
+ auth?: Auth<S>;
418
185
  DEBUG_MODE?: boolean;
419
186
  watchSchemaType?:
420
187
 
@@ -466,7 +233,7 @@ export type ProstglesInitOptions<DBO = DbHandler> = {
466
233
  */
467
234
 
468
235
  export type OnReady = {
469
- dbo: DbHandler;
236
+ dbo: DBHandlerServer;
470
237
  db: DB;
471
238
  }
472
239
 
@@ -478,9 +245,10 @@ const DEFAULT_KEYWORDS = {
478
245
  };
479
246
 
480
247
  import * as fs from 'fs';
481
- export class Prostgles<DBO = DbHandler> {
248
+ import { DBOFullyTyped } from "./DBSchemaBuilder";
249
+ export class Prostgles<S extends DBSchema = any> {
482
250
 
483
- opts: ProstglesInitOptions<DBO> = {
251
+ opts: ProstglesInitOptions<S> = {
484
252
  DEBUG_MODE: false,
485
253
  dbConnection: {
486
254
  host: "localhost",
@@ -501,7 +269,7 @@ export class Prostgles<DBO = DbHandler> {
501
269
  // dbOptions: DbConnectionOpts;
502
270
  db?: DB;
503
271
  pgp?: PGP;
504
- dbo?: DbHandler;
272
+ dbo?: DBHandlerServer;
505
273
  _dboBuilder?: DboBuilder;
506
274
  get dboBuilder(): DboBuilder {
507
275
  if(!this._dboBuilder) throw "get dboBuilder: it's undefined"
@@ -512,7 +280,7 @@ export class Prostgles<DBO = DbHandler> {
512
280
  }
513
281
  publishParser?: PublishParser;
514
282
 
515
- authHandler?: AuthHandler;
283
+ authHandler?: AuthHandler<S>;
516
284
 
517
285
 
518
286
  keywords = DEFAULT_KEYWORDS;
@@ -641,8 +409,8 @@ export class Prostgles<DBO = DbHandler> {
641
409
 
642
410
  isSuperUser = false;
643
411
  schema_checkIntervalMillis: any;
644
- async init(onReady: (dbo: DBO, db: DB) => any): Promise<{
645
- db: DbHandler;
412
+ async init(onReady: (dbo: DBOFullyTyped<S>, db: DB) => any): Promise<{
413
+ db: DBOFullyTyped<S>;
646
414
  _db: DB;
647
415
  pgp: PGP;
648
416
  io?: any;
@@ -766,7 +534,7 @@ export class Prostgles<DBO = DbHandler> {
766
534
 
767
535
  this.loaded = true;
768
536
  return {
769
- db: this.dbo!,
537
+ db: this.dbo! as any,
770
538
  _db: db,
771
539
  pgp,
772
540
  io: this.opts.io,
@@ -935,24 +703,26 @@ export class Prostgles<DBO = DbHandler> {
935
703
  // let DATA_TYPES = !needType? [] : await this.db.any("SELECT oid, typname FROM pg_type");
936
704
  // let USER_TABLES = !needType? [] : await this.db.any("SELECT relid, relname FROM pg_catalog.pg_statio_user_tables");
937
705
 
938
- let schema: TableSchemaForClient = {};
706
+ const { dbo, db, pgp, publishParser } = this;
707
+ let fullSchema: {
708
+ schema: TableSchemaForClient;
709
+ tables: DBSchemaTable[];
710
+ } | undefined;
939
711
  let publishValidationError;
940
712
  let rawSQL = false;
941
713
 
942
- const { dbo, db, pgp, publishParser } = this;
943
714
  try {
944
715
  if(!publishParser) throw "publishParser undefined";
945
- schema = await publishParser.getSchemaFromPublish(socket);
716
+ fullSchema = await publishParser.getSchemaFromPublish(socket);
946
717
  } catch(e){
947
718
  publishValidationError = "Server Error: PUBLISH VALIDATION ERROR";
948
719
  console.error(`\nProstgles PUBLISH VALIDATION ERROR (after socket connected):\n ->`, e);
949
720
  }
950
721
  socket.prostgles = socket.prostgles || {};
951
- socket.prostgles.schema = schema;
722
+ socket.prostgles.schema = fullSchema?.schema;
952
723
  /* RUN Raw sql from client IF PUBLISHED
953
724
  */
954
- let fullSchema: TableSchema[] = [];
955
- let allTablesViews = this.dboBuilder.tablesOrViews ?? [];
725
+
956
726
  if(this.opts.publishRawSQL && typeof this.opts.publishRawSQL === "function"){
957
727
  const canRunSQL = async () => {
958
728
  const publishParams = await this.publishParser?.getPublishParams({ socket })
@@ -974,13 +744,13 @@ export class Prostgles<DBO = DbHandler> {
974
744
  });
975
745
  if(db){
976
746
  // let allTablesViews = await db.any(STEP2_GET_ALL_TABLES_AND_COLUMNS);
977
- fullSchema = allTablesViews;
747
+ // fullSchema = allTablesViews;
978
748
  rawSQL = true;
979
749
  } else console.error("db missing");
980
750
  }
981
751
  }
982
752
 
983
- // let joinTables = [];
753
+ const { schema, tables } = fullSchema ?? { schema: {}, tables: [] };
984
754
  let joinTables2: string[][] = [];
985
755
  if(this.opts.joins){
986
756
  // joinTables = Array.from(new Set(flat(this.dboBuilder.getJoins().map(j => j.tables)).filter(t => schema[t])));
@@ -999,7 +769,7 @@ export class Prostgles<DBO = DbHandler> {
999
769
  const clientSchema: ClientSchema = {
1000
770
  schema,
1001
771
  methods: getKeys(methods),
1002
- ...(fullSchema? { fullSchema } : {}),
772
+ tableSchema: tables,
1003
773
  rawSQL,
1004
774
  joinTables: joinTables2,
1005
775
  auth,
@@ -1031,95 +801,6 @@ type SocketMethodRequest = {
1031
801
  params: any;
1032
802
  }
1033
803
 
1034
-
1035
- type Request = {
1036
- socket?: any;
1037
- httpReq?: any;
1038
- }
1039
-
1040
- type DboTable = Request & {
1041
- tableName: string;
1042
- localParams: LocalParams;
1043
- }
1044
- type DboTableCommand = Request & DboTable & {
1045
- command: string;
1046
- localParams: LocalParams;
1047
- }
1048
-
1049
- const RULE_TO_METHODS = [
1050
- {
1051
- rule: "getColumns",
1052
- sqlRule: "select",
1053
- methods: RULE_METHODS.getColumns,
1054
- no_limits: true,
1055
- allowed_params: [],
1056
- table_only: false,
1057
- hint: ` expecting false | true | undefined`
1058
- },
1059
- {
1060
- rule: "getInfo",
1061
- sqlRule: "select",
1062
- methods: RULE_METHODS.getInfo,
1063
- no_limits: true,
1064
- allowed_params: [],
1065
- table_only: false,
1066
- hint: ` expecting false | true | undefined`
1067
- },
1068
- {
1069
- rule: "insert",
1070
- sqlRule: "insert",
1071
- methods: RULE_METHODS.insert,
1072
- no_limits: <SelectRule>{ fields: "*" },
1073
- table_only: true,
1074
- allowed_params: <Array<keyof InsertRule>>["fields", "forcedData", "returningFields", "validate", "preValidate"] ,
1075
- hint: ` expecting "*" | true | { fields: string | string[] | {} }`
1076
- },
1077
- {
1078
- rule: "update",
1079
- sqlRule: "update",
1080
- methods: RULE_METHODS.update,
1081
- no_limits: <UpdateRule>{ fields: "*", filterFields: "*", returningFields: "*" },
1082
- table_only: true,
1083
- allowed_params: <Array<keyof UpdateRule>>["fields", "filterFields", "forcedFilter", "forcedData", "returningFields", "validate", "dynamicFields"] ,
1084
- hint: ` expecting "*" | true | { fields: string | string[] | {} }`
1085
- },
1086
- {
1087
- rule: "select",
1088
- sqlRule: "select",
1089
- methods: RULE_METHODS.select,
1090
- no_limits: <SelectRule>{ fields: "*", filterFields: "*" },
1091
- table_only: false,
1092
- allowed_params: <Array<keyof SelectRule>>["fields", "filterFields", "forcedFilter", "validate", "maxLimit"] ,
1093
- hint: ` expecting "*" | true | { fields: ( string | string[] | {} ) }`
1094
- },
1095
- {
1096
- rule: "delete",
1097
- sqlRule: "delete",
1098
- methods: RULE_METHODS.delete,
1099
- no_limits: <DeleteRule>{ filterFields: "*" } ,
1100
- table_only: true,
1101
- allowed_params: <Array<keyof DeleteRule>>["filterFields", "forcedFilter", "returningFields", "validate"] ,
1102
- hint: ` expecting "*" | true | { filterFields: ( string | string[] | {} ) } \n Will use "select", "update", "delete" and "insert" rules`
1103
- },
1104
- {
1105
- rule: "sync",
1106
- sqlRule: "select",
1107
- methods: RULE_METHODS.sync,
1108
- no_limits: null,
1109
- table_only: true,
1110
- allowed_params: <Array<keyof SyncRule>>["id_fields", "synced_field", "sync_type", "allow_delete", "throttle", "batch_size"],
1111
- hint: ` expecting "*" | true | { id_fields: string[], synced_field: string }`
1112
- },
1113
- {
1114
- rule: "subscribe",
1115
- sqlRule: "select",
1116
- methods: RULE_METHODS.subscribe,
1117
- no_limits: <SubscribeRule>{ throttle: 0 },
1118
- table_only: true,
1119
- allowed_params: <Array<keyof SubscribeRule>>["throttle"],
1120
- hint: ` expecting "*" | true | { throttle: number } \n Will use "select" rules`
1121
- }
1122
- ] as const;
1123
804
  // const ALL_PUBLISH_METHODS = ["update", "upsert", "delete", "insert", "find", "findOne", "subscribe", "unsubscribe", "sync", "unsync", "remove"];
1124
805
  // const ALL_PUBLISH_METHODS = RULE_TO_METHODS.map(r => r.methods).flat();
1125
806
 
@@ -1132,359 +813,7 @@ const RULE_TO_METHODS = [
1132
813
  // return res;
1133
814
  // }
1134
815
 
1135
- export class PublishParser {
1136
- publish: any;
1137
- publishMethods?: any;
1138
- publishRawSQL?: any;
1139
- dbo: DbHandler;
1140
- db: DB
1141
- prostgles: Prostgles;
1142
-
1143
- constructor(publish: any, publishMethods: any, publishRawSQL: any, dbo: DbHandler, db: DB, prostgles: Prostgles){
1144
- this.publish = publish;
1145
- this.publishMethods = publishMethods;
1146
- this.publishRawSQL = publishRawSQL;
1147
- this.dbo = dbo;
1148
- this.db = db;
1149
- this.prostgles = prostgles;
1150
-
1151
- if(!this.dbo || !this.publish) throw "INTERNAL ERROR: dbo and/or publish missing";
1152
- }
1153
-
1154
- async getPublishParams(localParams: LocalParams, clientInfo?: ClientInfo): Promise<PublishParams> {
1155
- return {
1156
- ...(clientInfo || await this.prostgles.authHandler?.getClientInfo(localParams)),
1157
- dbo: this.dbo,
1158
- db: this.db,
1159
- socket: localParams.socket!
1160
- }
1161
- }
1162
-
1163
- async getMethods(socket: any){
1164
- let methods = {};
1165
-
1166
- const publishParams = await this.getPublishParams({ socket });
1167
- const _methods = await applyParamsIfFunc(this.publishMethods, publishParams);
1168
-
1169
- if(_methods && Object.keys(_methods).length){
1170
- getKeys(_methods).map(key => {
1171
- if(_methods[key] && (typeof _methods[key] === "function" || typeof _methods[key].then === "function")){
1172
- //@ts-ignore
1173
- methods[key] = _methods[key];
1174
- } else {
1175
- throw `invalid publishMethods item -> ${key} \n Expecting a function or promise`
1176
- }
1177
- });
1178
- }
1179
-
1180
- return methods;
1181
- }
1182
-
1183
- /**
1184
- * Parses the first level of publish. (If false then nothing if * then all tables and views)
1185
- * @param socket
1186
- * @param user
1187
- */
1188
- async getPublish(localParams: LocalParams, clientInfo?: ClientInfo): Promise<PublishObject> {
1189
- const publishParams: PublishParams = await this.getPublishParams(localParams, clientInfo)
1190
- let _publish = await applyParamsIfFunc(this.publish, publishParams );
1191
-
1192
- if(_publish === "*"){
1193
- let publish = {} as any;
1194
- this.prostgles.dboBuilder.tablesOrViews?.map(tov => {
1195
- publish[tov.name] = "*";
1196
- });
1197
- return publish;
1198
- }
1199
-
1200
- return _publish;
1201
- }
1202
- async getValidatedRequestRuleWusr({ tableName, command, localParams }: DboTableCommand): Promise<TableRule>{
1203
- const clientInfo = await this.prostgles.authHandler!.getClientInfo(localParams);
1204
- return await this.getValidatedRequestRule({ tableName, command, localParams }, clientInfo);
1205
- }
1206
-
1207
- async getValidatedRequestRule({ tableName, command, localParams }: DboTableCommand, clientInfo?: ClientInfo): Promise<TableRule>{
1208
- if(!this.dbo) throw "INTERNAL ERROR: dbo is missing";
1209
-
1210
- if(!command || !tableName) throw "command OR tableName are missing";
1211
816
 
1212
- let rtm = RULE_TO_METHODS.find(rtms => (rtms.methods as any).includes(command));
1213
- if(!rtm){
1214
- throw "Invalid command: " + command;
1215
- }
1216
-
1217
- /* Must be local request -> allow everything */
1218
- if(!localParams || (!localParams.socket && !localParams.httpReq)){
1219
- return RULE_TO_METHODS.reduce((a, v) => ({
1220
- ...a,
1221
- [v.rule]: v.no_limits
1222
- }), {})
1223
- }
1224
-
1225
- /* Must be from socket. Must have a publish */
1226
- if(!this.publish) throw "publish is missing";
1227
-
1228
- /* Get any publish errors for socket */
1229
- const schm = localParams?.socket?.prostgles?.schema?.[tableName]?.[command];
1230
-
1231
- if(schm && schm.err) throw schm.err;
1232
-
1233
- let table_rule = await this.getTableRules({ tableName, localParams }, clientInfo);
1234
- if(!table_rule) throw "Invalid or disallowed table: " + tableName;
1235
-
1236
-
1237
- if(command === "upsert"){
1238
- if(!table_rule.update || !table_rule.insert){
1239
- throw `Invalid or disallowed command: upsert`;
1240
- }
1241
- }
1242
-
1243
- if(rtm && table_rule && table_rule[rtm.rule]){
1244
- return table_rule;
1245
- } else throw `Invalid or disallowed command: ${tableName}.${command}`;
1246
- }
1247
-
1248
- async getTableRules({ tableName, localParams }: DboTable, clientInfo?: ClientInfo): Promise<PublishTable> {
1249
-
1250
- try {
1251
- if(!localParams || !tableName) throw "publish OR socket OR dbo OR tableName are missing";
1252
-
1253
- let _publish = await this.getPublish(localParams, clientInfo);
1254
-
1255
- let table_rules = _publish[tableName];// applyParamsIfFunc(_publish[tableName], localParams, this.dbo, this.db, user);
1256
-
1257
- /* Get view or table specific rules */
1258
- const tHandler = (this.dbo[tableName] as TableHandler | ViewHandler);
1259
-
1260
- const is_view = tHandler.is_view,
1261
- MY_RULES = RULE_TO_METHODS.filter(r => !is_view || !r.table_only);
1262
-
1263
- // if(tableName === "various") console.warn(1033, MY_RULES)
1264
- if(table_rules){
1265
-
1266
- /* All methods allowed. Add no limits for table rules */
1267
- if([true, "*"].includes(table_rules as any)){
1268
- table_rules = {};
1269
- MY_RULES.map(r => {
1270
- /** Check PG User privileges */
1271
- if(
1272
- tHandler.tableOrViewInfo.privileges[r.sqlRule]
1273
- ){
1274
- // @ts-ignore
1275
- table_rules[r.rule] = { ...(r as any).no_limits } as any;
1276
- }
1277
-
1278
-
1279
- });
1280
- // if(tableName === "various") console.warn(1042, table_rules)
1281
- }
1282
-
1283
- /* Add missing implied rules */
1284
- MY_RULES.map(r => {
1285
-
1286
- if(["getInfo", "getColumns"].includes(r.rule) && ![null, false, 0].includes((table_rules as any)[r.rule])){
1287
- // @ts-ignore
1288
- table_rules[r.rule] = r.no_limits;
1289
- return ;
1290
- }
1291
-
1292
- /* Add nested properties for fully allowed rules */
1293
- // @ts-ignore
1294
- if ([true, "*"].includes(table_rules[r.rule]) && r.no_limits) {
1295
- // @ts-ignore
1296
- table_rules[r.rule] = Object.assign({}, r.no_limits);
1297
- }
1298
-
1299
- // @ts-ignore
1300
- if(table_rules[r.rule]){
1301
- /* Add implied methods if not falsy */
1302
- // @ts-ignore
1303
- r.methods.forEach(method => {
1304
- // @ts-ignore
1305
- if(table_rules[method] === undefined){
1306
- const publishedTable = (table_rules as PublishTable);
1307
- if(method === "updateBatch" && !publishedTable.update){
1308
-
1309
- } else if(method === "upsert" && (!publishedTable.update || !publishedTable.insert)){
1310
- // return;
1311
- } else {
1312
- // @ts-ignore
1313
- table_rules[method] = {};
1314
- }
1315
- }
1316
- });
1317
- }
1318
- // if(tableName === "v_various") console.warn(table_rules, r)
1319
- });
1320
-
1321
- /*
1322
- Add defaults
1323
- Check for invalid params
1324
- */
1325
- if(table_rules && getKeys(table_rules).length && table_rules !== "*"){
1326
- const ruleKeys = getKeys(table_rules as PublishTableRule | PublishViewRule)
1327
-
1328
- // @ts-ignore
1329
- ruleKeys.filter(m => table_rules[m])
1330
- .find(method => {
1331
- let rm = MY_RULES.find(r => r.rule === method || (r.methods as any).includes(method));
1332
- if(!rm){
1333
- throw `Invalid rule in publish.${tableName} -> ${method} \nExpecting any of: ${MY_RULES.flatMap(r => [r.rule, ...r.methods]).join(", ")}`;
1334
- }
1335
-
1336
- /** Check user privileges */
1337
- if(!tHandler.tableOrViewInfo.privileges[rm.sqlRule]){
1338
- // @ts-ignore
1339
- delete table_rules![method];
1340
- return;
1341
- }
1342
-
1343
- /* Check RULES for invalid params */
1344
- /* Methods do not have params -> They use them from rules */
1345
- if(method === rm.rule){
1346
- // @ts-ignore
1347
- let method_params = getKeys(table_rules![method]);
1348
- let iparam = method_params.find(p => !rm?.allowed_params.includes(<never>p));
1349
- if(iparam){
1350
- throw `Invalid setting in publish.${tableName}.${method} -> ${iparam}. \n Expecting any of: ${rm.allowed_params.join(", ")}`;
1351
- }
1352
- }
1353
-
1354
- /* Add default params (if missing) */
1355
- // @ts-ignore
1356
- if(method === "sync"){
1357
-
1358
- // @ts-ignore
1359
- if([true, "*"].includes(table_rules![method])){
1360
- throw "Invalid sync rule. Expecting { id_fields: string[], synced_field: string } ";
1361
- }
1362
- if(typeof get(table_rules, [method, "throttle"]) !== "number"){
1363
- // @ts-ignore
1364
- table_rules![method].throttle = 100;
1365
- }
1366
- if(typeof get(table_rules, [method, "batch_size"]) !== "number"){
1367
- // @ts-ignore
1368
- table_rules![method].batch_size = DEFAULT_SYNC_BATCH_SIZE;
1369
- }
1370
- }
1371
-
1372
- /* Enable subscribe if not explicitly disabled */
1373
- // @ts-ignore
1374
- if(method === "select" && !ruleKeys.includes("subscribe")){
1375
- const sr = MY_RULES.find(r => r.rule === "subscribe");
1376
- if(sr){
1377
- // @ts-ignore
1378
- table_rules[sr.rule] = { ...sr.no_limits };
1379
- // @ts-ignore
1380
- (table_rules as PublishTable).subscribeOne = { ...sr.no_limits };
1381
- }
1382
- }
1383
- });
1384
- }
1385
- }
1386
-
1387
- return table_rules as PublishTable;
1388
- } catch (e) {
1389
- throw e;
1390
- }
1391
- }
1392
-
1393
-
1394
-
1395
- /* Prepares schema for client. Only allowed views and commands will be present */
1396
- async getSchemaFromPublish(socket: any): Promise<TableSchemaForClient> {
1397
- let schema: TableSchemaForClient = {};
1398
-
1399
- try {
1400
- /* Publish tables and views based on socket */
1401
- const clientInfo = await this.prostgles.authHandler?.getClientInfo({ socket });
1402
- let _publish = await this.getPublish(socket, clientInfo);
1403
-
1404
-
1405
- if(_publish && Object.keys(_publish).length){
1406
- let txKey = "tx";
1407
- if(!this.prostgles.opts.transactions) txKey = "";
1408
- if(typeof this.prostgles.opts.transactions === "string") txKey = this.prostgles.opts.transactions;
1409
-
1410
- const tableNames = Object.keys(_publish).filter(k => !txKey || txKey !== k);
1411
-
1412
- await Promise.all(tableNames
1413
- .map(async tableName => {
1414
- if(!this.dbo[tableName]) {
1415
- throw `Table ${tableName} does not exist
1416
- Expecting one of: ${this.prostgles.dboBuilder.tablesOrViews?.map(tov => tov.name).join(", ")}
1417
- DBO tables: ${Object.keys(this.dbo).filter(k => (this.dbo[k] as any).find).join(", ")}
1418
- `;
1419
- }
1420
-
1421
- const table_rules = await this.getTableRules({ localParams: {socket}, tableName }, clientInfo);
1422
-
1423
- // if(tableName === "insert_rule") throw {table_rules}
1424
- if(table_rules && Object.keys(table_rules).length){
1425
- schema[tableName] = {};
1426
- let methods: Array<MethodKey> = [];
1427
-
1428
- if(typeof table_rules === "object"){
1429
- methods = getKeys(table_rules) as any;
1430
- }
1431
-
1432
- await Promise.all(methods.filter(m => m !== "select" as any).map(async method => {
1433
- if(method === "sync" && table_rules[method]){
1434
-
1435
- /* Pass sync info */
1436
- schema[tableName][method] = table_rules[method];
1437
- } else if(table_rules[method]) {
1438
-
1439
- schema[tableName][method] = {};
1440
-
1441
- /* Test for issues with the publish rules */
1442
- if(TABLE_METHODS.includes(method)){
1443
-
1444
- let err = null;
1445
- try {
1446
- let valid_table_command_rules = await this.getValidatedRequestRule({ tableName, command: method, localParams: {socket} }, clientInfo);
1447
- await (this.dbo[tableName] as any)[method]({}, {}, {}, valid_table_command_rules, { socket, has_rules: true, testRule: true });
1448
-
1449
- } catch(e) {
1450
- err = "INTERNAL PUBLISH ERROR";
1451
- schema[tableName][method] = { err };
1452
-
1453
- throw `publish.${tableName}.${method}: \n -> ${e}`;
1454
- }
1455
- }
1456
- }
1457
- }));
1458
- }
1459
-
1460
- return true;
1461
- })
1462
- );
1463
- }
1464
-
1465
-
1466
- } catch (e) {
1467
- console.error("Prostgles \nERRORS IN PUBLISH: ", JSON.stringify(e));
1468
- throw e;
1469
- }
1470
-
1471
- return schema;
1472
- }
1473
-
1474
- }
1475
-
1476
-
1477
-
1478
- function applyParamsIfFunc(maybeFunc: any, ...params: any): any{
1479
- if(
1480
- (maybeFunc !== null && maybeFunc !== undefined) &&
1481
- (typeof maybeFunc === "function" || typeof maybeFunc.then === "function")
1482
- ){
1483
- return maybeFunc(...params);
1484
- }
1485
-
1486
- return maybeFunc;
1487
- }
1488
817
 
1489
818
  export async function isSuperUser(db: DB): Promise<boolean>{
1490
819
  return db.oneOrNone("select usesuper from pg_user where usename = CURRENT_USER;").then(r => r.usesuper);