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,485 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Stefan L. All rights reserved.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ import * as pgPromise from 'pg-promise';
7
+ import { AuthHandler } from "./Auth/AuthHandler";
8
+ import { FileManager } from "./FileManager/FileManager";
9
+ import { SchemaWatch } from "./SchemaWatch/SchemaWatch";
10
+ import { OnInitReason, initProstgles } from "./initProstgles";
11
+ import { makeSocketError, onSocketConnected } from "./onSocketConnected";
12
+ import { clientCanRunSqlRequest, runClientSqlRequest } from "./runClientRequest";
13
+ import pg = require('pg-promise/typescript/pg-subset');
14
+ const { version } = require('../package.json');
15
+
16
+ import type { ProstglesInitOptions } from "./ProstglesTypes";
17
+ import { RestApi } from "./RestApi";
18
+ import TableConfigurator from "./TableConfig/TableConfig";
19
+
20
+ import { DBHandlerServer, DboBuilder, LocalParams, PRGLIOSocket, getErrorAsObject } from "./DboBuilder/DboBuilder";
21
+ export { DBHandlerServer };
22
+ export type PGP = pgPromise.IMain<{}, pg.IClient>;
23
+
24
+ import {
25
+ CHANNELS,
26
+ ClientSchema,
27
+ DBSchemaTable,
28
+ SQLRequest, TableSchemaForClient,
29
+ isObject, omitKeys, tryCatch
30
+ } from "prostgles-types";
31
+ import { DBEventsManager } from "./DBEventsManager";
32
+ import { PublishParser } from "./PublishParser/PublishParser";
33
+
34
+ export type DB = pgPromise.IDatabase<{}, pg.IClient>;
35
+ export type DBorTx = DB | pgPromise.ITask<{}>
36
+
37
+
38
+ export const TABLE_METHODS = ["update", "find", "findOne", "insert", "delete", "upsert"] as const;
39
+
40
+
41
+
42
+ /*
43
+ 1. Connect to db
44
+ 2. Execute any SQL file if provided
45
+ 3. Make DBO object from all tables and views
46
+ 4. Set publish listeners
47
+ 5. Finish init and provide DBO object
48
+ */
49
+
50
+ export type OnReady = {
51
+ dbo: DBHandlerServer;
52
+ db: DB;
53
+ }
54
+
55
+ const DEFAULT_KEYWORDS = {
56
+ $filter: "$filter",
57
+ $and: "$and",
58
+ $or: "$or",
59
+ $not: "$not"
60
+ };
61
+
62
+ import { randomUUID } from "crypto";
63
+ import * as fs from 'fs';
64
+
65
+ export class Prostgles {
66
+ /**
67
+ * Used facilitate concurrent prostgles connections to the same database
68
+ */
69
+ readonly appId = randomUUID();
70
+ opts: ProstglesInitOptions = {
71
+ DEBUG_MODE: false,
72
+ dbConnection: {
73
+ host: "localhost",
74
+ port: 5432,
75
+ application_name: "prostgles_app"
76
+ },
77
+ onReady: () => {
78
+ //empty
79
+ },
80
+ watchSchema: false,
81
+ watchSchemaType: "DDL_trigger",
82
+ };
83
+
84
+ db?: DB;
85
+ pgp?: PGP;
86
+ dbo?: DBHandlerServer;
87
+ _dboBuilder?: DboBuilder;
88
+ get dboBuilder(): DboBuilder {
89
+ if (!this._dboBuilder) {
90
+ console.trace(1)
91
+ throw "get dboBuilder: it's undefined";
92
+ }
93
+ return this._dboBuilder;
94
+ }
95
+ set dboBuilder(d: DboBuilder) {
96
+ this._dboBuilder = d;
97
+ }
98
+ publishParser?: PublishParser;
99
+
100
+ authHandler?: AuthHandler;
101
+
102
+ schemaWatch?: SchemaWatch;
103
+
104
+ keywords = DEFAULT_KEYWORDS;
105
+ loaded = false;
106
+
107
+ dbEventsManager?: DBEventsManager;
108
+ schemaAge = "0";
109
+
110
+ fileManager?: FileManager;
111
+ restApi?: RestApi;
112
+
113
+ tableConfigurator?: TableConfigurator;
114
+
115
+ isMedia(tableName: string) {
116
+ return this.opts?.fileTable?.tableName === tableName;
117
+ }
118
+
119
+ constructor(params: ProstglesInitOptions) {
120
+ if (!params) throw "ProstglesInitOptions missing";
121
+
122
+ const config: Record<keyof ProstglesInitOptions, 1> = {
123
+ transactions: 1, joins: 1, tsGeneratedTypesDir: 1, disableRealtime: 1,
124
+ onReady: 1, dbConnection: 1, dbOptions: 1, publishMethods: 1,
125
+ io: 1, publish: 1, schema: 1, publishRawSQL: 1, wsChannelNamePrefix: 1,
126
+ onSocketConnect: 1, onSocketDisconnect: 1, sqlFilePath: 1, auth: 1,
127
+ DEBUG_MODE: 1, watchSchema: 1, watchSchemaType: 1, fileTable: 1, onQuery: 1,
128
+ tableConfig: 1, tableConfigMigrations: 1, keywords: 1, onNotice: 1, onLog: 1, restApi: 1, testRulesOnConnect: 1
129
+ };
130
+ const unknownParams = Object.keys(params).filter((key: string) => !Object.keys(config).includes(key))
131
+ if (unknownParams.length) {
132
+ console.error(`Unrecognised ProstglesInitOptions params: ${unknownParams.join()}`);
133
+ }
134
+
135
+ Object.assign(this.opts, params);
136
+
137
+ /* set defaults */
138
+ if (this.opts?.fileTable) {
139
+ this.opts.fileTable.tableName ??= "media";
140
+ }
141
+ this.opts.schema ??= { "public": 1 };
142
+
143
+ this.keywords = {
144
+ ...DEFAULT_KEYWORDS,
145
+ ...params.keywords,
146
+ }
147
+ }
148
+
149
+ destroyed = false;
150
+
151
+ checkDb() {
152
+ if (!this.db || !this.db.connect) throw "something went wrong getting a db connection";
153
+ }
154
+
155
+ getTSFileName() {
156
+ const fileName = "DBoGenerated.d.ts" //`dbo_${this.schema}_types.ts`;
157
+ const _dir = (this.opts.tsGeneratedTypesDir || "");
158
+ const dir = _dir.endsWith("/")? _dir : `${_dir}/`;
159
+ const fullPath = dir + fileName;
160
+ return { fileName, fullPath }
161
+ }
162
+
163
+ private getFileText(fullPath: string, _format = "utf8"): Promise<string> {
164
+ return new Promise((resolve, reject) => {
165
+ fs.readFile(fullPath, 'utf8', function (err, data) {
166
+ if (err) reject(err);
167
+ else resolve(data);
168
+ });
169
+ })
170
+ }
171
+
172
+ getTSFileContent = () => {
173
+ const header = `/* This file was generated by Prostgles \n` +
174
+ // `* ${(new Date).toUTCString()} \n`
175
+ `*/ \n\n `;
176
+ return header + this.dboBuilder.tsTypesDefinition;
177
+ }
178
+
179
+ /**
180
+ * Will write the Schema Typescript definitions to file (tsGeneratedTypesDir)
181
+ */
182
+ writeDBSchema(force = false) {
183
+
184
+ if (this.opts.tsGeneratedTypesDir) {
185
+ const { fullPath, fileName } = this.getTSFileName();
186
+ const fileContent = this.getTSFileContent();
187
+ fs.readFile(fullPath, 'utf8', function (err, data) {
188
+ if (err || (force || data !== fileContent)) {
189
+ fs.writeFileSync(fullPath, fileContent);
190
+ console.log("Prostgles: Created typescript schema definition file: \n " + fileName)
191
+ }
192
+ });
193
+ } else if (force) {
194
+ console.error("Schema changed. tsGeneratedTypesDir needs to be set to reload server")
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Will re-create the dbo object
200
+ */
201
+ refreshDBO = async () => {
202
+ await this.opts.onLog?.({
203
+ type: "debug",
204
+ command: "refreshDBO.start",
205
+ duration: -1,
206
+ data: { }
207
+ });
208
+ const start = Date.now();
209
+ if (this._dboBuilder) {
210
+ await this._dboBuilder.build();
211
+ } else {
212
+ this.dboBuilder = await DboBuilder.create(this);
213
+ }
214
+ if (!this.dboBuilder) throw "this.dboBuilder";
215
+ this.dbo = this.dboBuilder.dbo;
216
+ await this.opts.onLog?.({ type: "debug", command: "refreshDBO.end", duration: Date.now() - start })
217
+ return this.dbo;
218
+ }
219
+
220
+ initRestApi = async () => {
221
+ if (this.opts.restApi) {
222
+ this.restApi = new RestApi({ prostgles: this, ...this.opts.restApi });
223
+ } else {
224
+ this.restApi?.destroy();
225
+ this.restApi = undefined;
226
+ }
227
+ }
228
+
229
+ initAuthHandler = async () => {
230
+ this.authHandler?.destroy();
231
+ this.authHandler = new AuthHandler(this as any);
232
+ await this.authHandler.init();
233
+ }
234
+
235
+ initTableConfig = async (reason: OnInitReason) => {
236
+ const res = await tryCatch(async () => {
237
+
238
+ if(this.tableConfigurator?.initialising){
239
+ console.error("TableConfigurator WILL deadlock", { reason });
240
+ }
241
+ await this.tableConfigurator?.destroy();
242
+ this.tableConfigurator = new TableConfigurator(this);
243
+ try {
244
+ const now = Date.now();
245
+ await this.opts.onLog?.({ type: "debug", command: "tableConfigurator.init.start", duration: -1 });
246
+ await this.tableConfigurator.init();
247
+ await this.opts.onLog?.({ type: "debug", command: "tableConfigurator.init.end", duration: Date.now() - now });
248
+ } catch (e) {
249
+ if(this.opts.tableConfigMigrations?.silentFail === false){
250
+ console.error("TableConfigurator silentFail: ", e);
251
+ } else {
252
+ throw e;
253
+ }
254
+ }
255
+ });
256
+ await this.opts.onLog?.({ type: "debug", command: "initTableConfig", ...res });
257
+ if(res.hasError) throw res.error;
258
+ return res.data;
259
+ }
260
+
261
+ /* Create media table if required */
262
+ initFileTable = async () => {
263
+ const res = await tryCatch(async () => {
264
+
265
+ if (this.opts.fileTable) {
266
+ const { cloudClient, localConfig, imageOptions } = this.opts.fileTable;
267
+ await this.refreshDBO();
268
+ if (!cloudClient && !localConfig) throw "fileTable missing param: Must provide awsS3Config OR localConfig";
269
+
270
+ this.fileManager = new FileManager(cloudClient || localConfig!, imageOptions);
271
+
272
+ try {
273
+ await this.fileManager.init(this);
274
+ } catch (e) {
275
+ console.error("FileManager: ", e);
276
+ this.fileManager = undefined;
277
+ }
278
+ } else {
279
+ await this.fileManager?.destroy();
280
+ this.fileManager = undefined;
281
+ }
282
+ await this.refreshDBO();
283
+ return { data: {} }
284
+ });
285
+ await this.opts.onLog?.({
286
+ type: "debug",
287
+ command: "initFileTable",
288
+ ...res,
289
+ });
290
+ if(res.error !== undefined) throw res.error;
291
+ return res.data;
292
+ }
293
+
294
+ isSuperUser = false;
295
+
296
+ init = initProstgles.bind(this);
297
+
298
+ async runSQLFile(filePath: string) {
299
+
300
+ const res = await tryCatch(async () => {
301
+ const fileContent = await this.getFileText(filePath);//.then(console.log);
302
+
303
+ const result = await this.db?.multi(fileContent)
304
+ .then((data) => {
305
+ console.log("Prostgles: SQL file executed successfuly \n -> " + filePath);
306
+ return data;
307
+ }).catch((err) => {
308
+ const { position, length } = err,
309
+ lines = fileContent.split("\n");
310
+ let errMsg = filePath + " error: ";
311
+
312
+ if (position && length && fileContent) {
313
+ const startLine = Math.max(0, fileContent.substring(0, position).split("\n").length - 2),
314
+ endLine = startLine + 3;
315
+
316
+ errMsg += "\n\n";
317
+ errMsg += lines.slice(startLine, endLine).map((txt, i) => `${startLine + i + 1} ${i === 1 ? "->" : " "} ${txt}`).join("\n");
318
+ errMsg += "\n\n";
319
+ }
320
+ console.error(errMsg, err);
321
+ return Promise.reject(err);
322
+ });
323
+ return { success: result?.length }
324
+ });
325
+
326
+ await this.opts.onLog?.({ type: "debug", command: "runSQLFile", ...res });
327
+ if(res.error !== undefined) throw res.error;
328
+ return res.success;
329
+ }
330
+
331
+
332
+ connectedSockets: PRGLIOSocket[] = [];
333
+ async setSocketEvents() {
334
+ this.checkDb();
335
+
336
+ if (!this.dbo) throw "dbo missing";
337
+
338
+ const publishParser = new PublishParser(
339
+ this.opts.publish,
340
+ this.opts.publishMethods,
341
+ this.opts.publishRawSQL,
342
+ this.dbo,
343
+ this.db!,
344
+ this
345
+ );
346
+ this.publishParser = publishParser;
347
+
348
+ if (!this.opts.io) return;
349
+
350
+ /* Already initialised. Only reconnect sockets */
351
+ if (this.connectedSockets.length) {
352
+ this.connectedSockets.forEach(s => {
353
+ s.emit(CHANNELS.SCHEMA_CHANGED);
354
+ this.pushSocketSchema(s);
355
+ });
356
+ return;
357
+ }
358
+
359
+ /* Initialise */
360
+ this.opts.io.removeAllListeners('connection');
361
+ this.opts.io.on('connection', this.onSocketConnected);
362
+ /** In some cases io will re-init with already connected sockets */
363
+ this.opts.io?.sockets.sockets.forEach(socket => this.onSocketConnected(socket))
364
+ }
365
+
366
+ onSocketConnected = onSocketConnected.bind(this);
367
+
368
+ getClientSchema = async (clientReq: Pick<LocalParams, "socket" | "httpReq">) => {
369
+
370
+ const result = await tryCatch(async () => {
371
+
372
+ const clientInfo = clientReq.socket? { type: "socket" as const, socket: clientReq.socket } : clientReq.httpReq? { type: "http" as const, httpReq: clientReq.httpReq } : undefined;
373
+ if(!clientInfo) throw "Invalid client";
374
+ if(!this.authHandler) throw "this.authHandler missing";
375
+ const userData = await this.authHandler.getClientInfo(clientInfo);
376
+ const { publishParser } = this;
377
+ let fullSchema: {
378
+ schema: TableSchemaForClient;
379
+ tables: DBSchemaTable[];
380
+ } | undefined;
381
+ let publishValidationError;
382
+
383
+ try {
384
+ if (!publishParser) throw "publishParser undefined";
385
+ fullSchema = await publishParser.getSchemaFromPublish({ ...clientInfo, userData });
386
+ } catch (e) {
387
+ publishValidationError = e;
388
+ console.error(`\nProstgles Publish validation failed (after socket connected):\n ->`, e);
389
+ }
390
+ let rawSQL = false;
391
+ if (this.opts.publishRawSQL && typeof this.opts.publishRawSQL === "function") {
392
+ const { allowed } = await clientCanRunSqlRequest.bind(this)(clientInfo);
393
+ rawSQL = allowed;
394
+ }
395
+
396
+ const { schema, tables } = fullSchema ?? { schema: {}, tables: [] };
397
+ const joinTables2: string[][] = [];
398
+ if (this.opts.joins) {
399
+ const _joinTables2 = this.dboBuilder.getAllJoinPaths()
400
+ .filter(jp =>
401
+ ![jp.t1, jp.t2].find(t => !schema[t] || !schema[t]?.findOne)
402
+ ).map(jp => [jp.t1, jp.t2].sort());
403
+ _joinTables2.map(jt => {
404
+ if (!joinTables2.find(_jt => _jt.join() === jt.join())) {
405
+ joinTables2.push(jt);
406
+ }
407
+ });
408
+ }
409
+
410
+ const methods = await publishParser?.getAllowedMethods(clientInfo, userData);
411
+
412
+ const methodSchema: ClientSchema["methods"] = !methods? [] : Object.entries(methods).map(([methodName, method]) => {
413
+ if(isObject(method) && "run" in method){
414
+ return {
415
+ name: methodName,
416
+ ...omitKeys(method, ["run"]),
417
+ }
418
+ }
419
+ return methodName;
420
+ }).sort((a, b) => {
421
+ const aName = isObject(a)? a.name : a;
422
+ const bName = isObject(b)? b.name : b;
423
+ return aName.localeCompare(bName);
424
+ });
425
+
426
+ const { auth } = await this.authHandler.getClientAuth(clientReq);
427
+
428
+ const clientSchema: ClientSchema = {
429
+ schema,
430
+ methods: methodSchema,
431
+ tableSchema: tables,
432
+ rawSQL,
433
+ joinTables: joinTables2,
434
+ auth,
435
+ version,
436
+ err: publishValidationError? "Server Error: User publish validation failed." : undefined
437
+ };
438
+
439
+ return {
440
+ publishValidationError,
441
+ clientSchema,
442
+ userData
443
+ }
444
+ });
445
+ const sid = result.userData?.sid ?? this.authHandler?.getSIDNoError(clientReq);
446
+ await this.opts.onLog?.({
447
+ type: "connect.getClientSchema",
448
+ duration: result.duration,
449
+ sid,
450
+ socketId: clientReq.socket?.id,
451
+ error: result.error || result.publishValidationError,
452
+ });
453
+ if(result.hasError) throw result.error;
454
+ return result.clientSchema;
455
+ }
456
+
457
+ pushSocketSchema = async (socket: PRGLIOSocket) => {
458
+
459
+ try {
460
+ const clientSchema = await this.getClientSchema({ socket });
461
+ socket.prostgles = socket.prostgles || {};
462
+ socket.prostgles.schema = clientSchema.schema;
463
+ if (clientSchema.rawSQL) {
464
+ socket.removeAllListeners(CHANNELS.SQL)
465
+ socket.on(CHANNELS.SQL, async ({ query, params, options }: SQLRequest, cb = (..._callback: any) => { /* Empty */ }) => {
466
+
467
+ runClientSqlRequest.bind(this)({ type: "socket", socket, query, args: params, options }).then(res => {
468
+ cb(null, res);
469
+ }).catch(err => {
470
+ makeSocketError(cb, err);
471
+ });
472
+ });
473
+ }
474
+ socket.emit(CHANNELS.SCHEMA, clientSchema);
475
+
476
+ } catch (err) {
477
+ socket.emit(CHANNELS.SCHEMA, { err: getErrorAsObject(err) });
478
+ }
479
+ }
480
+ }
481
+
482
+
483
+ export async function getIsSuperUser(db: DB): Promise<boolean> {
484
+ return db.oneOrNone("select usesuper from pg_user where usename = CURRENT_USER;").then(r => r.usesuper);
485
+ }
@@ -0,0 +1,196 @@
1
+
2
+ import { FileColumnConfig } from "prostgles-types";
3
+ import { Auth, AuthRequestParams, SessionUser } from "./Auth/AuthTypes";
4
+ import { EventTriggerTagFilter } from "./Event_Trigger_Tags";
5
+ import { CloudClient, ImageOptions, LocalConfig } from "./FileManager/FileManager";
6
+ import { EventInfo } from "./Logging";
7
+ import { ExpressApp } from "./RestApi";
8
+ import { OnSchemaChangeCallback } from "./SchemaWatch/SchemaWatch";
9
+ import { ColConstraint } from "./TableConfig/getConstraintDefinitionQueries";
10
+ import { DbConnection, DbConnectionOpts, OnReadyCallback } from "./initProstgles";
11
+ import { RestApiConfig } from "./RestApi";
12
+ import { TableConfig } from "./TableConfig/TableConfig";
13
+
14
+ import { PRGLIOSocket } from "./DboBuilder/DboBuilder";
15
+
16
+ import {
17
+ AnyObject
18
+ } from "prostgles-types";
19
+ import type { Server } from "socket.io";
20
+ import { Publish, PublishMethods, PublishParams } from "./PublishParser/PublishParser";
21
+ import { DB } from "./Prostgles";
22
+ import pgPromise from "pg-promise";
23
+ import pg from "pg-promise/typescript/pg-subset";
24
+
25
+ /**
26
+ * Allows uploading and downloading files.
27
+ * Currently supports only S3.
28
+ *
29
+ * @description
30
+ * Will create a media table that contains file metadata and urls
31
+ * Inserting a file into this table through prostgles will upload it to S3 and insert the relevant metadata into the media table
32
+ * Requesting a file from HTTP GET {fileUrlPath}/{fileId} will:
33
+ * 1. check auth (if provided)
34
+ * 2. check the permissions in publish (if provided)
35
+ * 3. redirect the request to the signed url (if allowed)
36
+ *
37
+ * Specifying referencedTables will:
38
+ * 1. create a column in that table called media
39
+ * 2. create a lookup table lookup_media_{referencedTable} that joins referencedTable to the media table
40
+ */
41
+ export type FileTableConfig = {
42
+ tableName?: string; /* defaults to 'media' */
43
+
44
+ /**
45
+ * GET path used in serving media. defaults to /${tableName}
46
+ */
47
+ fileServeRoute?: string;
48
+
49
+ cloudClient?: CloudClient;
50
+ localConfig?: LocalConfig;
51
+
52
+ /**
53
+ * If defined the the files will not be deleted immediately
54
+ * Instead, the "deleted" field will be updated to the current timestamp and after the day interval provided in "deleteAfterNDays" the files will be deleted
55
+ * "checkIntervalMinutes" is the frequency in hours at which the files ready for deletion are deleted
56
+ */
57
+ delayedDelete?: {
58
+ /**
59
+ * Minimum amount of time measured in days for which the files will not be deleted after requesting delete
60
+ */
61
+ deleteAfterNDays: number;
62
+ /**
63
+ * How freuquently the files will be checked for deletion delay
64
+ */
65
+ checkIntervalHours?: number;
66
+ }
67
+ expressApp: ExpressApp;
68
+ referencedTables?: {
69
+ [tableName: string]:
70
+
71
+ /**
72
+ * If defined then will try to create (if necessary) these columns which will reference files_table(id)
73
+ * Prostgles UI will use these hints (obtained through tableHandler.getInfo())
74
+ * */
75
+ | { type: "column", referenceColumns: Record<string, FileColumnConfig> }
76
+ },
77
+ imageOptions?: ImageOptions
78
+ };
79
+
80
+
81
+
82
+ type Keywords = {
83
+ $and: string;
84
+ $or: string;
85
+ $not: string;
86
+ };
87
+
88
+ export const JOIN_TYPES = ["one-many", "many-one", "one-one", "many-many"] as const;
89
+ export type Join = {
90
+ tables: [string, string];
91
+ on: { [key: string]: string }[]; // Allow multi references to table
92
+ type: typeof JOIN_TYPES[number];
93
+ };
94
+ type Joins = Join[] | "inferred";
95
+
96
+ export type ProstglesInitOptions<S = void, SUser extends SessionUser = SessionUser> = {
97
+ dbConnection: DbConnection;
98
+ dbOptions?: DbConnectionOpts;
99
+ tsGeneratedTypesDir?: string;
100
+ disableRealtime?: boolean;
101
+ io?: Server;
102
+ publish?: Publish<S, SUser>;
103
+ /**
104
+ * If true then will test all table methods on each socket connect
105
+ */
106
+ testRulesOnConnect?: boolean;
107
+ publishMethods?: PublishMethods<S, SUser>;
108
+ publishRawSQL?(params: PublishParams<S, SUser>): ((boolean | "*") | Promise<(boolean | "*")>);
109
+ joins?: Joins;
110
+ schema?: Record<string, 1> | Record<string, 0>;
111
+ sqlFilePath?: string;
112
+ onReady: OnReadyCallback<S>;
113
+ transactions?: string | boolean;
114
+ wsChannelNamePrefix?: string;
115
+ /**
116
+ * Use for connection verification. Will disconnect socket on any errors
117
+ */
118
+ onSocketConnect?: (args: AuthRequestParams<S, SUser> & { socket: PRGLIOSocket }) => void | Promise<void>;
119
+ onSocketDisconnect?: (args: AuthRequestParams<S, SUser> & { socket: PRGLIOSocket }) => void | Promise<void>;
120
+ auth?: Auth<S, SUser>;
121
+ DEBUG_MODE?: boolean;
122
+ onQuery?: (error: any, ctx: pgPromise.IEventContext<pg.IClient>) => void;
123
+ watchSchemaType?:
124
+
125
+ /**
126
+ * Will set database event trigger for schema changes. Requires superuser
127
+ * Default
128
+ */
129
+ | "DDL_trigger"
130
+
131
+ /**
132
+ * Will check client queries for schema changes
133
+ * fallback if DDL not possible
134
+ */
135
+ | "prostgles_queries"
136
+
137
+ /**
138
+ * If truthy then DBoGenerated.d.ts will be updated and "onReady" will be called with new schema on both client and server
139
+ */
140
+ watchSchema?:
141
+ /**
142
+ * Will listen only to few events (create table/view)
143
+ */
144
+ | boolean
145
+
146
+ /**
147
+ * Will listen to specified events (or all if "*" is specified)
148
+ */
149
+ | EventTriggerTagFilter
150
+
151
+ /**
152
+ * Will only rewrite the DBoGenerated.d.ts found in tsGeneratedTypesDir
153
+ * This is meant to be used in development when server restarts on file change
154
+ */
155
+ | "hotReloadMode"
156
+
157
+ /**
158
+ * Function called when schema changes. Nothing else triggered
159
+ */
160
+ | OnSchemaChangeCallback;
161
+
162
+ keywords?: Keywords;
163
+ onNotice?: (notice: AnyObject, message?: string) => void;
164
+ fileTable?: FileTableConfig;
165
+ restApi?: RestApiConfig;
166
+ /**
167
+ * Creates tables and provides UI labels, autocomplete and hints for a given json structure
168
+ */
169
+ tableConfig?: TableConfig;
170
+ tableConfigMigrations?: {
171
+ /**
172
+ * If false then prostgles won't start on any tableConfig error
173
+ * true by default
174
+ */
175
+ silentFail?: boolean;
176
+
177
+ version: number;
178
+ /** Table that will contain the schema version number and the tableConfig
179
+ * Defaults to schema_version
180
+ */
181
+ versionTableName?: string;
182
+ /**
183
+ * Script run before tableConfig is loaded IF an older schema_version is present
184
+ */
185
+ onMigrate: (args: {
186
+ db: DB;
187
+ oldVersion: number | undefined;
188
+ getConstraints: (
189
+ table: string,
190
+ column?: string,
191
+ types?: ColConstraint["type"][]
192
+ ) => Promise<ColConstraint[]>
193
+ }) => void;
194
+ };
195
+ onLog?: (evt: EventInfo) => Promise<void>;
196
+ }