prostgles-server 4.2.159 → 4.2.161

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 (107) hide show
  1. package/dist/Auth/setEmailProvider.js +2 -2
  2. package/dist/Auth/setEmailProvider.js.map +1 -1
  3. package/lib/Auth/AuthHandler.ts +436 -0
  4. package/lib/Auth/AuthTypes.ts +280 -0
  5. package/lib/Auth/getSafeReturnURL.ts +35 -0
  6. package/lib/Auth/sendEmail.ts +83 -0
  7. package/lib/Auth/setAuthProviders.ts +128 -0
  8. package/lib/Auth/setEmailProvider.ts +85 -0
  9. package/lib/Auth/setupAuthRoutes.ts +161 -0
  10. package/lib/DBEventsManager.ts +178 -0
  11. package/lib/DBSchemaBuilder.ts +225 -0
  12. package/lib/DboBuilder/DboBuilder.ts +319 -0
  13. package/lib/DboBuilder/DboBuilderTypes.ts +361 -0
  14. package/lib/DboBuilder/QueryBuilder/Functions.ts +1153 -0
  15. package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +288 -0
  16. package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +263 -0
  17. package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +271 -0
  18. package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +136 -0
  19. package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +22 -0
  20. package/lib/DboBuilder/QueryStreamer.ts +250 -0
  21. package/lib/DboBuilder/TableHandler/DataValidator.ts +428 -0
  22. package/lib/DboBuilder/TableHandler/TableHandler.ts +205 -0
  23. package/lib/DboBuilder/TableHandler/delete.ts +115 -0
  24. package/lib/DboBuilder/TableHandler/insert.ts +183 -0
  25. package/lib/DboBuilder/TableHandler/insertTest.ts +78 -0
  26. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +62 -0
  27. package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +134 -0
  28. package/lib/DboBuilder/TableHandler/update.ts +126 -0
  29. package/lib/DboBuilder/TableHandler/updateBatch.ts +49 -0
  30. package/lib/DboBuilder/TableHandler/updateFile.ts +48 -0
  31. package/lib/DboBuilder/TableHandler/upsert.ts +34 -0
  32. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +393 -0
  33. package/lib/DboBuilder/ViewHandler/count.ts +38 -0
  34. package/lib/DboBuilder/ViewHandler/find.ts +153 -0
  35. package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +73 -0
  36. package/lib/DboBuilder/ViewHandler/getExistsFilters.ts +74 -0
  37. package/lib/DboBuilder/ViewHandler/getInfo.ts +32 -0
  38. package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +84 -0
  39. package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +96 -0
  40. package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +105 -0
  41. package/lib/DboBuilder/ViewHandler/parseJoinPath.ts +208 -0
  42. package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +163 -0
  43. package/lib/DboBuilder/ViewHandler/prepareWhere.ts +90 -0
  44. package/lib/DboBuilder/ViewHandler/size.ts +37 -0
  45. package/lib/DboBuilder/ViewHandler/subscribe.ts +118 -0
  46. package/lib/DboBuilder/ViewHandler/validateViewRules.ts +70 -0
  47. package/lib/DboBuilder/dboBuilderUtils.ts +222 -0
  48. package/lib/DboBuilder/getColumns.ts +114 -0
  49. package/lib/DboBuilder/getCondition.ts +201 -0
  50. package/lib/DboBuilder/getSubscribeRelatedTables.ts +190 -0
  51. package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +426 -0
  52. package/lib/DboBuilder/insertNestedRecords.ts +355 -0
  53. package/lib/DboBuilder/parseUpdateRules.ts +187 -0
  54. package/lib/DboBuilder/prepareShortestJoinPaths.ts +186 -0
  55. package/lib/DboBuilder/runSQL.ts +182 -0
  56. package/lib/DboBuilder/runTransaction.ts +50 -0
  57. package/lib/DboBuilder/sqlErrCodeToMsg.ts +254 -0
  58. package/lib/DboBuilder/uploadFile.ts +69 -0
  59. package/lib/Event_Trigger_Tags.ts +118 -0
  60. package/lib/FileManager/FileManager.ts +358 -0
  61. package/lib/FileManager/getValidatedFileType.ts +69 -0
  62. package/lib/FileManager/initFileManager.ts +187 -0
  63. package/lib/FileManager/upload.ts +62 -0
  64. package/lib/FileManager/uploadStream.ts +79 -0
  65. package/lib/Filtering.ts +463 -0
  66. package/lib/JSONBValidation/validate_jsonb_schema_sql.ts +502 -0
  67. package/lib/JSONBValidation/validation.ts +143 -0
  68. package/lib/Logging.ts +127 -0
  69. package/lib/PostgresNotifListenManager.ts +143 -0
  70. package/lib/Prostgles.ts +485 -0
  71. package/lib/ProstglesTypes.ts +196 -0
  72. package/lib/PubSubManager/PubSubManager.ts +609 -0
  73. package/lib/PubSubManager/addSub.ts +138 -0
  74. package/lib/PubSubManager/addSync.ts +141 -0
  75. package/lib/PubSubManager/getCreatePubSubManagerError.ts +72 -0
  76. package/lib/PubSubManager/getPubSubManagerInitQuery.ts +662 -0
  77. package/lib/PubSubManager/initPubSubManager.ts +79 -0
  78. package/lib/PubSubManager/notifListener.ts +173 -0
  79. package/lib/PubSubManager/orphanTriggerCheck.ts +70 -0
  80. package/lib/PubSubManager/pushSubData.ts +55 -0
  81. package/lib/PublishParser/PublishParser.ts +162 -0
  82. package/lib/PublishParser/getFileTableRules.ts +124 -0
  83. package/lib/PublishParser/getSchemaFromPublish.ts +141 -0
  84. package/lib/PublishParser/getTableRulesWithoutFileTable.ts +177 -0
  85. package/lib/PublishParser/publishTypesAndUtils.ts +399 -0
  86. package/lib/RestApi.ts +127 -0
  87. package/lib/SchemaWatch/SchemaWatch.ts +90 -0
  88. package/lib/SchemaWatch/createSchemaWatchEventTrigger.ts +3 -0
  89. package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +45 -0
  90. package/lib/SchemaWatch/getWatchSchemaTagList.ts +27 -0
  91. package/lib/SyncReplication.ts +557 -0
  92. package/lib/TableConfig/TableConfig.ts +468 -0
  93. package/lib/TableConfig/getColumnDefinitionQuery.ts +111 -0
  94. package/lib/TableConfig/getConstraintDefinitionQueries.ts +95 -0
  95. package/lib/TableConfig/getFutureTableSchema.ts +64 -0
  96. package/lib/TableConfig/getPGIndexes.ts +53 -0
  97. package/lib/TableConfig/getTableColumnQueries.ts +129 -0
  98. package/lib/TableConfig/initTableConfig.ts +326 -0
  99. package/lib/index.ts +13 -0
  100. package/lib/initProstgles.ts +319 -0
  101. package/lib/onSocketConnected.ts +102 -0
  102. package/lib/runClientRequest.ts +129 -0
  103. package/lib/shortestPath.ts +122 -0
  104. package/lib/typeTests/DBoGenerated.d.ts +320 -0
  105. package/lib/typeTests/dboTypeCheck.ts +81 -0
  106. package/lib/utils.ts +15 -0
  107. package/package.json +1 -1
@@ -0,0 +1,399 @@
1
+ import { AnyObject, DBSchema, FullFilter, Method, RULE_METHODS } from "prostgles-types";
2
+ import type { DBOFullyTyped, PublishFullyTyped } from "../DBSchemaBuilder";
3
+ import { CommonTableRules, Filter, LocalParams, PRGLIOSocket, TableOrViewInfo } from "../DboBuilder/DboBuilder";
4
+ import { DB, DBHandlerServer } from "../Prostgles";
5
+
6
+ export type PublishMethods<S = void, SUser extends SessionUser = SessionUser> = (params: PublishParams<S, SUser>) => { [key: string]: Method } | Promise<{ [key: string]: Method } | null>;
7
+
8
+ export type Awaitable<T> = T | Promise<T>;
9
+
10
+ type Request = {
11
+ socket?: any;
12
+ httpReq?: any;
13
+ }
14
+
15
+ export type DboTable = Request & {
16
+ tableName: string;
17
+ localParams: LocalParams;
18
+ }
19
+ export type DboTableCommand = Request & DboTable & {
20
+ command: string;
21
+ localParams: LocalParams;
22
+ }
23
+
24
+ export const RULE_TO_METHODS = [
25
+ {
26
+ rule: "getColumns",
27
+ sqlRule: "select",
28
+ methods: RULE_METHODS.getColumns,
29
+ no_limits: true,
30
+ allowed_params: [],
31
+ table_only: false,
32
+ hint: ` expecting false | true | undefined`
33
+ },
34
+ {
35
+ rule: "getInfo",
36
+ sqlRule: "select",
37
+ methods: RULE_METHODS.getInfo,
38
+ no_limits: true,
39
+ allowed_params: [],
40
+ table_only: false,
41
+ hint: ` expecting false | true | undefined`
42
+ },
43
+ {
44
+ rule: "insert",
45
+ sqlRule: "insert",
46
+ methods: RULE_METHODS.insert,
47
+ no_limits: <SelectRule>{ fields: "*" },
48
+ table_only: true,
49
+ allowed_params: { checkFilter: 1, fields: 1, forcedData: 1, postValidate: 1, preValidate: 1, returningFields: 1, validate: 1, allowedNestedInserts: 1 } satisfies Record<keyof InsertRule, 1>,
50
+ hint: ` expecting "*" | true | { fields: string | string[] | {} }`
51
+ },
52
+ {
53
+ rule: "update",
54
+ sqlRule: "update",
55
+ methods: RULE_METHODS.update,
56
+ no_limits: <UpdateRule>{ fields: "*", filterFields: "*", returningFields: "*" },
57
+ table_only: true,
58
+ allowed_params: { checkFilter: 1, dynamicFields: 1, fields: 1, filterFields: 1, forcedData: 1, forcedFilter: 1, postValidate: 1, returningFields: 1, validate: 1, } satisfies Record<keyof UpdateRule, 1>,
59
+ hint: ` expecting "*" | true | { fields: string | string[] | {} }`
60
+ },
61
+ {
62
+ rule: "select",
63
+ sqlRule: "select",
64
+ methods: RULE_METHODS.select,
65
+ no_limits: <SelectRule>{ fields: "*", filterFields: "*" },
66
+ table_only: false,
67
+ allowed_params: { fields: 1, filterFields: 1, forcedFilter: 1, maxLimit: 1, orderByFields: 1, validate: 1 } satisfies Record<keyof SelectRule, 1>,
68
+ hint: ` expecting "*" | true | { fields: ( string | string[] | {} ) }`
69
+ },
70
+ {
71
+ rule: "delete",
72
+ sqlRule: "delete",
73
+ methods: RULE_METHODS.delete,
74
+ no_limits: <DeleteRule>{ filterFields: "*" },
75
+ table_only: true,
76
+ allowed_params: { returningFields: 1, validate: 1, filterFields: 1, forcedFilter: 1 } satisfies Record<keyof DeleteRule, 1>,
77
+ hint: ` expecting "*" | true | { filterFields: ( string | string[] | {} ) } \n Will use "select", "update", "delete" and "insert" rules`
78
+ },
79
+ {
80
+ rule: "sync",
81
+ sqlRule: "select",
82
+ methods: RULE_METHODS.sync,
83
+ no_limits: null,
84
+ table_only: true,
85
+ allowed_params: { allow_delete: 1, batch_size: 1, id_fields: 1, synced_field: 1, throttle: 1 } satisfies Record<keyof SyncRule, 1>,
86
+ hint: ` expecting "*" | true | { id_fields: string[], synced_field: string }`
87
+ },
88
+ {
89
+ rule: "subscribe",
90
+ sqlRule: "select",
91
+ methods: RULE_METHODS.subscribe,
92
+ no_limits: <SubscribeRule>{ throttle: 0 },
93
+ table_only: false,
94
+ allowed_params: { throttle: 1 } satisfies Record<keyof SubscribeRule, 1>,
95
+ hint: ` expecting "*" | true | { throttle: number; throttleOpts?: { skipFirst?: boolean; } } \n Will use "select" rules`
96
+ }
97
+ ] as const;
98
+
99
+ import { FieldFilter, SelectParams } from "prostgles-types";
100
+ import { TableSchemaColumn } from "../DboBuilder/DboBuilderTypes";
101
+ import { SessionUser } from "../Auth/AuthTypes";
102
+
103
+ export type InsertRequestData = {
104
+ data: object | object[]
105
+ returning: FieldFilter;
106
+ }
107
+ export type SelectRequestData = {
108
+ filter: object;
109
+ params: SelectParams;
110
+ }
111
+ export type DeleteRequestData = {
112
+ filter: object;
113
+ returning: FieldFilter;
114
+ }
115
+ export type UpdateRequestDataOne<R extends AnyObject, S extends DBSchema | void = void> = {
116
+ filter: FullFilter<R, S>
117
+ data: Partial<R>;
118
+ returning: FieldFilter<R>;
119
+ }
120
+ export type UpdateReq<R extends AnyObject, S extends DBSchema | void = void> = {
121
+ filter: FullFilter<R, S>
122
+ data: Partial<R>;
123
+ }
124
+ export type UpdateRequestDataBatch<R extends AnyObject> = {
125
+ data: UpdateReq<R>[];
126
+ }
127
+ export type UpdateRequestData<R extends AnyObject = AnyObject> = UpdateRequestDataOne<R> | UpdateRequestDataBatch<R>;
128
+
129
+ export type ValidateRowArgs<R = AnyObject, DBX = DBHandlerServer> = {
130
+ row: R;
131
+ dbx: DBX;
132
+ localParams: LocalParams;
133
+ }
134
+ export type ValidateUpdateRowArgs<U = Partial<AnyObject>, F = Filter, DBX = DBHandlerServer> = {
135
+ update: U;
136
+ filter: F;
137
+ dbx: DBX;
138
+ localParams: LocalParams;
139
+ }
140
+ export type ValidateRow<R extends AnyObject = AnyObject, S = void> = (args: ValidateRowArgs<R, DBOFullyTyped<S>>) => R | Promise<R>;
141
+ export type PostValidateRow<R extends AnyObject = AnyObject, S = void> = (args: ValidateRowArgs<R, DBOFullyTyped<S>>) => void | Promise<void>;
142
+ export type PostValidateRowBasic = (args: ValidateRowArgs) => void | Promise<void>;
143
+ export type ValidateRowBasic = (args: ValidateRowArgs) => AnyObject | Promise<AnyObject>;
144
+ export type ValidateUpdateRow<R extends AnyObject = AnyObject, S extends DBSchema | void = void> = (args: ValidateUpdateRowArgs<Partial<R>, FullFilter<R, S>, DBOFullyTyped<S>>) => Partial<R> | Promise<Partial<R>>;
145
+ export type ValidateUpdateRowBasic = (args: ValidateUpdateRowArgs) => AnyObject | Promise<AnyObject>;
146
+
147
+
148
+ export type SelectRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
149
+
150
+ /**
151
+ * Fields allowed to be selected.
152
+ * Tip: Use false to exclude field
153
+ */
154
+ fields: FieldFilter<Cols>;
155
+
156
+ /**
157
+ * Fields allowed to sorted
158
+ * Defaults to the "fields". Use empty array/object to disallow sorting
159
+ */
160
+ orderByFields?: FieldFilter<Cols>;
161
+
162
+ /**
163
+ * 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
164
+ */
165
+ maxLimit?: number | null;
166
+
167
+ /**
168
+ * Filter added to every query (e.g. user_id) to restrict access
169
+ */
170
+ forcedFilter?: FullFilter<Cols, S>;
171
+
172
+ /**
173
+ * Fields user can filter by
174
+ * */
175
+ filterFields?: FieldFilter<Cols>;
176
+
177
+ /**
178
+ * Validation logic to check/update data for each request
179
+ */
180
+ validate?(args: SelectRequestData): SelectRequestData | Promise<SelectRequestData>;
181
+
182
+ }
183
+
184
+ export type CommonInsertUpdateRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
185
+
186
+ /**
187
+ * Filter that the new records must match or the update/insert will fail
188
+ * Similar to a policy WITH CHECK clause
189
+ */
190
+ checkFilter?: SelectRule<Cols, S>["forcedFilter"];
191
+
192
+ /**
193
+ * Data to include and overwrite on each update/insert
194
+ * These fields cannot be updated by the user
195
+ */
196
+ forcedData?: Partial<Cols>;
197
+ }
198
+
199
+ export type InsertRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = CommonInsertUpdateRule<Cols, S> & {
200
+
201
+ /**
202
+ * Fields allowed to be inserted. Tip: Use false to exclude field
203
+ */
204
+ fields: SelectRule<Cols>["fields"]
205
+
206
+ /**
207
+ * Fields user can view after inserting
208
+ */
209
+ returningFields?: SelectRule<Cols>["fields"]
210
+
211
+ /**
212
+ * Validation logic to check/update data for each request. Happens before publish rule checks (for fields, forcedData/forcedFilter)
213
+ */
214
+ preValidate?: S extends DBSchema? ValidateRow<Cols, S> : ValidateRowBasic;
215
+
216
+ /**
217
+ * Validation logic to check/update data for each request. Happens after publish rule checks (for fields, forcedData/forcedFilter)
218
+ */
219
+ validate?: S extends DBSchema? ValidateRow<Cols, S> : ValidateRowBasic;
220
+
221
+ /**
222
+ * Validation logic to check/update data after the insert.
223
+ * Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
224
+ */
225
+ postValidate?: S extends DBSchema? PostValidateRow<Required<Cols>, S> : PostValidateRowBasic;
226
+
227
+ /**
228
+ * If defined then only nested inserts from these tables are allowed
229
+ * Direct inserts will fail
230
+ */
231
+ allowedNestedInserts?: {
232
+ table: string;
233
+ column: string;
234
+ }[];
235
+ }
236
+
237
+
238
+ export type UpdateRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = CommonInsertUpdateRule<Cols, S> & {
239
+
240
+ /**
241
+ * Fields allowed to be updated. Tip: Use false/0 to exclude field
242
+ */
243
+ fields: SelectRule<Cols>["fields"]
244
+
245
+ /**
246
+ * Row level FGAC
247
+ * Used when the editable fields change based on the updated row
248
+ * If specified then the fields from the first matching filter table.count({ ...filter, ...updateFilter }) > 0 will be used
249
+ * If none matching then the "fields" will be used
250
+ * Specify in decreasing order of specificity otherwise a more general filter will match first
251
+ */
252
+ dynamicFields?: {
253
+ filter: FullFilter<Cols, S>;
254
+ fields: SelectRule<Cols>["fields"]
255
+ }[];
256
+
257
+ /**
258
+ * Filter added to every query (e.g. user_id) to restrict access
259
+ * This filter cannot be updated
260
+ */
261
+ forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
262
+
263
+ /**
264
+ * Fields user can use to find the updates
265
+ */
266
+ filterFields?: SelectRule<Cols>["fields"]
267
+
268
+ /**
269
+ * Fields user can view after updating
270
+ */
271
+ returningFields?: SelectRule<Cols>["fields"]
272
+
273
+ /**
274
+ * Validation logic to check/update data for each request
275
+ */
276
+ validate?: S extends DBSchema? ValidateUpdateRow<Cols, S> : ValidateUpdateRowBasic;
277
+
278
+ /**
279
+ * Validation logic to check/update data after the insert.
280
+ * Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
281
+ */
282
+ postValidate?: S extends DBSchema? PostValidateRow<Required<Cols>, S> : PostValidateRowBasic;
283
+ };
284
+
285
+ export type DeleteRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
286
+
287
+ /**
288
+ * Filter added to every query (e.g. user_id) to restrict access
289
+ */
290
+ forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
291
+
292
+ /**
293
+ * Fields user can filter by
294
+ */
295
+ filterFields: FieldFilter<Cols>;
296
+
297
+ /**
298
+ * Fields user can view after deleting
299
+ */
300
+ returningFields?: SelectRule<Cols>["filterFields"]
301
+
302
+ /**
303
+ * Validation logic to check/update data for each request
304
+ */
305
+ validate?(...args: any[]): Awaitable<void>;// UpdateRequestData<Cols>;
306
+ }
307
+ export type SyncRule<Cols extends AnyObject = AnyObject> = {
308
+
309
+ /**
310
+ * Primary keys used in updating data
311
+ */
312
+ id_fields: (keyof Cols)[];
313
+
314
+ /**
315
+ * Numerical incrementing fieldname (last updated timestamp) used to sync items
316
+ */
317
+ synced_field: keyof Cols;
318
+
319
+ /**
320
+ * EXPERIMENTAL. Disabled by default. If true then server will attempt to delete any records missing from client.
321
+ */
322
+ allow_delete?: boolean;
323
+
324
+ /**
325
+ * Throttle replication transmission in milliseconds. Defaults to 100
326
+ */
327
+ throttle?: number;
328
+
329
+ /**
330
+ * Number of rows to send per trip. Defaults to 50
331
+ */
332
+ batch_size?: number;
333
+ }
334
+ export type SubscribeRule = {
335
+ throttle?: number;
336
+ }
337
+
338
+ export type ViewRule<S extends AnyObject = AnyObject> = CommonTableRules & {
339
+ /**
340
+ * What can be read from the table
341
+ */
342
+ select?: SelectRule<S>;
343
+ };
344
+ export type TableRule<RowType extends AnyObject = AnyObject, S extends DBSchema | void = void> = ViewRule<RowType> & {
345
+ insert?: InsertRule<RowType, S>;
346
+ update?: UpdateRule<RowType, S>;
347
+ delete?: DeleteRule<RowType, S>;
348
+ sync?: SyncRule<RowType>;
349
+ subscribe?: SubscribeRule;
350
+ };
351
+ export type PublishViewRule<Col extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
352
+ select?: SelectRule<Col, S> | PublishAllOrNothing
353
+ getColumns?: PublishAllOrNothing;
354
+ getInfo?: PublishAllOrNothing;
355
+ };
356
+ export type PublishTableRule<Col extends AnyObject = AnyObject, S extends DBSchema | void = void> = PublishViewRule<Col, S> & {
357
+ insert?: InsertRule<Col, S> | PublishAllOrNothing
358
+ update?: UpdateRule<Col, S> | PublishAllOrNothing
359
+ delete?: DeleteRule<Col, S> | PublishAllOrNothing
360
+ sync?: SyncRule<Col>;
361
+ subscribe?: SubscribeRule | PublishAllOrNothing;
362
+ };
363
+
364
+
365
+ export type ParsedPublishTable = {
366
+ select?: SelectRule
367
+ getColumns?: true;
368
+ getInfo?: true;
369
+
370
+ insert?: InsertRule;
371
+ update?: UpdateRule;
372
+ delete?: DeleteRule;
373
+ sync?: SyncRule;
374
+ subscribe?: SubscribeRule;
375
+ subscribeOne?: SubscribeRule;
376
+ }
377
+ export type DbTableInfo = {
378
+ name: string;
379
+ info: TableOrViewInfo;
380
+ columns: TableSchemaColumn[];
381
+ }
382
+ export type PublishParams<S = void, SUser extends SessionUser = SessionUser> = {
383
+ sid?: string;
384
+ dbo: DBOFullyTyped<S>;
385
+ db: DB;
386
+ user?: SUser["user"];
387
+ socket: PRGLIOSocket;
388
+ tables: DbTableInfo[];
389
+ }
390
+ export type RequestParams = { dbo?: DBHandlerServer, socket?: any };
391
+ export type PublishAllOrNothing = true | "*" | false | null;
392
+ export type PublishObject = {
393
+ [table_name: string]: (PublishTableRule | PublishViewRule | PublishAllOrNothing)
394
+ };
395
+ export type ParsedPublishTables = {
396
+ [table_name: string]: ParsedPublishTable
397
+ };
398
+ export type PublishedResult<Schema = void> = PublishAllOrNothing | PublishFullyTyped<Schema>;
399
+ export type Publish<Schema = void, SUser extends SessionUser = SessionUser> = PublishedResult<Schema> | ((params: PublishParams<Schema, SUser>) => Awaitable<PublishedResult<Schema>>);
package/lib/RestApi.ts ADDED
@@ -0,0 +1,127 @@
1
+ import * as bodyParser from "body-parser";
2
+ import { Express } from "express";
3
+ import { HTTPCODES } from "./Auth/AuthHandler";
4
+ import { ExpressReq, ExpressRes } from "./Auth/AuthTypes";
5
+ import { getSerializedClientErrorFromPGError } from "./DboBuilder/DboBuilder";
6
+ import { Prostgles } from "./Prostgles";
7
+ import { runClientMethod, runClientRequest, runClientSqlRequest } from "./runClientRequest";
8
+ import { VoidFunction } from "./SchemaWatch/SchemaWatch";
9
+ const jsonParser = bodyParser.json();
10
+
11
+ export type ExpressApp = {
12
+ _router?: {
13
+ stack?: {
14
+ name: string;
15
+ handle: VoidFunction;
16
+ path: undefined,
17
+ keys?: any[];
18
+ route?: {
19
+ path?: string;
20
+ methods?: {
21
+ get?: boolean;
22
+ post?: boolean;
23
+ put?: boolean;
24
+ delete?: boolean;
25
+ };
26
+ }
27
+ }[]
28
+ }
29
+ } & Omit<Express, "_router">;
30
+
31
+ export type RestApiConfig = {
32
+ expressApp: Express;
33
+ routePrefix: string;
34
+ };
35
+
36
+ export class RestApi {
37
+ prostgles: Prostgles;
38
+ routes: {
39
+ db: string;
40
+ sql: string;
41
+ methods: string;
42
+ schema: string;
43
+ }
44
+ expressApp: Express;
45
+ constructor({ expressApp, routePrefix, prostgles }: RestApiConfig & { prostgles: Prostgles; }){
46
+ this.prostgles = prostgles;
47
+ this.routes = {
48
+ db: `${routePrefix}/db/:tableName/:command`,
49
+ sql: `${routePrefix}/db/sql`,
50
+ methods: `${routePrefix}/methods/:method`,
51
+ schema: `${routePrefix}/schema`,
52
+ };
53
+ this.expressApp = expressApp;
54
+ expressApp.post(this.routes.db, jsonParser, this.onPost);
55
+ expressApp.post(this.routes.sql, jsonParser, this.onPostSql);
56
+ expressApp.post(this.routes.methods, jsonParser, this.onPostMethod);
57
+ expressApp.post(this.routes.schema, jsonParser, this.onPostSchema);
58
+ }
59
+
60
+ destroy = () => {
61
+ this.expressApp.removeListener(this.routes.db, this.onPost);
62
+ this.expressApp.removeListener(this.routes.sql, this.onPostSql);
63
+ this.expressApp.removeListener(this.routes.methods, this.onPostMethod);
64
+ }
65
+ onPostMethod = async (req: ExpressReq, res: ExpressRes) => {
66
+ const { method = "" } = req.params;
67
+ const params = req.body || [];
68
+
69
+ try {
70
+ const data = await runClientMethod.bind(this.prostgles)({
71
+ type: "http",
72
+ httpReq: req, method, params
73
+ });
74
+ res.json(data);
75
+ } catch(rawError){
76
+ const error = getSerializedClientErrorFromPGError(rawError, { type: "method", localParams: { httpReq: req } });
77
+ res.status(400).json({ error });
78
+ }
79
+ }
80
+ onPostSchema = async (req: ExpressReq, res: ExpressRes) => {
81
+ try {
82
+ const data = await this.prostgles.getClientSchema({ httpReq: req });
83
+ res.json(data);
84
+ } catch(rawError){
85
+ const error = getSerializedClientErrorFromPGError(rawError, { type: "method", localParams: { httpReq: req } });
86
+ res.status(400).json({ error });
87
+ }
88
+ }
89
+ onPostSql = async (req: ExpressReq, res: ExpressRes) => {
90
+ const [query, args, options] = req.body || [];
91
+ try {
92
+ const data = await runClientSqlRequest.bind(this.prostgles)({ type: "http", httpReq: req, query, args, options });
93
+ res.json(data);
94
+ } catch(rawError){
95
+ const error = getSerializedClientErrorFromPGError(rawError, { type: "sql" });
96
+ res.status(400).json({ error });
97
+ }
98
+ }
99
+
100
+ onPost = async (req: ExpressReq, res: ExpressRes) => {
101
+ const { params } = req;
102
+ const { tableName, command } = params;
103
+ if(!tableName || typeof tableName !== "string"){
104
+ res.status(400).json({ error: "tableName missing" });
105
+ return;
106
+ }
107
+ if(!command || typeof command !== "string"){
108
+ res.status(HTTPCODES.BAD_REQUEST).json({ error: "command missing" });
109
+ return;
110
+ }
111
+
112
+ try {
113
+ const [param1, param2, param3] = req.body || [];
114
+ const data = await runClientRequest.bind(this.prostgles)({
115
+ type: "http",
116
+ httpReq: req,
117
+ tableName,
118
+ command,
119
+ param1, param2, param3
120
+ });
121
+ res.json(data);
122
+ } catch(rawError){
123
+ const error = getSerializedClientErrorFromPGError(rawError, { type: "tableMethod", localParams: { httpReq: req }, view: this.prostgles.dboBuilder.dbo[tableName] });
124
+ res.status(400).json({ error });
125
+ }
126
+ }
127
+ }
@@ -0,0 +1,90 @@
1
+ import type { DboBuilder } from "../DboBuilder/DboBuilder";
2
+ import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
3
+ import { PubSubManager, log } from "../PubSubManager/PubSubManager";
4
+ import { ValidatedWatchSchemaType, getValidatedWatchSchemaType } from "./getValidatedWatchSchemaType";
5
+ const COMMAND_FIRST_KEYWORDS = EVENT_TRIGGER_TAGS
6
+ .map(tag => tag.split(" ")[0]!)
7
+ .filter(tag => tag !== "SELECT"); /** SELECT INTO is not easily detectable with pg-node (command = "SELECT") */
8
+
9
+ const DB_FALLBACK_COMMANDS = Array.from(new Set(COMMAND_FIRST_KEYWORDS))
10
+ .concat([
11
+ "DO", // Do statement
12
+ "COMMIT" // Transaction block
13
+ ]);
14
+
15
+ export type VoidFunction = () => void;
16
+
17
+ export type OnSchemaChangeCallback = ((event: { command: string; query: string }) => void);
18
+
19
+ export class SchemaWatch {
20
+
21
+ dboBuilder: DboBuilder;
22
+ type: ValidatedWatchSchemaType;
23
+ private constructor(dboBuilder: DboBuilder){
24
+ this.dboBuilder = dboBuilder;
25
+ this.type = getValidatedWatchSchemaType(dboBuilder);
26
+ if(this.type.watchType === "NONE") {
27
+ this.onSchemaChange = undefined;
28
+ this.onSchemaChangeFallback = undefined;
29
+ }
30
+ if(this.type.watchType === "DDL_trigger") {
31
+ this.onSchemaChangeFallback = undefined;
32
+ }
33
+ }
34
+
35
+ static create = async (dboBuilder: DboBuilder) => {
36
+ const instance = new SchemaWatch(dboBuilder);
37
+ if(instance.type.watchType === "DDL_trigger") {
38
+ await dboBuilder.getPubSubManager();
39
+ // TODO finish createSchemaWatchEventTrigger to ensure the query is not used in NOTIFY and exclude happens inside Postgres
40
+ }
41
+ return instance;
42
+ }
43
+
44
+ /**
45
+ * Fallback for watchSchema in case of not a superuser (cannot add db event listener)
46
+ */
47
+ onSchemaChangeFallback: OnSchemaChangeCallback | undefined = async ({ command, query }) => {
48
+
49
+ if (typeof query === "string" && query.includes(PubSubManager.EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID)) {
50
+ log("Schema change event excluded from triggers due to EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID");
51
+ return;
52
+ }
53
+ if(
54
+ this.type.watchType !== "prostgles_queries" ||
55
+ !this.onSchemaChange ||
56
+ !DB_FALLBACK_COMMANDS.includes(command)
57
+ ) return;
58
+
59
+ this.onSchemaChange({ command, query })
60
+ }
61
+
62
+ onSchemaChange: OnSchemaChangeCallback | undefined = async (event) => {
63
+
64
+ const { watchSchema, onReady, tsGeneratedTypesDir } = this.dboBuilder.prostgles.opts;
65
+ if (watchSchema && this.dboBuilder.prostgles.loaded) {
66
+ log("Schema changed");
67
+ const { query, command } = event;
68
+
69
+ this.dboBuilder.cacheDBTypes(true);
70
+ if (typeof watchSchema === "function") {
71
+ /* Only call the provided func */
72
+ watchSchema(event);
73
+
74
+ } else if (watchSchema === "hotReloadMode") {
75
+ if (tsGeneratedTypesDir) {
76
+ /* Hot reload integration. Will only touch tsGeneratedTypesDir */
77
+ console.log("watchSchema: Re-writing TS schema");
78
+
79
+ await this.dboBuilder.prostgles.refreshDBO();
80
+ this.dboBuilder.prostgles.writeDBSchema(true);
81
+ }
82
+
83
+ } else if (watchSchema) {
84
+ /* Full re-init. Sockets must reconnect */
85
+ console.log("watchSchema: Full re-initialisation", { query })
86
+ this.dboBuilder.prostgles.init(onReady as any, { type: "schema change", query, command });
87
+ }
88
+ }
89
+ };
90
+ }
@@ -0,0 +1,3 @@
1
+ // export const createSchemaWatchEventTrigger = () => {
2
+
3
+ // }
@@ -0,0 +1,45 @@
1
+ import type { DboBuilder } from "../DboBuilder/DboBuilder";
2
+ import { OnSchemaChangeCallback } from "./SchemaWatch";
3
+
4
+ export type ValidatedWatchSchemaType =
5
+ | { watchType: "NONE" }
6
+ | { watchType: "DDL_trigger"; onChange?: OnSchemaChangeCallback; }
7
+ | { watchType: "prostgles_queries"; onChange?: OnSchemaChangeCallback; isFallbackFromDDL: boolean; }
8
+
9
+ export const getValidatedWatchSchemaType = (dboBuilder: DboBuilder): ValidatedWatchSchemaType => {
10
+ const {watchSchema, watchSchemaType, tsGeneratedTypesDir, disableRealtime } = dboBuilder.prostgles.opts;
11
+ if(!watchSchema) return { watchType: "NONE" };
12
+
13
+ if (watchSchema === "hotReloadMode" && !tsGeneratedTypesDir) {
14
+ throw "tsGeneratedTypesDir option is needed for watchSchema: hotReloadMode to work ";
15
+ }
16
+
17
+ const onChange = typeof watchSchema === "function"? watchSchema : undefined;
18
+
19
+ if(watchSchemaType === "DDL_trigger" || !watchSchemaType){
20
+ if(!dboBuilder.prostgles.isSuperUser || disableRealtime){
21
+
22
+ if(watchSchemaType === "DDL_trigger"){
23
+ console.error(`watchSchemaType "DDL_trigger" cannot be used because db user is not a superuser. Will fallback to watchSchemaType "prostgles_queries" `)
24
+ } else {
25
+ console.warn(`watchSchema fallback to watchSchemaType "prostgles_queries" due to ${disableRealtime? "disableRealtime setting" : "non-superuser"}`)
26
+ }
27
+ return {
28
+ watchType: "prostgles_queries",
29
+ onChange,
30
+ isFallbackFromDDL: true
31
+ }
32
+ }
33
+
34
+ return {
35
+ watchType: "DDL_trigger",
36
+ onChange
37
+ };
38
+ }
39
+
40
+ return {
41
+ watchType: watchSchemaType,
42
+ isFallbackFromDDL: false,
43
+ onChange
44
+ }
45
+ }
@@ -0,0 +1,27 @@
1
+ import { getKeys, isObject } from "prostgles-types";
2
+ import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
3
+ import { ProstglesInitOptions } from "../ProstglesTypes";
4
+
5
+ export const getWatchSchemaTagList = (watchSchema: ProstglesInitOptions["watchSchema"]) => {
6
+ if(!watchSchema) return undefined;
7
+
8
+ if(watchSchema === "*"){
9
+ return EVENT_TRIGGER_TAGS.slice(0);
10
+ }
11
+ if (isObject(watchSchema) && typeof watchSchema !== "function"){
12
+ const watchSchemaKeys = getKeys(watchSchema);
13
+ const isInclusive = Object.values(watchSchema).every(v => v);
14
+ return EVENT_TRIGGER_TAGS
15
+ .slice(0)
16
+ .filter(v => {
17
+ const matches = watchSchemaKeys.includes(v);
18
+ return isInclusive? matches : !matches;
19
+ });
20
+ }
21
+
22
+ const coreTags: typeof EVENT_TRIGGER_TAGS[number][] = [
23
+ 'COMMENT', 'CREATE TABLE', 'ALTER TABLE', 'DROP TABLE', 'CREATE VIEW',
24
+ 'DROP VIEW', 'ALTER VIEW', 'CREATE TABLE AS', 'SELECT INTO', 'CREATE POLICY'
25
+ ];
26
+ return coreTags;
27
+ }