prostgles-server 2.0.178 → 2.0.179

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 (98) hide show
  1. package/dist/AuthHandler.d.ts +4 -4
  2. package/dist/AuthHandler.d.ts.map +1 -1
  3. package/dist/DBSchemaBuilder.d.ts +6 -6
  4. package/dist/DBSchemaBuilder.d.ts.map +1 -1
  5. package/dist/DBSchemaBuilder.js +25 -8
  6. package/dist/DBSchemaBuilder.js.map +1 -1
  7. package/dist/DboBuilder.d.ts +20 -21
  8. package/dist/DboBuilder.d.ts.map +1 -1
  9. package/dist/DboBuilder.js +1 -1
  10. package/dist/DboBuilder.js.map +1 -1
  11. package/dist/Prostgles.d.ts +8 -10
  12. package/dist/Prostgles.d.ts.map +1 -1
  13. package/dist/Prostgles.js.map +1 -1
  14. package/dist/PublishParser.d.ts +37 -37
  15. package/dist/PublishParser.d.ts.map +1 -1
  16. package/dist/PublishParser.js.map +1 -1
  17. package/dist/index.d.ts +2 -3
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/lib/AuthHandler.d.ts +148 -0
  21. package/lib/AuthHandler.d.ts.map +1 -0
  22. package/lib/AuthHandler.js +411 -0
  23. package/lib/AuthHandler.ts +3 -3
  24. package/lib/DBEventsManager.d.ts +38 -0
  25. package/lib/DBEventsManager.d.ts.map +1 -0
  26. package/lib/DBEventsManager.js +136 -0
  27. package/lib/DBSchemaBuilder.d.ts +11 -0
  28. package/lib/DBSchemaBuilder.d.ts.map +1 -0
  29. package/lib/DBSchemaBuilder.js +102 -0
  30. package/lib/DBSchemaBuilder.ts +62 -27
  31. package/lib/DboBuilder.d.ts +428 -0
  32. package/lib/DboBuilder.d.ts.map +1 -0
  33. package/lib/DboBuilder.js +3078 -0
  34. package/lib/DboBuilder.ts +25 -25
  35. package/lib/FileManager.d.ts +168 -0
  36. package/lib/FileManager.d.ts.map +1 -0
  37. package/lib/FileManager.js +474 -0
  38. package/lib/Filtering.d.ts +15 -0
  39. package/lib/Filtering.d.ts.map +1 -0
  40. package/lib/Filtering.js +299 -0
  41. package/lib/PostgresNotifListenManager.d.ts +27 -0
  42. package/lib/PostgresNotifListenManager.d.ts.map +1 -0
  43. package/lib/PostgresNotifListenManager.js +122 -0
  44. package/lib/Prostgles.d.ts +193 -0
  45. package/lib/Prostgles.d.ts.map +1 -0
  46. package/lib/Prostgles.js +579 -0
  47. package/lib/Prostgles.ts +6 -6
  48. package/lib/PubSubManager.d.ts +157 -0
  49. package/lib/PubSubManager.d.ts.map +1 -0
  50. package/lib/PubSubManager.js +1400 -0
  51. package/lib/PublishParser.d.ts +262 -0
  52. package/lib/PublishParser.d.ts.map +1 -0
  53. package/lib/PublishParser.js +390 -0
  54. package/lib/PublishParser.ts +39 -38
  55. package/lib/QueryBuilder.d.ts +124 -0
  56. package/lib/QueryBuilder.d.ts.map +1 -0
  57. package/lib/QueryBuilder.js +1349 -0
  58. package/lib/SyncReplication.d.ts +34 -0
  59. package/lib/SyncReplication.d.ts.map +1 -0
  60. package/lib/SyncReplication.js +411 -0
  61. package/lib/TableConfig.d.ts +175 -0
  62. package/lib/TableConfig.d.ts.map +1 -0
  63. package/lib/TableConfig.js +231 -0
  64. package/lib/index.d.ts +10 -0
  65. package/lib/index.d.ts.map +1 -0
  66. package/lib/index.js +45 -0
  67. package/lib/index.ts +3 -4
  68. package/lib/shortestPath.d.ts +10 -0
  69. package/lib/shortestPath.d.ts.map +1 -0
  70. package/lib/shortestPath.js +111 -0
  71. package/lib/utils.d.ts +2 -0
  72. package/lib/utils.d.ts.map +1 -0
  73. package/lib/utils.js +5 -0
  74. package/package.json +3 -3
  75. package/tests/client/PID.txt +1 -1
  76. package/tests/client/index.d.ts +1 -1
  77. package/tests/client/index.d.ts.map +1 -1
  78. package/tests/client_only_queries.d.ts +4 -0
  79. package/tests/client_only_queries.d.ts.map +1 -0
  80. package/tests/isomorphic_queries.d.ts +6 -0
  81. package/tests/isomorphic_queries.d.ts.map +1 -0
  82. package/tests/server/DBoGenerated.d.ts +97 -193
  83. package/tests/server/dboTypeCheck.d.ts +2 -0
  84. package/tests/server/dboTypeCheck.d.ts.map +1 -0
  85. package/tests/server/dboTypeCheck.js +14 -0
  86. package/tests/server/dboTypeCheck.ts +17 -0
  87. package/tests/server/index.d.ts +2 -0
  88. package/tests/server/index.d.ts.map +1 -0
  89. package/tests/server/index.js +11 -11
  90. package/tests/server/index.ts +23 -16
  91. package/tests/server/package-lock.json +5 -5
  92. package/tests/server/publishTypeCheck.d.ts +2 -0
  93. package/tests/server/publishTypeCheck.d.ts.map +1 -0
  94. package/tests/server/publishTypeCheck.js +120 -0
  95. package/tests/server/publishTypeCheck.ts +129 -0
  96. package/tests/server/tsconfig.json +4 -5
  97. package/tests/server_only_queries.d.ts +2 -0
  98. package/tests/server_only_queries.d.ts.map +1 -0
@@ -0,0 +1,579 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Stefan L. All rights reserved.
4
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ var __importDefault = (this && this.__importDefault) || function (mod) {
30
+ return (mod && mod.__esModule) ? mod : { "default": mod };
31
+ };
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.isSuperUser = exports.Prostgles = exports.JOIN_TYPES = exports.TABLE_METHODS = void 0;
34
+ const promise = __importStar(require("bluebird"));
35
+ const pgPromise = __importStar(require("pg-promise"));
36
+ const FileManager_1 = __importDefault(require("./FileManager"));
37
+ const pkgj = require('../package.json');
38
+ const version = pkgj.version;
39
+ const AuthHandler_1 = __importDefault(require("./AuthHandler"));
40
+ console.log("Add a basic auth mode where user and sessions table are created");
41
+ const TableConfig_1 = __importDefault(require("./TableConfig"));
42
+ const utils_1 = require("./utils");
43
+ const DboBuilder_1 = require("./DboBuilder");
44
+ const PubSubManager_1 = require("./PubSubManager");
45
+ const prostgles_types_1 = require("prostgles-types");
46
+ const PublishParser_1 = require("./PublishParser");
47
+ const DBEventsManager_1 = require("./DBEventsManager");
48
+ exports.TABLE_METHODS = ["update", "find", "findOne", "insert", "delete", "upsert"];
49
+ function getDbConnection(dbConnection, options, debugQueries = false, onNotice) {
50
+ let pgp = pgPromise({
51
+ promiseLib: promise,
52
+ ...(debugQueries ? {
53
+ query: function (e) {
54
+ console.log({ psql: e.query, params: e.params });
55
+ },
56
+ } : {}),
57
+ ...((onNotice || debugQueries) ? {
58
+ connect: function (client, dc, isFresh) {
59
+ if (isFresh && !client.listeners('notice').length) {
60
+ client.on('notice', function (msg) {
61
+ if (onNotice) {
62
+ onNotice(msg, (0, utils_1.get)(msg, "message"));
63
+ }
64
+ else {
65
+ console.log("notice: %j", (0, utils_1.get)(msg, "message"));
66
+ }
67
+ });
68
+ }
69
+ if (isFresh && !client.listeners('error').length) {
70
+ client.on('error', function (msg) {
71
+ if (onNotice) {
72
+ onNotice(msg, (0, utils_1.get)(msg, "message"));
73
+ }
74
+ else {
75
+ console.log("error: %j", (0, utils_1.get)(msg, "message"));
76
+ }
77
+ });
78
+ }
79
+ },
80
+ } : {})
81
+ });
82
+ pgp.pg.defaults.max = 70;
83
+ // /* Casts count/sum/max to bigint. Needs rework to remove casting "+count" and other issues; */
84
+ // pgp.pg.types.setTypeParser(20, BigInt);
85
+ if (options) {
86
+ Object.assign(pgp.pg.defaults, options);
87
+ }
88
+ return {
89
+ db: pgp(dbConnection),
90
+ pgp
91
+ };
92
+ }
93
+ exports.JOIN_TYPES = ["one-many", "many-one", "one-one", "many-many"];
94
+ const DEFAULT_KEYWORDS = {
95
+ $filter: "$filter",
96
+ $and: "$and",
97
+ $or: "$or",
98
+ $not: "$not"
99
+ };
100
+ const fs = __importStar(require("fs"));
101
+ class Prostgles {
102
+ constructor(params) {
103
+ this.opts = {
104
+ DEBUG_MODE: false,
105
+ dbConnection: {
106
+ host: "localhost",
107
+ port: 5432,
108
+ application_name: "prostgles_app"
109
+ },
110
+ onReady: () => { },
111
+ schema: "public",
112
+ watchSchema: false,
113
+ watchSchemaType: "queries",
114
+ };
115
+ this.keywords = DEFAULT_KEYWORDS;
116
+ this.loaded = false;
117
+ this.destroyed = false;
118
+ this.refreshDBO = async () => {
119
+ if (this._dboBuilder)
120
+ this._dboBuilder.destroy();
121
+ this.dboBuilder = await DboBuilder_1.DboBuilder.create(this);
122
+ if (!this.dboBuilder)
123
+ throw "this.dboBuilder";
124
+ this.dbo = this.dboBuilder.dbo;
125
+ return this.dbo;
126
+ };
127
+ this.isSuperUser = false;
128
+ this.connectedSockets = [];
129
+ this.pushSocketSchema = async (socket) => {
130
+ let auth = await this.authHandler?.makeSocketAuth(socket) || {};
131
+ // let needType = this.publishRawSQL && typeof this.publishRawSQL === "function";
132
+ // let DATA_TYPES = !needType? [] : await this.db.any("SELECT oid, typname FROM pg_type");
133
+ // let USER_TABLES = !needType? [] : await this.db.any("SELECT relid, relname FROM pg_catalog.pg_statio_user_tables");
134
+ const { dbo, db, pgp, publishParser } = this;
135
+ let fullSchema;
136
+ let publishValidationError;
137
+ let rawSQL = false;
138
+ try {
139
+ if (!publishParser)
140
+ throw "publishParser undefined";
141
+ fullSchema = await publishParser.getSchemaFromPublish(socket);
142
+ }
143
+ catch (e) {
144
+ publishValidationError = "Server Error: PUBLISH VALIDATION ERROR";
145
+ console.error(`\nProstgles PUBLISH VALIDATION ERROR (after socket connected):\n ->`, e);
146
+ }
147
+ socket.prostgles = socket.prostgles || {};
148
+ socket.prostgles.schema = fullSchema?.schema;
149
+ /* RUN Raw sql from client IF PUBLISHED
150
+ */
151
+ if (this.opts.publishRawSQL && typeof this.opts.publishRawSQL === "function") {
152
+ const canRunSQL = async () => {
153
+ const publishParams = await this.publishParser?.getPublishParams({ socket });
154
+ let res = await this.opts.publishRawSQL?.(publishParams);
155
+ return Boolean(res && typeof res === "boolean" || res === "*");
156
+ };
157
+ if (await canRunSQL()) {
158
+ socket.removeAllListeners(prostgles_types_1.CHANNELS.SQL);
159
+ socket.on(prostgles_types_1.CHANNELS.SQL, async ({ query, params, options }, cb = (...callback) => { }) => {
160
+ if (!this.dbo?.sql)
161
+ throw "Internal error: sql handler missing";
162
+ this.dbo.sql(query, params, options, { socket }).then(res => {
163
+ cb(null, res);
164
+ }).catch(err => {
165
+ makeSocketError(cb, err);
166
+ });
167
+ });
168
+ if (db) {
169
+ // let allTablesViews = await db.any(STEP2_GET_ALL_TABLES_AND_COLUMNS);
170
+ // fullSchema = allTablesViews;
171
+ rawSQL = true;
172
+ }
173
+ else
174
+ console.error("db missing");
175
+ }
176
+ }
177
+ const { schema, tables } = fullSchema ?? { schema: {}, tables: [] };
178
+ let joinTables2 = [];
179
+ if (this.opts.joins) {
180
+ // joinTables = Array.from(new Set(flat(this.dboBuilder.getJoins().map(j => j.tables)).filter(t => schema[t])));
181
+ let _joinTables2 = this.dboBuilder.getJoinPaths()
182
+ .filter(jp => ![jp.t1, jp.t2].find(t => !schema[t] || !schema[t].findOne)).map(jp => [jp.t1, jp.t2].sort());
183
+ _joinTables2.map(jt => {
184
+ if (!joinTables2.find(_jt => _jt.join() === jt.join())) {
185
+ joinTables2.push(jt);
186
+ }
187
+ });
188
+ }
189
+ const methods = await publishParser?.getMethods(socket);
190
+ const clientSchema = {
191
+ schema,
192
+ methods: (0, prostgles_types_1.getKeys)(methods),
193
+ tableSchema: tables,
194
+ rawSQL,
195
+ joinTables: joinTables2,
196
+ auth,
197
+ version,
198
+ err: publishValidationError
199
+ };
200
+ socket.emit(prostgles_types_1.CHANNELS.SCHEMA, clientSchema);
201
+ };
202
+ if (!params)
203
+ throw "ProstglesInitOptions missing";
204
+ if (!params.io)
205
+ console.warn("io missing. WebSockets will not be set up");
206
+ // TODO: find an exact keyof T<->arr TS matching method
207
+ let config = [
208
+ "transactions", "joins", "tsGeneratedTypesDir",
209
+ "onReady", "dbConnection", "dbOptions", "publishMethods", "io",
210
+ "publish", "schema", "publishRawSQL", "wsChannelNamePrefix", "onSocketConnect",
211
+ "onSocketDisconnect", "sqlFilePath", "auth", "DEBUG_MODE", "watchSchema", "watchSchemaType",
212
+ "fileTable", "tableConfig"
213
+ ];
214
+ const unknownParams = Object.keys(params).filter((key) => !config.includes(key));
215
+ if (unknownParams.length) {
216
+ console.error(`Unrecognised ProstglesInitOptions params: ${unknownParams.join()}`);
217
+ }
218
+ Object.assign(this.opts, params);
219
+ /* set defaults */
220
+ if (this.opts?.fileTable) {
221
+ this.opts.fileTable.tableName = this.opts?.fileTable?.tableName || "media";
222
+ }
223
+ this.opts.schema = this.opts.schema || "public";
224
+ this.keywords = {
225
+ ...DEFAULT_KEYWORDS,
226
+ ...params.keywords,
227
+ };
228
+ }
229
+ get dboBuilder() {
230
+ if (!this._dboBuilder)
231
+ throw "get dboBuilder: it's undefined";
232
+ return this._dboBuilder;
233
+ }
234
+ set dboBuilder(d) {
235
+ this._dboBuilder = d;
236
+ }
237
+ isMedia(tableName) {
238
+ return this.opts?.fileTable?.tableName === tableName;
239
+ }
240
+ async onSchemaChange(event) {
241
+ const { watchSchema, onReady, tsGeneratedTypesDir } = this.opts;
242
+ if (watchSchema && this.loaded) {
243
+ console.log("Schema changed");
244
+ const { query } = event;
245
+ if (typeof query === "string" && query.includes(PubSubManager_1.PubSubManager.EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID)) {
246
+ console.log("Schema change event excluded from triggers due to EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID");
247
+ return;
248
+ }
249
+ if (typeof watchSchema === "function") {
250
+ /* Only call the provided func */
251
+ watchSchema(event);
252
+ }
253
+ else if (watchSchema === "hotReloadMode") {
254
+ if (tsGeneratedTypesDir) {
255
+ /* Hot reload integration. Will only touch tsGeneratedTypesDir */
256
+ console.log("watchSchema: Re-writing TS schema");
257
+ await this.refreshDBO();
258
+ this.writeDBSchema(true);
259
+ }
260
+ }
261
+ else if (watchSchema === true || "checkIntervalMillis" in watchSchema) {
262
+ /* Full re-init. Sockets must reconnect */
263
+ console.log("watchSchema: Full re-initialisation");
264
+ this.init(onReady);
265
+ }
266
+ }
267
+ }
268
+ checkDb() {
269
+ if (!this.db || !this.db.connect)
270
+ throw "something went wrong getting a db connection";
271
+ }
272
+ getTSFileName() {
273
+ const fileName = "DBoGenerated.d.ts"; //`dbo_${this.schema}_types.ts`;
274
+ const fullPath = (this.opts.tsGeneratedTypesDir || "") + fileName;
275
+ return { fileName, fullPath };
276
+ }
277
+ getFileText(fullPath, format = "utf8") {
278
+ return new Promise((resolve, reject) => {
279
+ fs.readFile(fullPath, 'utf8', function (err, data) {
280
+ if (err)
281
+ reject(err);
282
+ else
283
+ resolve(data);
284
+ });
285
+ });
286
+ }
287
+ writeDBSchema(force = false) {
288
+ if (this.opts.tsGeneratedTypesDir) {
289
+ const { fullPath, fileName } = this.getTSFileName();
290
+ const header = `/* This file was generated by Prostgles \n` +
291
+ // `* ${(new Date).toUTCString()} \n`
292
+ `*/ \n\n `;
293
+ const fileContent = header + this.dboBuilder.tsTypesDefinition;
294
+ fs.readFile(fullPath, 'utf8', function (err, data) {
295
+ if (err || (force || data !== fileContent)) {
296
+ fs.writeFileSync(fullPath, fileContent);
297
+ console.log("Prostgles: Created typescript schema definition file: \n " + fileName);
298
+ }
299
+ });
300
+ }
301
+ else if (force) {
302
+ console.error("Schema changed. tsGeneratedTypesDir needs to be set to reload server");
303
+ }
304
+ }
305
+ async init(onReady) {
306
+ this.loaded = false;
307
+ if (this.opts.watchSchema === "hotReloadMode" && !this.opts.tsGeneratedTypesDir) {
308
+ throw "tsGeneratedTypesDir option is needed for watchSchema: hotReloadMode to work ";
309
+ }
310
+ else if (this.opts.watchSchema &&
311
+ typeof this.opts.watchSchema === "object" &&
312
+ "checkIntervalMillis" in this.opts.watchSchema &&
313
+ typeof this.opts.watchSchema.checkIntervalMillis === "number") {
314
+ if (this.schema_checkIntervalMillis) {
315
+ clearInterval(this.schema_checkIntervalMillis);
316
+ this.schema_checkIntervalMillis = setInterval(async () => {
317
+ const dbuilder = await DboBuilder_1.DboBuilder.create(this);
318
+ if (dbuilder.tsTypesDefinition !== this.dboBuilder.tsTypesDefinition) {
319
+ this.refreshDBO();
320
+ this.init(onReady);
321
+ }
322
+ }, this.opts.watchSchema.checkIntervalMillis);
323
+ }
324
+ }
325
+ /* 1. Connect to db */
326
+ if (!this.db) {
327
+ const { db, pgp } = getDbConnection(this.opts.dbConnection, this.opts.dbOptions, this.opts.DEBUG_MODE, notice => {
328
+ if (this.opts.onNotice)
329
+ this.opts.onNotice(notice);
330
+ if (this.dbEventsManager) {
331
+ this.dbEventsManager.onNotice(notice);
332
+ }
333
+ });
334
+ this.db = db;
335
+ this.pgp = pgp;
336
+ this.isSuperUser = await isSuperUser(db);
337
+ }
338
+ this.checkDb();
339
+ const db = this.db;
340
+ const pgp = this.pgp;
341
+ /* 2. Execute any SQL file if provided */
342
+ if (this.opts.sqlFilePath) {
343
+ await this.runSQLFile(this.opts.sqlFilePath);
344
+ }
345
+ try {
346
+ await this.refreshDBO();
347
+ if (this.opts.tableConfig) {
348
+ this.tableConfigurator = new TableConfig_1.default(this);
349
+ try {
350
+ await this.tableConfigurator.init();
351
+ }
352
+ catch (e) {
353
+ console.error("TableConfigurator: ", e);
354
+ throw e;
355
+ }
356
+ }
357
+ /* 3. Make DBO object from all tables and views */
358
+ await this.refreshDBO();
359
+ /* Create media table if required */
360
+ if (this.opts.fileTable) {
361
+ const { awsS3Config, localConfig, imageOptions } = this.opts.fileTable;
362
+ await this.refreshDBO();
363
+ if (!awsS3Config && !localConfig)
364
+ throw "fileTable missing param: Must provide awsS3Config OR localConfig";
365
+ //@ts-ignore
366
+ this.fileManager = new FileManager_1.default(awsS3Config || localConfig, imageOptions);
367
+ try {
368
+ await this.fileManager.init(this);
369
+ }
370
+ catch (e) {
371
+ console.error("FileManager: ", e);
372
+ throw e;
373
+ }
374
+ }
375
+ await this.refreshDBO();
376
+ if (this.opts.publish) {
377
+ if (!this.opts.io)
378
+ console.warn("IO missing. Publish has no effect without io");
379
+ /* 3.9 Check auth config */
380
+ this.authHandler = new AuthHandler_1.default(this);
381
+ await this.authHandler.init();
382
+ this.publishParser = new PublishParser_1.PublishParser(this.opts.publish, this.opts.publishMethods, this.opts.publishRawSQL, this.dbo, this.db, this);
383
+ this.dboBuilder.publishParser = this.publishParser;
384
+ /* 4. Set publish and auth listeners */
385
+ await this.setSocketEvents();
386
+ }
387
+ else if (this.opts.auth)
388
+ throw "Auth config does not work without publish";
389
+ // if(this.watchSchema){
390
+ // if(!(await isSuperUser(db))) throw "Cannot watchSchema without a super user schema. Set watchSchema=false or provide a super user";
391
+ // }
392
+ this.dbEventsManager = new DBEventsManager_1.DBEventsManager(db, pgp);
393
+ this.writeDBSchema();
394
+ /* 5. Finish init and provide DBO object */
395
+ try {
396
+ if (this.destroyed) {
397
+ console.trace(1);
398
+ }
399
+ onReady(this.dbo, this.db);
400
+ }
401
+ catch (err) {
402
+ console.error("Prostgles: Error within onReady: \n", err);
403
+ }
404
+ this.loaded = true;
405
+ return {
406
+ db: this.dbo,
407
+ _db: db,
408
+ pgp,
409
+ io: this.opts.io,
410
+ destroy: async () => {
411
+ console.log("destroying prgl instance");
412
+ this.destroyed = true;
413
+ if (this.opts.io) {
414
+ this.opts.io.on("connection", () => {
415
+ console.log("Socket connected to destroyed instance");
416
+ });
417
+ if (typeof this.opts.io.close === "function") {
418
+ this.opts.io.close();
419
+ console.log("this.io.close");
420
+ }
421
+ }
422
+ this.dboBuilder?.destroy();
423
+ this.dbo = undefined;
424
+ this.db = undefined;
425
+ await db.$pool.end();
426
+ await sleep(1000);
427
+ return true;
428
+ }
429
+ };
430
+ }
431
+ catch (e) {
432
+ console.trace(e);
433
+ // @ts-ignore
434
+ throw "init issues: " + e.toString();
435
+ }
436
+ }
437
+ async runSQLFile(filePath) {
438
+ const fileContent = await this.getFileText(filePath); //.then(console.log);
439
+ return this.db?.multi(fileContent).then((data) => {
440
+ console.log("Prostgles: SQL file executed successfuly \n -> " + filePath);
441
+ return data;
442
+ }).catch((err) => {
443
+ const { position, length } = err, lines = fileContent.split("\n");
444
+ let errMsg = filePath + " error: ";
445
+ if (position && length && fileContent) {
446
+ const startLine = Math.max(0, fileContent.substring(0, position).split("\n").length - 2), endLine = startLine + 3;
447
+ errMsg += "\n\n";
448
+ errMsg += lines.slice(startLine, endLine).map((txt, i) => `${startLine + i + 1} ${i === 1 ? "->" : " "} ${txt}`).join("\n");
449
+ errMsg += "\n\n";
450
+ }
451
+ console.error(errMsg, err);
452
+ throw err;
453
+ });
454
+ }
455
+ async setSocketEvents() {
456
+ this.checkDb();
457
+ if (!this.dbo)
458
+ throw "dbo missing";
459
+ let publishParser = new PublishParser_1.PublishParser(this.opts.publish, this.opts.publishMethods, this.opts.publishRawSQL, this.dbo, this.db, this);
460
+ this.publishParser = publishParser;
461
+ if (!this.opts.io)
462
+ return;
463
+ /* Already initialised. Only reconnect sockets */
464
+ if (this.connectedSockets.length) {
465
+ this.connectedSockets.forEach((s) => {
466
+ s.emit(prostgles_types_1.CHANNELS.SCHEMA_CHANGED);
467
+ this.pushSocketSchema(s);
468
+ });
469
+ return;
470
+ }
471
+ /* Initialise */
472
+ this.opts.io.on('connection', async (socket) => {
473
+ if (this.destroyed) {
474
+ console.log("Socket connected to destroyed instance");
475
+ socket.disconnect();
476
+ return;
477
+ }
478
+ this.connectedSockets.push(socket);
479
+ if (!this.db || !this.dbo)
480
+ throw "db/dbo missing";
481
+ let { dbo, db, pgp } = this;
482
+ try {
483
+ if (this.opts.onSocketConnect)
484
+ await this.opts.onSocketConnect(socket, dbo, db);
485
+ /* RUN Client request from Publish.
486
+ Checks request against publish and if OK run it with relevant publish functions. Local (server) requests do not check the policy
487
+ */
488
+ socket.removeAllListeners(prostgles_types_1.CHANNELS.DEFAULT);
489
+ socket.on(prostgles_types_1.CHANNELS.DEFAULT, async ({ tableName, command, param1, param2, param3 }, cb = (...callback) => { }) => {
490
+ try { /* Channel name will only include client-sent params so we ignore table_rules enforced params */
491
+ if (!socket || !this.authHandler || !this.publishParser || !this.dbo) {
492
+ console.error("socket or authhandler missing??!!");
493
+ throw "socket or authhandler missing??!!";
494
+ }
495
+ const clientInfo = await this.authHandler.getClientInfo({ socket });
496
+ let valid_table_command_rules = await this.publishParser.getValidatedRequestRule({ tableName, command, localParams: { socket } }, clientInfo);
497
+ if (valid_table_command_rules) {
498
+ //@ts-ignore
499
+ let res = await this.dbo[tableName][command](param1, param2, param3, valid_table_command_rules, { socket, has_rules: true });
500
+ cb(null, res);
501
+ }
502
+ else
503
+ throw `Invalid OR disallowed request: ${tableName}.${command} `;
504
+ }
505
+ catch (err) {
506
+ // const _err_msg = err.toString();
507
+ // cb({ msg: _err_msg, err });
508
+ console.trace(err);
509
+ cb(err);
510
+ // console.warn("runPublishedRequest ERROR: ", err, socket._user);
511
+ }
512
+ });
513
+ socket.on("disconnect", () => {
514
+ this.dbEventsManager?.removeNotice(socket);
515
+ this.dbEventsManager?.removeNotify(undefined, socket);
516
+ this.connectedSockets = this.connectedSockets.filter(s => s.id !== socket.id);
517
+ // subscriptions = subscriptions.filter(sub => sub.socket.id !== socket.id);
518
+ if (this.opts.onSocketDisconnect) {
519
+ this.opts.onSocketDisconnect(socket, dbo);
520
+ }
521
+ ;
522
+ });
523
+ socket.removeAllListeners(prostgles_types_1.CHANNELS.METHOD);
524
+ socket.on(prostgles_types_1.CHANNELS.METHOD, async ({ method, params }, cb = (...callback) => { }) => {
525
+ try {
526
+ const methods = await this.publishParser?.getMethods(socket);
527
+ if (!methods || !methods[method]) {
528
+ cb("Disallowed/missing method " + JSON.stringify(method));
529
+ }
530
+ else {
531
+ try {
532
+ const res = await methods[method](...params);
533
+ cb(null, res);
534
+ }
535
+ catch (err) {
536
+ makeSocketError(cb, err);
537
+ }
538
+ }
539
+ }
540
+ catch (err) {
541
+ makeSocketError(cb, err);
542
+ console.warn("method ERROR: ", err, socket._user);
543
+ }
544
+ });
545
+ this.pushSocketSchema(socket);
546
+ }
547
+ catch (e) {
548
+ console.trace("setSocketEvents: ", e);
549
+ }
550
+ });
551
+ }
552
+ }
553
+ exports.Prostgles = Prostgles;
554
+ function makeSocketError(cb, err) {
555
+ const err_msg = (err instanceof Error) ?
556
+ err.toString() :
557
+ (0, DboBuilder_1.isPlainObject)(err) ?
558
+ JSON.stringify(err, null, 2) :
559
+ err.toString(), e = { err_msg, err };
560
+ cb(e);
561
+ }
562
+ // const ALL_PUBLISH_METHODS = ["update", "upsert", "delete", "insert", "find", "findOne", "subscribe", "unsubscribe", "sync", "unsync", "remove"];
563
+ // const ALL_PUBLISH_METHODS = RULE_TO_METHODS.map(r => r.methods).flat();
564
+ // export function flat(arr){
565
+ // // let res = arr.reduce((acc, val) => [ ...acc, ...val ], []);
566
+ // let res = arr.reduce(function (farr, toFlatten) {
567
+ // return farr.concat(Array.isArray(toFlatten) ? flat(toFlatten) : toFlatten);
568
+ // }, []);
569
+ // return res;
570
+ // }
571
+ async function isSuperUser(db) {
572
+ return db.oneOrNone("select usesuper from pg_user where usename = CURRENT_USER;").then(r => r.usesuper);
573
+ }
574
+ exports.isSuperUser = isSuperUser;
575
+ function sleep(ms) {
576
+ return new Promise((resolve) => {
577
+ setTimeout(resolve, ms);
578
+ });
579
+ }
package/lib/Prostgles.ts CHANGED
@@ -165,7 +165,7 @@ export type FileTableConfig = {
165
165
  imageOptions?: ImageOptions
166
166
  };
167
167
 
168
- export type ProstglesInitOptions<S extends DBSchema | undefined = undefined> = {
168
+ export type ProstglesInitOptions<S = void> = {
169
169
  dbConnection: DbConnection;
170
170
  dbOptions?: DbConnectionOpts;
171
171
  tsGeneratedTypesDir?: string;
@@ -246,9 +246,9 @@ const DEFAULT_KEYWORDS = {
246
246
 
247
247
  import * as fs from 'fs';
248
248
  import { DBOFullyTyped } from "./DBSchemaBuilder";
249
- export class Prostgles<S extends DBSchema | undefined = undefined> {
249
+ export class Prostgles {
250
250
 
251
- opts: ProstglesInitOptions<S> = {
251
+ opts: ProstglesInitOptions = {
252
252
  DEBUG_MODE: false,
253
253
  dbConnection: {
254
254
  host: "localhost",
@@ -280,7 +280,7 @@ export class Prostgles<S extends DBSchema | undefined = undefined> {
280
280
  }
281
281
  publishParser?: PublishParser;
282
282
 
283
- authHandler?: AuthHandler<S>;
283
+ authHandler?: AuthHandler;
284
284
 
285
285
 
286
286
  keywords = DEFAULT_KEYWORDS;
@@ -409,8 +409,8 @@ export class Prostgles<S extends DBSchema | undefined = undefined> {
409
409
 
410
410
  isSuperUser = false;
411
411
  schema_checkIntervalMillis: any;
412
- async init(onReady: (dbo: DBOFullyTyped<S>, db: DB) => any): Promise<{
413
- db: DBOFullyTyped<S>;
412
+ async init(onReady: (dbo: DBOFullyTyped, db: DB) => any): Promise<{
413
+ db: DBOFullyTyped;
414
414
  _db: DB;
415
415
  pgp: PGP;
416
416
  io?: any;