prostgles-server 4.2.551 → 4.2.554
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/Auth/AuthTypes.d.ts +0 -5
- package/dist/Auth/AuthTypes.d.ts.map +1 -1
- package/dist/Auth/AuthTypes.js.map +1 -1
- package/dist/Auth/sendEmail.d.ts +3 -3
- package/dist/Auth/sendEmail.d.ts.map +1 -1
- package/dist/Auth/sendEmail.js +22 -25
- package/dist/Auth/sendEmail.js.map +1 -1
- package/dist/DboBuilder/TableHandler/TableHandler.d.ts +11 -3
- package/dist/DboBuilder/TableHandler/TableHandler.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/TableHandler.js +49 -6
- package/dist/DboBuilder/TableHandler/TableHandler.js.map +1 -1
- package/dist/DboBuilder/TableHandler/delete.js +3 -3
- package/dist/DboBuilder/TableHandler/delete.js.map +1 -1
- package/dist/DboBuilder/TableHandler/{executeHooksCheckAndPostValidation.d.ts → executeAfterHooksCheckAndPostValidation.d.ts} +2 -2
- package/dist/DboBuilder/TableHandler/executeAfterHooksCheckAndPostValidation.d.ts.map +1 -0
- package/dist/DboBuilder/TableHandler/{executeHooksCheckAndPostValidation.js → executeAfterHooksCheckAndPostValidation.js} +19 -15
- package/dist/DboBuilder/TableHandler/executeAfterHooksCheckAndPostValidation.js.map +1 -0
- package/dist/DboBuilder/TableHandler/insert/insert.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/insert/insert.js +11 -5
- package/dist/DboBuilder/TableHandler/insert/insert.js.map +1 -1
- package/dist/DboBuilder/TableHandler/isApplicableHook.d.ts +5 -0
- package/dist/DboBuilder/TableHandler/isApplicableHook.d.ts.map +1 -0
- package/dist/DboBuilder/TableHandler/isApplicableHook.js +17 -0
- package/dist/DboBuilder/TableHandler/isApplicableHook.js.map +1 -0
- package/dist/DboBuilder/TableHandler/runInsertUpdateQuery.js +2 -2
- package/dist/DboBuilder/TableHandler/runInsertUpdateQuery.js.map +1 -1
- package/dist/DboBuilder/TableHandler/update.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/update.js +4 -1
- package/dist/DboBuilder/TableHandler/update.js.map +1 -1
- package/dist/FileManager/FileManager.js +1 -1
- package/dist/FileManager/FileManager.js.map +1 -1
- package/dist/PublishParser/publishTypesAndUtils.d.ts +28 -1
- package/dist/PublishParser/publishTypesAndUtils.d.ts.map +1 -1
- package/dist/PublishParser/publishTypesAndUtils.js.map +1 -1
- package/dist/TableConfig/TableConfig.d.ts +3 -2
- package/dist/TableConfig/TableConfig.d.ts.map +1 -1
- package/dist/TableConfig/TableConfig.js.map +1 -1
- package/dist/TableConfig/getCreateSchemaQueries.d.ts +2 -0
- package/dist/TableConfig/getCreateSchemaQueries.d.ts.map +1 -1
- package/dist/TableConfig/getTableConfigSchemaQueries.d.ts +2 -0
- package/dist/TableConfig/getTableConfigSchemaQueries.d.ts.map +1 -1
- package/lib/Auth/AuthTypes.ts +6 -11
- package/lib/Auth/sendEmail.ts +22 -29
- package/lib/DboBuilder/TableHandler/TableHandler.ts +59 -5
- package/lib/DboBuilder/TableHandler/delete.ts +3 -3
- package/lib/DboBuilder/TableHandler/{executeHooksCheckAndPostValidation.ts → executeAfterHooksCheckAndPostValidation.ts} +25 -16
- package/lib/DboBuilder/TableHandler/insert/insert.ts +21 -7
- package/lib/DboBuilder/TableHandler/isApplicableHook.ts +28 -0
- package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +2 -2
- package/lib/DboBuilder/TableHandler/update.ts +5 -1
- package/lib/FileManager/FileManager.ts +1 -1
- package/lib/PublishParser/publishTypesAndUtils.ts +31 -1
- package/lib/TableConfig/TableConfig.ts +3 -2
- package/package.json +11 -11
- package/dist/DboBuilder/TableHandler/executeHooksCheckAndPostValidation.d.ts.map +0 -1
- package/dist/DboBuilder/TableHandler/executeHooksCheckAndPostValidation.js.map +0 -1
package/lib/Auth/AuthTypes.ts
CHANGED
|
@@ -98,11 +98,6 @@ export type SMTPConfig =
|
|
|
98
98
|
region: string;
|
|
99
99
|
accessKeyId: string;
|
|
100
100
|
secretAccessKey: string;
|
|
101
|
-
/**
|
|
102
|
-
* Sending rate per second
|
|
103
|
-
* Defaults to 1
|
|
104
|
-
*/
|
|
105
|
-
sendingRate?: number;
|
|
106
101
|
};
|
|
107
102
|
|
|
108
103
|
export type Email = {
|
|
@@ -310,7 +305,7 @@ export type AuthConfig<S = void, SUser extends SessionUser = SessionUser> = {
|
|
|
310
305
|
onUseOrSocketConnected?: (
|
|
311
306
|
sid: string | undefined,
|
|
312
307
|
client: LoginClientInfo,
|
|
313
|
-
reqInfo: AuthClientRequest
|
|
308
|
+
reqInfo: AuthClientRequest,
|
|
314
309
|
) => Awaitable<void | { error: string; httpCode: 400 | 401 | 403 } | { session: BasicSession }>;
|
|
315
310
|
|
|
316
311
|
/**
|
|
@@ -326,7 +321,7 @@ export type AuthConfig<S = void, SUser extends SessionUser = SessionUser> = {
|
|
|
326
321
|
dbo: DBOFullyTyped<S>,
|
|
327
322
|
db: DB,
|
|
328
323
|
client: LoginClientInfo,
|
|
329
|
-
reqInfo: AuthClientRequest
|
|
324
|
+
reqInfo: AuthClientRequest,
|
|
330
325
|
) => Awaitable<AuthResultOrError<SUser>>;
|
|
331
326
|
|
|
332
327
|
/**
|
|
@@ -381,7 +376,7 @@ export const getMagicLinkUrl = ({
|
|
|
381
376
|
}
|
|
382
377
|
const { code, email } = data;
|
|
383
378
|
const confirmationUrl = new URL(
|
|
384
|
-
`${websiteUrl}${GET_AUTH_ROUTE(loginSignupConfig, "magicLinks")}
|
|
379
|
+
`${websiteUrl}${GET_AUTH_ROUTE(loginSignupConfig, "magicLinks")}`,
|
|
385
380
|
);
|
|
386
381
|
confirmationUrl.searchParams.set("email", email);
|
|
387
382
|
confirmationUrl.searchParams.set("code", code);
|
|
@@ -424,7 +419,7 @@ export type LoginSignupConfig<S, SUser extends SessionUser> = {
|
|
|
424
419
|
onGetRequestOK?: (
|
|
425
420
|
req: ExpressReq,
|
|
426
421
|
res: ExpressRes,
|
|
427
|
-
params: AuthRequestParams<S, SUser
|
|
422
|
+
params: AuthRequestParams<S, SUser>,
|
|
428
423
|
) => Awaitable<void>;
|
|
429
424
|
|
|
430
425
|
/**
|
|
@@ -439,7 +434,7 @@ export type LoginSignupConfig<S, SUser extends SessionUser> = {
|
|
|
439
434
|
data: MagicLinkOrOTPData,
|
|
440
435
|
dbo: DBOFullyTyped<S>,
|
|
441
436
|
db: DB,
|
|
442
|
-
client: LoginClientInfo
|
|
437
|
+
client: LoginClientInfo,
|
|
443
438
|
) => Awaitable<
|
|
444
439
|
| {
|
|
445
440
|
session: BasicSession | undefined;
|
|
@@ -470,7 +465,7 @@ export type LoginSignupConfig<S, SUser extends SessionUser> = {
|
|
|
470
465
|
dbo: DBOFullyTyped<S>,
|
|
471
466
|
db: DB,
|
|
472
467
|
client: LoginClientInfo,
|
|
473
|
-
getMagicLinkUrl: (data: MagicLinkOrOTPData, websiteUrl: string) => string
|
|
468
|
+
getMagicLinkUrl: (data: MagicLinkOrOTPData, websiteUrl: string) => string,
|
|
474
469
|
) => Awaitable<LoginResponse>;
|
|
475
470
|
|
|
476
471
|
logout: (sid: string | undefined, dbo: DBOFullyTyped<S>, db: DB) => Awaitable<void>;
|
package/lib/Auth/sendEmail.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Email, SMTPConfig } from "./AuthTypes";
|
|
2
2
|
import * as nodemailer from "nodemailer";
|
|
3
|
-
import
|
|
3
|
+
import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";
|
|
4
4
|
import type SESTransport from "nodemailer/lib/ses-transport";
|
|
5
5
|
import { checkDmarc } from "./utils/checkDmarc";
|
|
6
6
|
|
|
@@ -17,8 +17,8 @@ const transporterCache: Map<string, Transporter> = new Map();
|
|
|
17
17
|
* Allows sending emails using nodemailer default config or AWS SES
|
|
18
18
|
* https://www.nodemailer.com/transports/ses/
|
|
19
19
|
*/
|
|
20
|
-
const sendEmail = (
|
|
21
|
-
const transporter = getOrSetTransporter(
|
|
20
|
+
const sendEmail = (smtpConfig: SMTPConfig, email: Email) => {
|
|
21
|
+
const transporter = getOrSetTransporter(smtpConfig);
|
|
22
22
|
return send(transporter, email);
|
|
23
23
|
};
|
|
24
24
|
|
|
@@ -26,11 +26,11 @@ const sendEmail = (smptConfig: SMTPConfig, email: Email) => {
|
|
|
26
26
|
* Verifies DMARC and that the website has a valid DMARC records
|
|
27
27
|
*/
|
|
28
28
|
const emailSenderCache: Map<string, boolean> = new Map();
|
|
29
|
-
export const getEmailSender = async (
|
|
29
|
+
export const getEmailSender = async (smtpConfig: SMTPConfig, websiteUrl: string) => {
|
|
30
30
|
const result = {
|
|
31
|
-
sendEmail: (email: Email) => sendEmail(
|
|
31
|
+
sendEmail: (email: Email) => sendEmail(smtpConfig, email),
|
|
32
32
|
};
|
|
33
|
-
const configStr = JSON.stringify({
|
|
33
|
+
const configStr = JSON.stringify({ smtpConfig, websiteUrl });
|
|
34
34
|
if (emailSenderCache.has(configStr)) {
|
|
35
35
|
return result;
|
|
36
36
|
}
|
|
@@ -39,12 +39,12 @@ export const getEmailSender = async (smptConfig: SMTPConfig, websiteUrl: string)
|
|
|
39
39
|
}
|
|
40
40
|
await checkDmarc(websiteUrl);
|
|
41
41
|
|
|
42
|
-
await verifySMTPConfig(
|
|
42
|
+
await verifySMTPConfig(smtpConfig);
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Setup nodemailer transporters
|
|
46
46
|
*/
|
|
47
|
-
getOrSetTransporter(
|
|
47
|
+
getOrSetTransporter(smtpConfig);
|
|
48
48
|
emailSenderCache.set(configStr, true);
|
|
49
49
|
return result;
|
|
50
50
|
};
|
|
@@ -52,28 +52,20 @@ export const getEmailSender = async (smptConfig: SMTPConfig, websiteUrl: string)
|
|
|
52
52
|
/**
|
|
53
53
|
* Returns a transporter from cache or creates a new one
|
|
54
54
|
*/
|
|
55
|
-
export const getOrSetTransporter = (
|
|
56
|
-
const configStr = JSON.stringify(
|
|
57
|
-
const transporter = transporterCache.get(configStr) ?? getTransporter(
|
|
55
|
+
export const getOrSetTransporter = (smtpConfig: SMTPConfig) => {
|
|
56
|
+
const configStr = JSON.stringify(smtpConfig);
|
|
57
|
+
const transporter = transporterCache.get(configStr) ?? getTransporter(smtpConfig);
|
|
58
58
|
if (!transporterCache.has(configStr)) {
|
|
59
59
|
transporterCache.set(configStr, transporter);
|
|
60
60
|
}
|
|
61
61
|
return transporter;
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
const getTransporter = (
|
|
64
|
+
const getTransporter = (smtpConfig: SMTPConfig) => {
|
|
65
65
|
let transporter: Transporter | undefined;
|
|
66
|
-
if (
|
|
67
|
-
const {
|
|
68
|
-
|
|
69
|
-
accessKeyId,
|
|
70
|
-
secretAccessKey,
|
|
71
|
-
/**
|
|
72
|
-
* max 1 messages/second
|
|
73
|
-
*/
|
|
74
|
-
sendingRate = 1,
|
|
75
|
-
} = smptConfig;
|
|
76
|
-
const ses = new aws.SES({
|
|
66
|
+
if (smtpConfig.type === "aws-ses") {
|
|
67
|
+
const { region, accessKeyId, secretAccessKey } = smtpConfig;
|
|
68
|
+
const sesClient = new SESv2Client({
|
|
77
69
|
apiVersion: "2010-12-01",
|
|
78
70
|
region,
|
|
79
71
|
credentials: {
|
|
@@ -83,12 +75,13 @@ const getTransporter = (smptConfig: SMTPConfig) => {
|
|
|
83
75
|
});
|
|
84
76
|
|
|
85
77
|
transporter = nodemailer.createTransport({
|
|
86
|
-
SES: {
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
SES: {
|
|
79
|
+
sesClient,
|
|
80
|
+
SendEmailCommand,
|
|
81
|
+
},
|
|
89
82
|
});
|
|
90
83
|
} else {
|
|
91
|
-
const { user, pass, host, port, secure, tls } =
|
|
84
|
+
const { user, pass, host, port, secure, tls } = smtpConfig;
|
|
92
85
|
transporter = nodemailer.createTransport({
|
|
93
86
|
host,
|
|
94
87
|
port,
|
|
@@ -126,8 +119,8 @@ const send = (transporter: Transporter, email: Email) => {
|
|
|
126
119
|
});
|
|
127
120
|
};
|
|
128
121
|
|
|
129
|
-
export const verifySMTPConfig = async (
|
|
130
|
-
const transporter = getOrSetTransporter(
|
|
122
|
+
export const verifySMTPConfig = async (smtpConfig: SMTPConfig) => {
|
|
123
|
+
const transporter = getOrSetTransporter(smtpConfig);
|
|
131
124
|
return new Promise((resolve, reject) => {
|
|
132
125
|
transporter.verify((err, success) => {
|
|
133
126
|
if (err) {
|
|
@@ -34,6 +34,7 @@ import { insert } from "./insert/insert";
|
|
|
34
34
|
import { update } from "./update";
|
|
35
35
|
import { updateBatch } from "./updateBatch";
|
|
36
36
|
import { upsert } from "./upsert";
|
|
37
|
+
import { isApplicableHook } from "./isApplicableHook";
|
|
37
38
|
|
|
38
39
|
export type ValidatedParams = {
|
|
39
40
|
row: AnyObject;
|
|
@@ -71,7 +72,49 @@ export class TableHandler extends ViewHandler {
|
|
|
71
72
|
this.is_media = dboBuilder.prostgles.isMedia(this.name);
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
getBeforeHooks = (command: "update" | "insert", rows: AnyObject[]) => {
|
|
76
|
+
return (
|
|
77
|
+
this.config?.hooks?.beforeEach?.filter((hook) =>
|
|
78
|
+
isApplicableHook(this, rows, hook, command),
|
|
79
|
+
) ?? []
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
beforeEach = async (
|
|
84
|
+
row: AnyObject,
|
|
85
|
+
localParams: LocalParams | undefined,
|
|
86
|
+
command: "insert" | "update",
|
|
87
|
+
) => {
|
|
88
|
+
const transaction = this.getTransaction(localParams);
|
|
89
|
+
const hooks = this.getBeforeHooks(command, [row]);
|
|
90
|
+
let newRow = row;
|
|
91
|
+
const successCallbacks: (() => void)[] = [];
|
|
92
|
+
for (const hook of hooks) {
|
|
93
|
+
const isApplicable = isApplicableHook(this, [newRow], hook, command);
|
|
94
|
+
if (!isApplicable) continue;
|
|
95
|
+
const hookResult = await hook.validate({
|
|
96
|
+
row: newRow,
|
|
97
|
+
command: "insert",
|
|
98
|
+
data: newRow,
|
|
99
|
+
dbx: this.getFinalDbo(localParams),
|
|
100
|
+
localParams,
|
|
101
|
+
tx: transaction?.t || this.db,
|
|
102
|
+
});
|
|
103
|
+
if (hookResult) {
|
|
104
|
+
newRow = hookResult.row;
|
|
105
|
+
const { onInserted } = hookResult;
|
|
106
|
+
if (onInserted) {
|
|
107
|
+
successCallbacks.push(onInserted);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
row: newRow,
|
|
113
|
+
successCallbacks,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
getAfterHooksAndChecks = (
|
|
75
118
|
command:
|
|
76
119
|
| { name: "delete"; rule: undefined | DeleteRule }
|
|
77
120
|
| { name: "update"; rule: undefined | UpdateRule }
|
|
@@ -109,8 +152,12 @@ export class TableHandler extends ViewHandler {
|
|
|
109
152
|
.filter(isDefined);
|
|
110
153
|
return [
|
|
111
154
|
...(afterEachHooks ?? []),
|
|
112
|
-
...(postValidate ?
|
|
113
|
-
|
|
155
|
+
...(postValidate ?
|
|
156
|
+
[{ type: "postValidate", validate: postValidate, changedFields: undefined } as const]
|
|
157
|
+
: []),
|
|
158
|
+
...(checkFilter ?
|
|
159
|
+
[{ type: "checkFilter", checkFilter, changedFields: undefined } as const]
|
|
160
|
+
: []),
|
|
114
161
|
...(afterAllHooks ?? []),
|
|
115
162
|
];
|
|
116
163
|
};
|
|
@@ -120,10 +167,17 @@ export class TableHandler extends ViewHandler {
|
|
|
120
167
|
| { name: "update"; rule: undefined | UpdateRule }
|
|
121
168
|
| { name: "insert"; rule: undefined | InsertRule },
|
|
122
169
|
localParams: LocalParams | undefined,
|
|
170
|
+
newRows: AnyObject[],
|
|
123
171
|
) => {
|
|
124
172
|
const transaction = this.getTransaction(localParams);
|
|
125
|
-
const hasAfterChecks = this.
|
|
126
|
-
|
|
173
|
+
const hasAfterChecks = this.getAfterHooksAndChecks(command, localParams).length > 0;
|
|
174
|
+
const hasBeforeHooks =
|
|
175
|
+
command.name !== "delete" && this.getBeforeHooks(command.name, newRows).length > 0;
|
|
176
|
+
return {
|
|
177
|
+
shouldWrap: !transaction && (hasAfterChecks || hasBeforeHooks),
|
|
178
|
+
hasBeforeHooks,
|
|
179
|
+
hasAfterChecks,
|
|
180
|
+
};
|
|
127
181
|
};
|
|
128
182
|
getFinalDbo = (localParams: LocalParams | undefined) => {
|
|
129
183
|
return this.getTransaction(localParams)?.dbTX ?? this.dboBuilder.dbo;
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import type { TableHandler } from "./TableHandler";
|
|
12
12
|
import { onDeleteFromFileTable } from "./onDeleteFromFileTable";
|
|
13
13
|
import { getReturnTypeQuery } from "../ViewHandler/getReturnTypeQuery";
|
|
14
|
-
import {
|
|
14
|
+
import { executeAfterHooksCheckAndPostValidation } from "./executeAfterHooksCheckAndPostValidation";
|
|
15
15
|
|
|
16
16
|
export async function _delete(
|
|
17
17
|
this: TableHandler,
|
|
@@ -28,7 +28,7 @@ export async function _delete(
|
|
|
28
28
|
this.checkFilter(filter);
|
|
29
29
|
|
|
30
30
|
const operation = { name: "delete", rule: tableRules?.delete } as const;
|
|
31
|
-
const { hasAfterChecks, shouldWrap } = this.shouldWrapInTx(operation, localParams);
|
|
31
|
+
const { hasAfterChecks, shouldWrap } = this.shouldWrapInTx(operation, localParams, []);
|
|
32
32
|
if (shouldWrap) {
|
|
33
33
|
return this.dboBuilder.getTX((t) =>
|
|
34
34
|
(t[this.name] as Partial<typeof this> | undefined)?.delete?.(
|
|
@@ -183,7 +183,7 @@ export async function _delete(
|
|
|
183
183
|
return fullRow;
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
-
await
|
|
186
|
+
await executeAfterHooksCheckAndPostValidation({
|
|
187
187
|
tableHandler: this,
|
|
188
188
|
operation,
|
|
189
189
|
localParams,
|
|
@@ -3,8 +3,9 @@ import type { DeleteRule, InsertRule, UpdateRule } from "../../PublishParser/Pub
|
|
|
3
3
|
import { isArray } from "../../utils/utils";
|
|
4
4
|
import type { LocalParams } from "../DboBuilder";
|
|
5
5
|
import type { TableHandler } from "./TableHandler";
|
|
6
|
+
import { isApplicableHook } from "./isApplicableHook";
|
|
6
7
|
|
|
7
|
-
export const
|
|
8
|
+
export const executeAfterHooksCheckAndPostValidation = async ({
|
|
8
9
|
tableHandler,
|
|
9
10
|
operation,
|
|
10
11
|
data,
|
|
@@ -22,22 +23,13 @@ export const executeHooksCheckAndPostValidation = async ({
|
|
|
22
23
|
}) => {
|
|
23
24
|
const command = operation.name;
|
|
24
25
|
const transaction = tableHandler.getTransaction(localParams);
|
|
25
|
-
const hooks = tableHandler.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
changedFieldsSet ??= new Set<string>(
|
|
29
|
-
(isArray(data) ? data : [data]).map((row) => Object.keys(row)).flat(),
|
|
30
|
-
);
|
|
31
|
-
return changedFieldsSet;
|
|
32
|
-
};
|
|
26
|
+
const hooks = tableHandler.getAfterHooksAndChecks(operation, localParams);
|
|
27
|
+
const newRows = isArray(data) ? data : [data];
|
|
28
|
+
|
|
33
29
|
const applicableHooks = hooks.filter((hook) => {
|
|
34
30
|
if (hook.type === "checkFilter") return false;
|
|
35
31
|
if (hook.type === "postValidate") return true;
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
commands[command] &&
|
|
39
|
-
(!changedFields || changedFields.some((f) => getChangedFieldsSet().has(f)))
|
|
40
|
-
);
|
|
32
|
+
return isApplicableHook(tableHandler, newRows, hook, command);
|
|
41
33
|
});
|
|
42
34
|
|
|
43
35
|
if (applicableHooks.length) {
|
|
@@ -58,6 +50,13 @@ export const executeHooksCheckAndPostValidation = async ({
|
|
|
58
50
|
data,
|
|
59
51
|
} as const;
|
|
60
52
|
for (const hook of applicableHooks) {
|
|
53
|
+
const isApplicable = isApplicableHook(
|
|
54
|
+
tableHandler,
|
|
55
|
+
[row],
|
|
56
|
+
{ commands: { [command]: 1 }, changedFields: hook.changedFields },
|
|
57
|
+
command,
|
|
58
|
+
);
|
|
59
|
+
if (!isApplicable) continue;
|
|
61
60
|
if (hook.type === "afterEach") {
|
|
62
61
|
await hook.validate({
|
|
63
62
|
...commonParams,
|
|
@@ -74,12 +73,22 @@ export const executeHooksCheckAndPostValidation = async ({
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
for (const hook of applicableHooks) {
|
|
76
|
+
const applicableRows = newRows.filter((row) => {
|
|
77
|
+
const isApplicable = isApplicableHook(
|
|
78
|
+
tableHandler,
|
|
79
|
+
[row],
|
|
80
|
+
{ commands: { [command]: 1 }, changedFields: hook.changedFields },
|
|
81
|
+
command,
|
|
82
|
+
);
|
|
83
|
+
return isApplicable;
|
|
84
|
+
});
|
|
85
|
+
if (!applicableRows.length) continue;
|
|
77
86
|
if (hook.type === "afterAll") {
|
|
78
87
|
await hook.validate({
|
|
79
88
|
...txParams,
|
|
80
89
|
command,
|
|
81
|
-
data:
|
|
82
|
-
rows,
|
|
90
|
+
data: newRows,
|
|
91
|
+
rows: applicableRows,
|
|
83
92
|
localParams,
|
|
84
93
|
});
|
|
85
94
|
}
|
|
@@ -14,6 +14,7 @@ import { insertTest } from "../insertTest";
|
|
|
14
14
|
import { runInsertUpdateQuery } from "../runInsertUpdateQuery";
|
|
15
15
|
import { insertNestedRecords } from "./insertNestedRecords";
|
|
16
16
|
import { getReturnTypeQuery } from "../../ViewHandler/getReturnTypeQuery";
|
|
17
|
+
import { isArray } from "../../../utils/utils";
|
|
17
18
|
|
|
18
19
|
export async function insert(
|
|
19
20
|
this: TableHandler,
|
|
@@ -34,7 +35,13 @@ export async function insert(
|
|
|
34
35
|
|
|
35
36
|
const finalDBtx = this.getTransaction(localParams);
|
|
36
37
|
/** Post validate and checkFilter require a transaction dbo handler because they need the action result */
|
|
37
|
-
if (
|
|
38
|
+
if (
|
|
39
|
+
this.shouldWrapInTx(
|
|
40
|
+
{ name: ACTION, rule },
|
|
41
|
+
localParams,
|
|
42
|
+
isArray(rowOrRows) ? rowOrRows : [rowOrRows],
|
|
43
|
+
).shouldWrap
|
|
44
|
+
) {
|
|
38
45
|
return this.dboBuilder.getTX((t) =>
|
|
39
46
|
t[this.name]?.[ACTION](rowOrRows, insertParams, param3_unused, tableRules, localParams),
|
|
40
47
|
);
|
|
@@ -59,8 +66,8 @@ export async function insert(
|
|
|
59
66
|
throw `Direct inserts not allowed. Only nested inserts from these tables: ${JSON.stringify(allowedNestedInserts)} `;
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
|
-
const isMultiInsert =
|
|
63
|
-
const rows = isMultiInsert ?
|
|
69
|
+
const isMultiInsert = isArray(rowOrRows);
|
|
70
|
+
const rows = isMultiInsert ? rowOrRows : [rowOrRows];
|
|
64
71
|
|
|
65
72
|
requiredNestedInserts?.forEach(({ ftable, maxRows, minRows }) => {
|
|
66
73
|
if (this.column_names.includes(ftable))
|
|
@@ -69,7 +76,7 @@ export async function insert(
|
|
|
69
76
|
const nestedInsert = row[ftable] as unknown;
|
|
70
77
|
const nestedInsertRows =
|
|
71
78
|
isObject(nestedInsert) ? [nestedInsert]
|
|
72
|
-
:
|
|
79
|
+
: isArray(nestedInsert) ? nestedInsert
|
|
73
80
|
: [];
|
|
74
81
|
if (!nestedInsertRows.length) {
|
|
75
82
|
throw `Missing required nested insert on rowId ${rowId} for ftable: ${ftable}`;
|
|
@@ -85,15 +92,17 @@ export async function insert(
|
|
|
85
92
|
|
|
86
93
|
validateInsertParams(insertParams);
|
|
87
94
|
|
|
88
|
-
// const tx = localParams?.tx?.t || this.tx?.t;
|
|
89
95
|
const transaction = this.getTransaction(localParams);
|
|
90
96
|
const tx = transaction?.t || this.db;
|
|
91
97
|
|
|
98
|
+
const successCallbacks: (() => void)[] = [];
|
|
92
99
|
const preValidatedRows = await Promise.all(
|
|
93
100
|
rows.map(async (nonValidated) => {
|
|
94
101
|
const { preValidate, validate } = rule ?? {};
|
|
95
102
|
const { tableConfigurator } = this.dboBuilder.prostgles;
|
|
96
|
-
if (!tableConfigurator)
|
|
103
|
+
if (!tableConfigurator) {
|
|
104
|
+
throw "tableConfigurator missing";
|
|
105
|
+
}
|
|
97
106
|
let row = await tableConfigurator.getPreInsertRow(this, {
|
|
98
107
|
dbx: this.getFinalDbo(localParams),
|
|
99
108
|
validate,
|
|
@@ -103,6 +112,11 @@ export async function insert(
|
|
|
103
112
|
command: "insert",
|
|
104
113
|
data: nonValidated,
|
|
105
114
|
});
|
|
115
|
+
|
|
116
|
+
const beforeResult = await this.beforeEach(row, localParams, "insert");
|
|
117
|
+
row = beforeResult.row;
|
|
118
|
+
beforeResult.successCallbacks.forEach((cb) => successCallbacks.push(cb));
|
|
119
|
+
|
|
106
120
|
if (preValidate) {
|
|
107
121
|
if (!localParams) {
|
|
108
122
|
throw "localParams missing for insert preValidate";
|
|
@@ -269,7 +283,7 @@ export async function insert(
|
|
|
269
283
|
data: { rowOrRows, param2: insertParams },
|
|
270
284
|
duration: Date.now() - start,
|
|
271
285
|
});
|
|
272
|
-
|
|
286
|
+
successCallbacks.forEach((cb) => cb());
|
|
273
287
|
return result;
|
|
274
288
|
} catch (e) {
|
|
275
289
|
await this._log({
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AnyObject } from "prostgles-types";
|
|
2
|
+
import type { AfterEachTsTrigger } from "../../PublishParser/PublishParser";
|
|
3
|
+
import type { TableHandler } from "./TableHandler";
|
|
4
|
+
|
|
5
|
+
export const isApplicableHook = (
|
|
6
|
+
tableHandler: TableHandler,
|
|
7
|
+
rows: AnyObject[],
|
|
8
|
+
hook: Pick<AfterEachTsTrigger<AnyObject, any>, "commands" | "changedFields">,
|
|
9
|
+
command: "insert" | "update" | "delete",
|
|
10
|
+
) => {
|
|
11
|
+
let changedFieldsSet = undefined as undefined | Set<string>;
|
|
12
|
+
const getChangedFieldsSet = () => {
|
|
13
|
+
changedFieldsSet ??= new Set(
|
|
14
|
+
tableHandler.column_names.filter((col) =>
|
|
15
|
+
rows.some((row) => row[col] !== undefined && row[col] !== null),
|
|
16
|
+
),
|
|
17
|
+
);
|
|
18
|
+
return changedFieldsSet;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const { commands, changedFields } = hook;
|
|
22
|
+
return Boolean(
|
|
23
|
+
commands[command] &&
|
|
24
|
+
(command === "delete" ||
|
|
25
|
+
!changedFields ||
|
|
26
|
+
changedFields.some((f) => getChangedFieldsSet().has(f))),
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -5,7 +5,7 @@ import type { LocalParams } from "../DboBuilder";
|
|
|
5
5
|
import { rejectWithPGClientError, withUserRLS } from "../DboBuilder";
|
|
6
6
|
import type { TableHandler } from "./TableHandler";
|
|
7
7
|
import { getSelectItemQuery } from "./TableHandler";
|
|
8
|
-
import {
|
|
8
|
+
import { executeAfterHooksCheckAndPostValidation } from "./executeAfterHooksCheckAndPostValidation";
|
|
9
9
|
|
|
10
10
|
type RunInsertUpdateQueryArgs = {
|
|
11
11
|
tableHandler: TableHandler;
|
|
@@ -126,7 +126,7 @@ export const runInsertUpdateQuery = async (args: RunInsertUpdateQueryArgs) => {
|
|
|
126
126
|
|
|
127
127
|
const rows = result.modified ?? [];
|
|
128
128
|
|
|
129
|
-
await
|
|
129
|
+
await executeAfterHooksCheckAndPostValidation({
|
|
130
130
|
tableHandler,
|
|
131
131
|
operation: command === "insert" ? { name: "insert", rule } : { name: "update", rule },
|
|
132
132
|
localParams,
|
|
@@ -33,7 +33,7 @@ export async function update(
|
|
|
33
33
|
),
|
|
34
34
|
);
|
|
35
35
|
const rule = tableRules?.[ACTION];
|
|
36
|
-
if (this.shouldWrapInTx({ name: ACTION, rule }, localParams).shouldWrap) {
|
|
36
|
+
if (this.shouldWrapInTx({ name: ACTION, rule }, localParams, [_newData]).shouldWrap) {
|
|
37
37
|
return wrapInTx();
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -76,6 +76,9 @@ export async function update(
|
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
const beforeResult = await this.beforeEach(newData, localParams, "update");
|
|
80
|
+
newData = beforeResult.row;
|
|
81
|
+
|
|
79
82
|
const { data, allowedCols } = prepareNewData({
|
|
80
83
|
row: newData,
|
|
81
84
|
forcedData,
|
|
@@ -205,6 +208,7 @@ export async function update(
|
|
|
205
208
|
data: { filter, _newData, params },
|
|
206
209
|
duration: Date.now() - start,
|
|
207
210
|
});
|
|
211
|
+
beforeResult.successCallbacks.forEach((cb) => cb());
|
|
208
212
|
return result;
|
|
209
213
|
} catch (e) {
|
|
210
214
|
await this._log({
|
|
@@ -333,7 +333,7 @@ export const getFileType = async (
|
|
|
333
333
|
>);
|
|
334
334
|
|
|
335
335
|
const fileNameMime = getFileTypeFromFilename(fileName);
|
|
336
|
-
if (!fileNameMime?.ext) throw new Error("File name must contain
|
|
336
|
+
if (!fileNameMime?.ext) throw new Error("File name must contain extension");
|
|
337
337
|
const res = await fileTypeFromBuffer(typeof file === "string" ? Buffer.from(file, "utf8") : file);
|
|
338
338
|
|
|
339
339
|
if (!res) {
|
|
@@ -55,6 +55,20 @@ export type UpdateRequestData<R extends AnyObject = AnyObject> =
|
|
|
55
55
|
| UpdateRequestDataOne<R>
|
|
56
56
|
| UpdateRequestDataBatch<R>;
|
|
57
57
|
|
|
58
|
+
export type ValidateBeforeRowArgsCommon<R = AnyObject, DBX = DBHandlerServer> = {
|
|
59
|
+
row: R;
|
|
60
|
+
dbx: DBX;
|
|
61
|
+
tx: pgPromise.ITask<{}> | DB;
|
|
62
|
+
} & (
|
|
63
|
+
| {
|
|
64
|
+
command: "insert";
|
|
65
|
+
data: R;
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
command: "update";
|
|
69
|
+
data: Partial<R>;
|
|
70
|
+
}
|
|
71
|
+
);
|
|
58
72
|
export type ValidateRowArgsCommon<R = AnyObject, DBX = DBHandlerServer> = {
|
|
59
73
|
row: R;
|
|
60
74
|
dbx: DBX;
|
|
@@ -74,9 +88,25 @@ export type ValidateRowArgsCommon<R = AnyObject, DBX = DBHandlerServer> = {
|
|
|
74
88
|
}
|
|
75
89
|
);
|
|
76
90
|
|
|
91
|
+
export type BeforeEachTsTrigger<R, DBX> = {
|
|
92
|
+
commands: Partial<Record<"insert" | "update", 1>>;
|
|
93
|
+
/**
|
|
94
|
+
* Will only run this trigger if the insert/update provides non null values for these fields.
|
|
95
|
+
*/
|
|
96
|
+
changedFields?: (keyof R)[];
|
|
97
|
+
validate: (
|
|
98
|
+
params: ValidateBeforeRowArgsCommon<R, DBX> & {
|
|
99
|
+
localParams: undefined | LocalParams;
|
|
100
|
+
},
|
|
101
|
+
) => Promise<void | { row: R; onInserted?: () => void }>;
|
|
102
|
+
};
|
|
103
|
+
|
|
77
104
|
export type AfterEachTsTrigger<R, DBX> = {
|
|
78
105
|
commands: Partial<Record<"insert" | "update" | "delete", 1>>;
|
|
79
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Will only run this trigger if the insert/update provides non null values for these fields.
|
|
108
|
+
*/
|
|
109
|
+
changedFields?: (keyof R)[];
|
|
80
110
|
validate: (
|
|
81
111
|
params: ValidateRowArgsCommon<R, DBX> & {
|
|
82
112
|
localParams: undefined | LocalParams;
|
|
@@ -16,10 +16,10 @@ import type { DB, DBHandlerServer, Prostgles } from "../Prostgles";
|
|
|
16
16
|
import type {
|
|
17
17
|
AfterAllTsTrigger,
|
|
18
18
|
AfterEachTsTrigger,
|
|
19
|
+
BeforeEachTsTrigger,
|
|
19
20
|
InsertRule,
|
|
20
21
|
SyncConfig,
|
|
21
22
|
ValidateRowArgsCommon,
|
|
22
|
-
ValidateRowsArgsCommon,
|
|
23
23
|
} from "../PublishParser/PublishParser";
|
|
24
24
|
import {
|
|
25
25
|
DEFAULT_SYNC_BATCH_SIZE,
|
|
@@ -63,11 +63,12 @@ type BaseTableDefinition<R = AnyObject, DBX = DBHandlerServer> = {
|
|
|
63
63
|
hooks?: {
|
|
64
64
|
/**
|
|
65
65
|
* Hook used to run custom logic before inserting a row.
|
|
66
|
-
* The returned row must satisfy the table schema
|
|
66
|
+
* The returned row must satisfy the table schema.
|
|
67
67
|
*/
|
|
68
68
|
getPreInsertRow?: (
|
|
69
69
|
args: GetPreInsertRowArgs,
|
|
70
70
|
) => Promise<{ row: AnyObject; onInserted: Promise<void> }>;
|
|
71
|
+
beforeEach?: BeforeEachTsTrigger<R, DBX>[];
|
|
71
72
|
afterEach?: AfterEachTsTrigger<R, DBX>[];
|
|
72
73
|
afterAll?: AfterAllTsTrigger<R, DBX>[];
|
|
73
74
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prostgles-server",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.554",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
],
|
|
39
39
|
"homepage": "https://prostgles.com",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@aws-sdk/client-
|
|
42
|
-
"@aws-sdk/credential-provider-node": "^3.972.
|
|
41
|
+
"@aws-sdk/client-sesv2": "^3.1090.0",
|
|
42
|
+
"@aws-sdk/credential-provider-node": "^3.972.70",
|
|
43
43
|
"@types/passport": "^1.0.17",
|
|
44
44
|
"@types/passport-facebook": "^3.0.4",
|
|
45
45
|
"@types/passport-github2": "^1.2.9",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@types/passport-microsoft": "^1.0.3",
|
|
48
48
|
"check-disk-space": "^3.4.0",
|
|
49
49
|
"file-type": "^18.7.0",
|
|
50
|
-
"nodemailer": "^
|
|
50
|
+
"nodemailer": "^9.0.3",
|
|
51
51
|
"passport": "^0.7.0",
|
|
52
52
|
"passport-facebook": "^3.0.0",
|
|
53
53
|
"passport-github2": "^0.1.12",
|
|
@@ -55,24 +55,24 @@
|
|
|
55
55
|
"passport-microsoft": "^2.1.0",
|
|
56
56
|
"passport-oauth2": "^1.8.0",
|
|
57
57
|
"pg": "^8.21.0",
|
|
58
|
-
"pg-cursor": "^2.
|
|
59
|
-
"pg-promise": "^12.
|
|
58
|
+
"pg-cursor": "^2.21.0",
|
|
59
|
+
"pg-promise": "^12.7.0",
|
|
60
60
|
"prostgles-types": "^4.0.264"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@eslint/js": "^9.22.0",
|
|
64
64
|
"@types/express": "^4.17.25",
|
|
65
65
|
"@types/json-schema": "^7.0.15",
|
|
66
|
-
"@types/node": "^22.
|
|
67
|
-
"@types/nodemailer": "^
|
|
66
|
+
"@types/node": "^22.20.1",
|
|
67
|
+
"@types/nodemailer": "^8.0.1",
|
|
68
68
|
"@types/pg": "^8.20.0",
|
|
69
69
|
"@types/pg-cursor": "^2.7.2",
|
|
70
70
|
"@types/sharp": "^0.30.5",
|
|
71
|
-
"eslint": "^9.39.
|
|
71
|
+
"eslint": "^9.39.5",
|
|
72
72
|
"eslint-plugin-security": "^3.0.1",
|
|
73
|
-
"prettier": "^3.
|
|
73
|
+
"prettier": "^3.9.5",
|
|
74
74
|
"socket.io": "^4.8.3",
|
|
75
75
|
"typescript": "^5.9.3",
|
|
76
|
-
"typescript-eslint": "^8.
|
|
76
|
+
"typescript-eslint": "^8.64.0"
|
|
77
77
|
}
|
|
78
78
|
}
|