prostgles-server 2.0.144 → 2.0.147
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.
- package/dist/AuthHandler.d.ts +15 -13
- package/dist/AuthHandler.d.ts.map +1 -1
- package/dist/AuthHandler.js +41 -43
- package/dist/AuthHandler.js.map +1 -1
- package/dist/DBEventsManager.d.ts +6 -5
- package/dist/DBEventsManager.d.ts.map +1 -1
- package/dist/DBEventsManager.js +8 -2
- package/dist/DBEventsManager.js.map +1 -1
- package/dist/DboBuilder.d.ts +62 -50
- package/dist/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder.js +229 -170
- package/dist/DboBuilder.js.map +1 -1
- package/dist/FileManager.d.ts +5 -5
- package/dist/FileManager.d.ts.map +1 -1
- package/dist/FileManager.js +48 -21
- package/dist/FileManager.js.map +1 -1
- package/dist/Filtering.d.ts.map +1 -1
- package/dist/Filtering.js +11 -9
- package/dist/Filtering.js.map +1 -1
- package/dist/PostgresNotifListenManager.d.ts +3 -3
- package/dist/PostgresNotifListenManager.d.ts.map +1 -1
- package/dist/PostgresNotifListenManager.js +7 -5
- package/dist/PostgresNotifListenManager.js.map +1 -1
- package/dist/Prostgles.d.ts +6 -9
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +122 -83
- package/dist/Prostgles.js.map +1 -1
- package/dist/PubSubManager.d.ts +9 -9
- package/dist/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager.js +10 -9
- package/dist/PubSubManager.js.map +1 -1
- package/dist/QueryBuilder.d.ts +26 -8
- package/dist/QueryBuilder.d.ts.map +1 -1
- package/dist/QueryBuilder.js +114 -46
- package/dist/QueryBuilder.js.map +1 -1
- package/dist/SyncReplication.d.ts +1 -1
- package/dist/SyncReplication.d.ts.map +1 -1
- package/dist/SyncReplication.js +31 -29
- package/dist/SyncReplication.js.map +1 -1
- package/dist/TableConfig.d.ts +0 -1
- package/dist/TableConfig.d.ts.map +1 -1
- package/dist/TableConfig.js +25 -17
- package/dist/TableConfig.js.map +1 -1
- package/dist/shortestPath.d.ts.map +1 -1
- package/dist/shortestPath.js.map +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -0
- package/dist/utils.js.map +1 -1
- package/lib/AuthHandler.ts +50 -40
- package/lib/DBEventsManager.ts +14 -7
- package/lib/DboBuilder.ts +265 -199
- package/lib/FileManager.ts +30 -21
- package/lib/Filtering.ts +19 -16
- package/lib/PostgresNotifListenManager.ts +11 -10
- package/lib/Prostgles.ts +89 -73
- package/lib/PubSubManager.ts +13 -11
- package/lib/QueryBuilder.ts +135 -54
- package/lib/SyncReplication.ts +10 -10
- package/lib/TableConfig.ts +23 -15
- package/lib/shortestPath.ts +6 -4
- package/lib/utils.ts +7 -1
- package/package.json +7 -8
- package/tests/client/PID.txt +1 -1
- package/tests/client/index.js +10 -7
- package/tests/client/index.ts +12 -8
- package/tests/client/package-lock.json +14 -14
- package/tests/client/package.json +2 -2
- package/tests/client/tsconfig.json +2 -2
- package/tests/client_only_queries.js +127 -104
- package/tests/client_only_queries.ts +43 -17
- package/tests/isomorphic_queries.js +44 -6
- package/tests/isomorphic_queries.ts +42 -6
- package/tests/server/package-lock.json +27 -29
- package/tests/server/package.json +2 -2
- package/tests/server/tsconfig.json +2 -2
- package/tsconfig.json +3 -2
package/lib/FileManager.ts
CHANGED
|
@@ -8,7 +8,7 @@ import * as sharp from "sharp";
|
|
|
8
8
|
|
|
9
9
|
import { DB, DbHandler, Prostgles } from './Prostgles';
|
|
10
10
|
import { asName, AnyObject } from 'prostgles-types';
|
|
11
|
-
import { TableHandler } from './DboBuilder';
|
|
11
|
+
import { getKeys, TableHandler } from './DboBuilder';
|
|
12
12
|
|
|
13
13
|
const HOUR = 3600 * 1000;
|
|
14
14
|
|
|
@@ -65,18 +65,24 @@ export type UploadedItem = {
|
|
|
65
65
|
|
|
66
66
|
export default class FileManager {
|
|
67
67
|
|
|
68
|
-
s3Client
|
|
68
|
+
s3Client?: S3;
|
|
69
69
|
|
|
70
70
|
config: S3Config | LocalConfig;
|
|
71
|
-
imageOptions
|
|
72
|
-
|
|
73
|
-
prostgles
|
|
74
|
-
get dbo(): DbHandler {
|
|
75
|
-
|
|
71
|
+
imageOptions?: ImageOptions;
|
|
72
|
+
|
|
73
|
+
prostgles?: Prostgles;
|
|
74
|
+
get dbo(): DbHandler {
|
|
75
|
+
if(!this.prostgles?.dbo) throw "this.prostgles.dbo missing"
|
|
76
|
+
return this.prostgles.dbo
|
|
77
|
+
};
|
|
78
|
+
get db(): DB {
|
|
79
|
+
if(!this.prostgles?.db) throw "this.prostgles.db missing"
|
|
80
|
+
return this.prostgles.db
|
|
81
|
+
};
|
|
76
82
|
|
|
77
|
-
tableName
|
|
83
|
+
tableName?: string;
|
|
78
84
|
|
|
79
|
-
private fileRoute
|
|
85
|
+
private fileRoute?: string;
|
|
80
86
|
|
|
81
87
|
constructor(config: FileManager["config"], imageOptions?: ImageOptions){
|
|
82
88
|
this.config = config;
|
|
@@ -106,7 +112,7 @@ export default class FileManager {
|
|
|
106
112
|
const nameParts = fileName.split(".");
|
|
107
113
|
|
|
108
114
|
const nameExt = nameParts[nameParts.length - 1].toLowerCase(),
|
|
109
|
-
mime =
|
|
115
|
+
mime = getKeys(CONTENT_TYPE_TO_EXT).find(k => (CONTENT_TYPE_TO_EXT[k] as readonly string[]).includes(nameExt));
|
|
110
116
|
|
|
111
117
|
let type = {
|
|
112
118
|
fileName,
|
|
@@ -119,17 +125,17 @@ export default class FileManager {
|
|
|
119
125
|
if(!mime){
|
|
120
126
|
/* Set correct/missing extension */
|
|
121
127
|
if(["xml", "txt", "csv", "tsv", "doc"].includes(nameExt)){
|
|
122
|
-
type = { ...type, mime: "text/" + nameExt, ext: nameExt };
|
|
128
|
+
type = { ...type, mime: ("text/" + nameExt) as any, ext: nameExt };
|
|
123
129
|
} else if(["svg"].includes(nameExt)){
|
|
124
130
|
type = { ...type, mime: "image/svg+xml", ext: nameExt };
|
|
125
131
|
} else if(Buffer.isBuffer(file)){
|
|
126
|
-
const res = await FileType.fromBuffer(file);
|
|
132
|
+
const res: any = await FileType.fromBuffer(file);
|
|
127
133
|
type = {
|
|
128
134
|
...res,
|
|
129
135
|
fileName,
|
|
130
136
|
}
|
|
131
137
|
} else if(typeof file === "string"){
|
|
132
|
-
const res = await FileType.fromFile(file);
|
|
138
|
+
const res: any = await FileType.fromFile(file);
|
|
133
139
|
type = {
|
|
134
140
|
...res,
|
|
135
141
|
fileName,
|
|
@@ -158,10 +164,12 @@ export default class FileManager {
|
|
|
158
164
|
if(nameExt !== ext) fileName = nameParts.slice(0, -1).join('') + "." + ext;
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
|
|
167
|
+
const res = {
|
|
162
168
|
...type,
|
|
163
169
|
fileName
|
|
164
170
|
}
|
|
171
|
+
if(!res.mime) throw "Could not find mime"
|
|
172
|
+
return res as any;
|
|
165
173
|
}
|
|
166
174
|
|
|
167
175
|
// async getUploadURL(fileName: string): Promise<string> {
|
|
@@ -227,7 +235,7 @@ export default class FileManager {
|
|
|
227
235
|
ContentType: mime,
|
|
228
236
|
Body: file
|
|
229
237
|
};
|
|
230
|
-
this.s3Client.upload(params, (err, res: ManagedUpload.SendData) => {
|
|
238
|
+
this.s3Client.upload(params, (err: any, res: ManagedUpload.SendData) => {
|
|
231
239
|
|
|
232
240
|
if(err){
|
|
233
241
|
reject("Something went wrong");
|
|
@@ -279,8 +287,8 @@ export default class FileManager {
|
|
|
279
287
|
}
|
|
280
288
|
}
|
|
281
289
|
_data = await sharp(data)
|
|
282
|
-
.resize(opts)
|
|
283
|
-
.withMetadata(Boolean(imageOptions
|
|
290
|
+
.resize(opts as any)
|
|
291
|
+
.withMetadata(Boolean(imageOptions?.keepMetadata) as any)
|
|
284
292
|
// .jpeg({ quality: 80 })
|
|
285
293
|
.toBuffer()
|
|
286
294
|
} else if(!imageOptions?.keepMetadata) {
|
|
@@ -304,10 +312,10 @@ export default class FileManager {
|
|
|
304
312
|
Key: fileName,
|
|
305
313
|
Expires: expiresInSeconds || 30 * 60
|
|
306
314
|
};
|
|
307
|
-
return await this.s3Client
|
|
315
|
+
return await this.s3Client?.getSignedUrlPromise("getObject", params);
|
|
308
316
|
}
|
|
309
317
|
|
|
310
|
-
private parseSQLIdentifier = async (name: string ) => asSQLIdentifier(name, this.prostgles
|
|
318
|
+
private parseSQLIdentifier = async (name: string ) => asSQLIdentifier(name, this.prostgles!.db!);// this.prostgles.dbo.sql<"value">("select format('%I', $1)", [name], { returnType: "value" } )
|
|
311
319
|
|
|
312
320
|
init = async (prg: Prostgles) => {
|
|
313
321
|
this.prostgles = prg;
|
|
@@ -315,6 +323,7 @@ export default class FileManager {
|
|
|
315
323
|
// const { dbo, db, opts } = prg;
|
|
316
324
|
|
|
317
325
|
const { fileTable } = prg.opts;
|
|
326
|
+
if(!fileTable) throw "fileTable missing";
|
|
318
327
|
const { tableName = "media", referencedTables = {} } = fileTable;
|
|
319
328
|
this.tableName = tableName;
|
|
320
329
|
|
|
@@ -376,7 +385,7 @@ export default class FileManager {
|
|
|
376
385
|
console.log(`Created ${action}`);
|
|
377
386
|
|
|
378
387
|
} else {
|
|
379
|
-
const cols = await this.dbo[lookupTableName].getColumns();
|
|
388
|
+
const cols = await this.dbo[lookupTableName].getColumns!();
|
|
380
389
|
const badCols = cols.filter(c => !c.references);
|
|
381
390
|
await Promise.all(badCols.map(async badCol => {
|
|
382
391
|
console.error(
|
|
@@ -436,7 +445,7 @@ export default class FileManager {
|
|
|
436
445
|
const { name } = req.params;
|
|
437
446
|
if(typeof name !== "string" || !name) throw "Invalid media name";
|
|
438
447
|
|
|
439
|
-
const media = await mediaTable.findOne({ name }, { select: { id: 1, name: 1, signed_url: 1, signed_url_expires: 1, content_type: 1 } }, { httpReq: req });
|
|
448
|
+
const media = await mediaTable.findOne({ name }, { select: { id: 1, name: 1, signed_url: 1, signed_url_expires: 1, content_type: 1 } }, { httpReq: req } as any);
|
|
440
449
|
|
|
441
450
|
if(!media) {
|
|
442
451
|
/**
|
package/lib/Filtering.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { SelectItem } from "./QueryBuilder";
|
|
4
4
|
import { isEmpty, FullFilter, EXISTS_KEYS, FilterDataType, GeomFilterKeys, GeomFilter_Funcs, TextFilter_FullTextSearchFilterKeys } from "prostgles-types";
|
|
5
|
-
import { isPlainObject } from "./DboBuilder";
|
|
5
|
+
import { getKeys, isPlainObject } from "./DboBuilder";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Parse a single filter
|
|
@@ -17,9 +17,9 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
17
17
|
const mErr = (msg: string) => {
|
|
18
18
|
throw `${msg}: ${JSON.stringify(_f, null, 2)}`
|
|
19
19
|
},
|
|
20
|
-
asValue = (v) => pgp.as.format("$1", [v]);
|
|
20
|
+
asValue = (v: any) => pgp.as.format("$1", [v]);
|
|
21
21
|
|
|
22
|
-
const fKeys =
|
|
22
|
+
const fKeys = getKeys(_f)
|
|
23
23
|
if(fKeys.length === 0){
|
|
24
24
|
return "";
|
|
25
25
|
} else if(fKeys.length > 1){
|
|
@@ -33,7 +33,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
33
33
|
.join(" AND ")
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const fKey = fKeys[0];
|
|
36
|
+
const fKey: string = fKeys[0];
|
|
37
37
|
|
|
38
38
|
/* Exists filter */
|
|
39
39
|
if(EXISTS_KEYS.find(k => k in _f)){
|
|
@@ -42,17 +42,17 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
42
42
|
|
|
43
43
|
let selItem: SelectItem | undefined;
|
|
44
44
|
if(select) selItem = select.find(s => fKey === s.alias);
|
|
45
|
-
let rightF: FilterDataType = _f[fKey];
|
|
45
|
+
let rightF: FilterDataType = (_f as any)[fKey];
|
|
46
46
|
|
|
47
47
|
const getLeftQ = (selItm: SelectItem) => {
|
|
48
|
-
if(selItm.type === "function") return
|
|
49
|
-
return
|
|
48
|
+
if(selItm.type === "function") return selItm.getQuery();
|
|
49
|
+
return selItm.getQuery(tableAlias);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Parsed left side of the query
|
|
54
54
|
*/
|
|
55
|
-
let leftQ: string;// = asName(selItem.alias);
|
|
55
|
+
let leftQ: string | undefined;// = asName(selItem.alias);
|
|
56
56
|
|
|
57
57
|
/* Check if string notation. Build obj if necessary */
|
|
58
58
|
const dot_notation_delims = ["->", "."];
|
|
@@ -65,7 +65,10 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
65
65
|
dot_notation_delims.find(dn => fKey.slice(s.alias.length).startsWith(dn))
|
|
66
66
|
);
|
|
67
67
|
}
|
|
68
|
-
if(!selItem)
|
|
68
|
+
if(!selItem) {
|
|
69
|
+
mErr("Bad filter. Could not match to a column or alias: ");
|
|
70
|
+
throw " "
|
|
71
|
+
}
|
|
69
72
|
const remainingStr = fKey.slice(selItem.alias.length);
|
|
70
73
|
|
|
71
74
|
/* Is json path spec */
|
|
@@ -135,7 +138,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
const key = remainingStr.slice(currIdx + 1, nIdx);
|
|
138
|
-
curObj[key] = nextIdx > -1? {} : _f[fKey];
|
|
141
|
+
curObj[key] = nextIdx > -1? {} : (_f as any)[fKey];
|
|
139
142
|
curObj = curObj[key];
|
|
140
143
|
|
|
141
144
|
currIdx = nextIdx;
|
|
@@ -155,7 +158,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
155
158
|
|
|
156
159
|
/* Matching sel item */
|
|
157
160
|
if(isPlainObject(rightF)){
|
|
158
|
-
const parseRightVal = (val, expect: "csv" | "array" | null = null) => {
|
|
161
|
+
const parseRightVal = (val: any, expect: "csv" | "array" | null = null) => {
|
|
159
162
|
const checkIfArr = () => {
|
|
160
163
|
if(!Array.isArray(val)) return mErr("This type of filter/column expects an Array of items");
|
|
161
164
|
}
|
|
@@ -177,7 +180,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
177
180
|
|
|
178
181
|
const fOpType = filterKeys[0];
|
|
179
182
|
const fVal = rightF[fOpType];
|
|
180
|
-
let sOpType: string;
|
|
183
|
+
let sOpType: string | undefined;
|
|
181
184
|
let sVal: any;
|
|
182
185
|
|
|
183
186
|
if(fVal && isPlainObject(fVal)){
|
|
@@ -227,7 +230,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
227
230
|
return " FALSE ";
|
|
228
231
|
}
|
|
229
232
|
|
|
230
|
-
let _fVal: any[] = fVal.filter(v => v !== null);
|
|
233
|
+
let _fVal: any[] = fVal.filter((v: any) => v !== null);
|
|
231
234
|
let c1 = "", c2 = "";
|
|
232
235
|
if(_fVal.length) c1 = leftQ + " IN " + parseRightVal(_fVal, "csv");
|
|
233
236
|
if(fVal.includes(null)) c2 = ` ${leftQ} IS NULL `;
|
|
@@ -238,7 +241,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
238
241
|
return " TRUE ";
|
|
239
242
|
}
|
|
240
243
|
|
|
241
|
-
let _fVal: any[] = fVal.filter(v => v !== null);
|
|
244
|
+
let _fVal: any[] = fVal.filter((v: any) => v !== null);
|
|
242
245
|
let c1 = "", c2 = "";
|
|
243
246
|
if(_fVal.length) c1 = leftQ + " NOT IN " + parseRightVal(_fVal, "csv");
|
|
244
247
|
if(fVal.includes(null)) c2 = ` ${leftQ} IS NOT NULL `;
|
|
@@ -274,9 +277,9 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
|
|
|
274
277
|
return leftQ + operand + parseRightVal(fVal, "array");
|
|
275
278
|
|
|
276
279
|
/* FTSQuery */
|
|
277
|
-
} else if(["@@"].includes(fOpType) && TextFilter_FullTextSearchFilterKeys.includes(sOpType)) {
|
|
280
|
+
} else if(["@@"].includes(fOpType) && TextFilter_FullTextSearchFilterKeys.includes(sOpType!)) {
|
|
278
281
|
let lq = `to_tsvector(${leftQ}::text)`;
|
|
279
|
-
if(selItem && selItem.columnPGDataType === "tsvector") lq = leftQ
|
|
282
|
+
if(selItem && selItem.columnPGDataType === "tsvector") lq = leftQ!;
|
|
280
283
|
|
|
281
284
|
let res = `${lq} ${operand} ` + `${sOpType}${parseRightVal(sVal, "csv")}`;
|
|
282
285
|
|
|
@@ -9,14 +9,14 @@ import pgPromise from "pg-promise";
|
|
|
9
9
|
|
|
10
10
|
export type PrglNotifListener = (args: { length: number; processId: number; channel: string; payload: string; name: string; }) => void;
|
|
11
11
|
export class PostgresNotifListenManager {
|
|
12
|
-
connection
|
|
12
|
+
connection?: pgPromise.IConnected<{}, pg.IClient>;
|
|
13
13
|
db_pg: DB;
|
|
14
14
|
notifListener: PrglNotifListener;
|
|
15
15
|
db_channel_name: string;
|
|
16
16
|
isListening: any;
|
|
17
17
|
client: any;
|
|
18
18
|
|
|
19
|
-
static create = (db_pg, notifListener: PrglNotifListener, db_channel_name: string): Promise<PostgresNotifListenManager> => {
|
|
19
|
+
static create = (db_pg: DB, notifListener: PrglNotifListener, db_channel_name: string): Promise<PostgresNotifListenManager> => {
|
|
20
20
|
let res = new PostgresNotifListenManager(db_pg, notifListener, db_channel_name, true);
|
|
21
21
|
return res.init();
|
|
22
22
|
}
|
|
@@ -32,7 +32,7 @@ export class PostgresNotifListenManager {
|
|
|
32
32
|
|
|
33
33
|
async init(): Promise<PostgresNotifListenManager> {
|
|
34
34
|
|
|
35
|
-
this.connection =
|
|
35
|
+
this.connection = undefined;
|
|
36
36
|
|
|
37
37
|
this.isListening = await this.startListening();
|
|
38
38
|
return this;
|
|
@@ -84,26 +84,27 @@ export class PostgresNotifListenManager {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
reconnect(delay =
|
|
87
|
+
reconnect(delay: number | undefined = 0, maxAttempts: number | undefined = 0) {
|
|
88
88
|
if(!this.db_pg || !this.notifListener) throw "db_pg OR notifListener missing";
|
|
89
89
|
|
|
90
|
-
delay = delay > 0 ? parseInt(delay) : 0;
|
|
91
|
-
maxAttempts = maxAttempts > 0 ? parseInt(maxAttempts) : 1;
|
|
90
|
+
delay = delay > 0 ? parseInt(delay + "") : 0;
|
|
91
|
+
maxAttempts = maxAttempts > 0 ? parseInt(maxAttempts + "") : 1;
|
|
92
92
|
|
|
93
|
-
const setListeners = (client, notifListener, db_channel_name) => {
|
|
93
|
+
const setListeners = (client: pg.IClient, notifListener: PrglNotifListener, db_channel_name: string) => {
|
|
94
94
|
client.on('notification', notifListener);
|
|
95
95
|
this.client = client;
|
|
96
|
+
if(!this.connection) throw "Connection missing";
|
|
96
97
|
return this.connection.none('LISTEN $1~', db_channel_name)
|
|
97
98
|
.catch(error => {
|
|
98
99
|
console.log("PostgresNotifListenManager: unexpected error: ", error); // unlikely to ever happen
|
|
99
100
|
});
|
|
100
101
|
},
|
|
101
|
-
removeListeners = (client) => {
|
|
102
|
+
removeListeners = (client: pg.IClient) => {
|
|
102
103
|
client.removeListener('notification', this.notifListener);
|
|
103
104
|
},
|
|
104
|
-
onConnectionLost = (err, e) => {
|
|
105
|
+
onConnectionLost = (err: any, e: pgPromise.ILostContext<pg.IClient>) => {
|
|
105
106
|
console.log('PostgresNotifListenManager: Connectivity Problem:', err);
|
|
106
|
-
this.connection =
|
|
107
|
+
this.connection = undefined; // prevent use of the broken connection
|
|
107
108
|
removeListeners(e.client);
|
|
108
109
|
this.reconnect(5000, 10) // retry 10 times, with 5-second intervals
|
|
109
110
|
.then(() => {
|