@contember/engine-tenant-api 2.0.0 → 2.1.0-alpha.1
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/development/index.cjs +1 -0
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +2 -1
- package/dist/development/src/model/mailing/UserMailer.cjs +18 -12
- package/dist/development/src/model/mailing/UserMailer.cjs.map +1 -1
- package/dist/development/src/model/mailing/UserMailer.js +18 -12
- package/dist/development/src/model/mailing/UserMailer.js.map +1 -1
- package/dist/development/src/model/service/InviteManager.cjs +2 -2
- package/dist/development/src/model/service/InviteManager.cjs.map +1 -1
- package/dist/development/src/model/service/InviteManager.js +2 -2
- package/dist/development/src/model/service/InviteManager.js.map +1 -1
- package/dist/development/src/utils/Mailer.cjs +29 -1
- package/dist/development/src/utils/Mailer.cjs.map +1 -1
- package/dist/development/src/utils/Mailer.js +29 -1
- package/dist/development/src/utils/Mailer.js.map +1 -1
- package/dist/production/index.cjs +1 -0
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.js +2 -1
- package/dist/production/src/model/mailing/UserMailer.cjs +18 -12
- package/dist/production/src/model/mailing/UserMailer.cjs.map +1 -1
- package/dist/production/src/model/mailing/UserMailer.js +18 -12
- package/dist/production/src/model/mailing/UserMailer.js.map +1 -1
- package/dist/production/src/model/service/InviteManager.cjs +2 -2
- package/dist/production/src/model/service/InviteManager.cjs.map +1 -1
- package/dist/production/src/model/service/InviteManager.js +2 -2
- package/dist/production/src/model/service/InviteManager.js.map +1 -1
- package/dist/production/src/utils/Mailer.cjs +29 -1
- package/dist/production/src/utils/Mailer.cjs.map +1 -1
- package/dist/production/src/utils/Mailer.js +29 -1
- package/dist/production/src/utils/Mailer.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/model/mailing/UserMailer.d.ts +4 -1
- package/dist/types/model/mailing/UserMailer.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dist/types/utils/Mailer.d.ts +24 -4
- package/dist/types/utils/Mailer.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/model/mailing/UserMailer.ts +23 -15
- package/src/model/service/InviteManager.ts +2 -2
- package/src/utils/Mailer.ts +56 -6
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Transporter, TransportOptions } from 'nodemailer';
|
|
2
|
-
import { Options as MailMessage } from 'nodemailer/lib/mailer';
|
|
3
2
|
import * as SMTPTransport from 'nodemailer/lib/smtp-transport';
|
|
4
3
|
import * as SMTPPool from 'nodemailer/lib/smtp-pool';
|
|
5
4
|
import * as SendmailTransport from 'nodemailer/lib/sendmail-transport';
|
|
6
5
|
import * as SESTransport from 'nodemailer/lib/ses-transport';
|
|
7
|
-
|
|
6
|
+
import { MailTemplateIdentifier } from '../model';
|
|
7
|
+
export interface MailMessage {
|
|
8
|
+
to: string;
|
|
9
|
+
subject: string;
|
|
10
|
+
html: string;
|
|
11
|
+
from?: string;
|
|
12
|
+
replyTo?: string;
|
|
13
|
+
variables?: Record<string, any>;
|
|
14
|
+
template?: MailTemplateIdentifier;
|
|
15
|
+
}
|
|
8
16
|
export interface SentInfo {
|
|
9
17
|
error?: any;
|
|
10
18
|
messageId?: string;
|
|
@@ -13,9 +21,14 @@ export interface SentInfo {
|
|
|
13
21
|
rejected?: string[];
|
|
14
22
|
pending?: string[];
|
|
15
23
|
}
|
|
16
|
-
export type MailerOptions =
|
|
24
|
+
export type MailerOptions = (SMTPTransport.Options | SMTPPool.Options | SendmailTransport.Options | SESTransport.Options | TransportOptions | {
|
|
25
|
+
webhook: string | {
|
|
26
|
+
url: string;
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
|
+
};
|
|
29
|
+
}) & {
|
|
17
30
|
from?: string;
|
|
18
|
-
}
|
|
31
|
+
};
|
|
19
32
|
export interface Mailer {
|
|
20
33
|
send(message: MailMessage): Promise<SentInfo>;
|
|
21
34
|
}
|
|
@@ -23,6 +36,13 @@ export declare class NodeMailer implements Mailer {
|
|
|
23
36
|
private readonly from;
|
|
24
37
|
private readonly transport;
|
|
25
38
|
constructor(from: string | undefined, transport: Transporter);
|
|
39
|
+
send({ variables, template, ...message }: MailMessage): Promise<SentInfo>;
|
|
40
|
+
}
|
|
41
|
+
export declare class WebhookMailer implements Mailer {
|
|
42
|
+
private readonly from;
|
|
43
|
+
private readonly url;
|
|
44
|
+
private readonly headers;
|
|
45
|
+
constructor(from: string | undefined, url: string, headers?: Record<string, string>);
|
|
26
46
|
send(message: MailMessage): Promise<SentInfo>;
|
|
27
47
|
}
|
|
28
48
|
export declare const createMailer: (options: MailerOptions) => Mailer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Mailer.d.ts","sourceRoot":"","sources":["../../../src/utils/Mailer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC3E,OAAO,
|
|
1
|
+
{"version":3,"file":"Mailer.d.ts","sourceRoot":"","sources":["../../../src/utils/Mailer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC3E,OAAO,KAAK,aAAa,MAAM,+BAA+B,CAAA;AAC9D,OAAO,KAAK,QAAQ,MAAM,0BAA0B,CAAA;AACpD,OAAO,KAAK,iBAAiB,MAAM,mCAAmC,CAAA;AACtE,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAA;AAE5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAEjD,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/B,QAAQ,CAAC,EAAE,sBAAsB,CAAA;CACjC;AAED,MAAM,WAAW,QAAQ;IACxB,KAAK,CAAC,EAAE,GAAG,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,GAAG,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,MAAM,aAAa,GACtB,CACD,aAAa,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,GAAG,iBAAiB,CAAC,OAAO,GAAG,YAAY,CAAC,OAAO,GAAG,gBAAgB,GAC5G;IACD,OAAO,EAAE,MAAM,GAAG;QACjB,GAAG,EAAE,MAAM,CAAA;QACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAChC,CAAA;CACD,CACD,GACC;IACD,IAAI,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAGF,MAAM,WAAW,MAAM;IACtB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC7C;AAED,qBAAa,UAAW,YAAW,MAAM;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAsB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAApD,IAAI,EAAE,MAAM,GAAG,SAAS,EAAmB,SAAS,EAAE,WAAW;IAEjF,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAQtF;AAED,qBAAa,aAAc,YAAW,MAAM;IAG1C,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFP,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAIzC,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAgB1D;AAGD,eAAO,MAAM,YAAY,YAAa,aAAa,KAAG,MAQrD,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-tenant-api",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.1.0-alpha.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/production/index.js",
|
|
6
6
|
"typings": "./dist/types/index.d.ts",
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
"generate": "gql-gen --config graphql.codegen.yml"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@contember/authorization": "2.0.
|
|
30
|
-
"@contember/config-loader": "2.0.
|
|
31
|
-
"@contember/database": "2.0.
|
|
32
|
-
"@contember/database-migrations": "2.0.
|
|
33
|
-
"@contember/dic": "2.0.
|
|
34
|
-
"@contember/graphql-utils": "2.0.
|
|
35
|
-
"@contember/logger": "2.0.
|
|
36
|
-
"@contember/schema": "2.0.
|
|
37
|
-
"@contember/schema-utils": "2.0.
|
|
38
|
-
"@contember/typesafe": "2.0.
|
|
29
|
+
"@contember/authorization": "2.1.0-alpha.1",
|
|
30
|
+
"@contember/config-loader": "2.1.0-alpha.1",
|
|
31
|
+
"@contember/database": "2.1.0-alpha.1",
|
|
32
|
+
"@contember/database-migrations": "2.1.0-alpha.1",
|
|
33
|
+
"@contember/dic": "2.1.0-alpha.1",
|
|
34
|
+
"@contember/graphql-utils": "2.1.0-alpha.1",
|
|
35
|
+
"@contember/logger": "2.1.0-alpha.1",
|
|
36
|
+
"@contember/schema": "2.1.0-alpha.1",
|
|
37
|
+
"@contember/schema-utils": "2.1.0-alpha.1",
|
|
38
|
+
"@contember/typesafe": "2.1.0-alpha.1",
|
|
39
39
|
"@graphql-tools/utils": "^10.5.5",
|
|
40
40
|
"graphql-tag": "^2.12.6",
|
|
41
41
|
"jose": "^5.8.0",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"otpauth": "^9.3.4"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@contember/database-tester": "2.0.
|
|
48
|
+
"@contember/database-tester": "2.1.0-alpha.1",
|
|
49
49
|
"@graphql-tools/schema": "^10.0.7",
|
|
50
50
|
"@types/mustache": "^4.2.5",
|
|
51
51
|
"@types/node": "^20.16.11",
|
|
@@ -7,6 +7,7 @@ import { MailTemplateData, MailTemplateIdentifier, MailType } from './type'
|
|
|
7
7
|
import { MailTemplateQuery } from '../queries'
|
|
8
8
|
import Layout from './templates/Layout.mustache'
|
|
9
9
|
import { DatabaseContext } from '../utils'
|
|
10
|
+
import { Acl } from '@contember/schema'
|
|
10
11
|
|
|
11
12
|
export class UserMailer {
|
|
12
13
|
constructor(
|
|
@@ -16,28 +17,30 @@ export class UserMailer {
|
|
|
16
17
|
|
|
17
18
|
async sendNewUserInvitedMail(
|
|
18
19
|
dbContext: DatabaseContext,
|
|
19
|
-
mailArguments: { email: string; password: string | null; token: string | null; project: string; projectSlug: string },
|
|
20
|
+
mailArguments: { email: string; password: string | null; token: string | null; project: string; projectSlug: string; memberships: readonly Acl.Membership[]},
|
|
20
21
|
customMailOptions: { projectId: string; variant: string },
|
|
21
22
|
): Promise<void> {
|
|
22
|
-
const
|
|
23
|
+
const templateId = { type: MailType.newUserInvited, ...customMailOptions }
|
|
24
|
+
const template = (await this.getCustomTemplate(dbContext, templateId)) || {
|
|
23
25
|
subject: 'You have been invited to {{project}}',
|
|
24
26
|
content: NewUserInvited,
|
|
25
27
|
replyTo: null,
|
|
26
28
|
}
|
|
27
|
-
await this.
|
|
29
|
+
await this.sendMail(templateId, template, mailArguments)
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
async sendExistingUserInvitedEmail(
|
|
31
33
|
dbContext: DatabaseContext,
|
|
32
|
-
mailArguments: { email: string; project: string; projectSlug: string },
|
|
34
|
+
mailArguments: { email: string; project: string; projectSlug: string; memberships: readonly Acl.Membership[] },
|
|
33
35
|
customMailOptions: { projectId: string; variant: string },
|
|
34
36
|
): Promise<void> {
|
|
35
|
-
const
|
|
37
|
+
const templateId = { type: MailType.existingUserInvited, ...customMailOptions }
|
|
38
|
+
const template = (await this.getCustomTemplate(dbContext, templateId)) || {
|
|
36
39
|
subject: 'You have been invited to {{project}}',
|
|
37
40
|
content: ExistingUserInvited,
|
|
38
41
|
replyTo: null,
|
|
39
42
|
}
|
|
40
|
-
await this.
|
|
43
|
+
await this.sendMail(templateId, template, mailArguments)
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
async sendPasswordResetEmail(
|
|
@@ -45,12 +48,13 @@ export class UserMailer {
|
|
|
45
48
|
mailArguments: { email: string; token: string; project?: string; projectSlug?: string },
|
|
46
49
|
customMailOptions: { projectId: string | null; variant: string },
|
|
47
50
|
): Promise<void> {
|
|
48
|
-
const
|
|
51
|
+
const templateId = { type: MailType.passwordReset, ...customMailOptions }
|
|
52
|
+
const template = (await this.getCustomTemplate(dbContext, templateId)) || {
|
|
49
53
|
subject: 'Password reset',
|
|
50
54
|
content: PasswordReset,
|
|
51
55
|
replyTo: null,
|
|
52
56
|
}
|
|
53
|
-
await this.
|
|
57
|
+
await this.sendMail(templateId, template, mailArguments)
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
async sendPasswordlessEmail(
|
|
@@ -58,23 +62,27 @@ export class UserMailer {
|
|
|
58
62
|
mailArguments: { email: string; token: string; project?: string; projectSlug?: string; url?: string },
|
|
59
63
|
customMailOptions: { projectId: string | null; variant: string },
|
|
60
64
|
): Promise<void> {
|
|
61
|
-
const
|
|
65
|
+
const templateId = { type: MailType.passwordlessSignIn, ...customMailOptions }
|
|
66
|
+
const template = (await this.getCustomTemplate(dbContext, templateId)) || {
|
|
62
67
|
subject: 'Sign in here',
|
|
63
68
|
content: PasswordlessSignIn,
|
|
64
69
|
replyTo: null,
|
|
65
70
|
}
|
|
66
|
-
await this.
|
|
71
|
+
await this.sendMail(templateId, template, mailArguments)
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
private async
|
|
74
|
+
private async sendMail(
|
|
75
|
+
templateId: MailTemplateIdentifier,
|
|
70
76
|
template: Pick<MailTemplateData, 'subject' | 'content' | 'replyTo'>,
|
|
71
|
-
|
|
77
|
+
variables: Record<string, any>,
|
|
72
78
|
) {
|
|
73
|
-
const html = await this.templateRenderer.render(template.content,
|
|
79
|
+
const html = await this.templateRenderer.render(template.content, variables)
|
|
74
80
|
await this.mailer.send({
|
|
75
|
-
to:
|
|
76
|
-
subject: await this.templateRenderer.render(template.subject,
|
|
81
|
+
to: variables.email,
|
|
82
|
+
subject: await this.templateRenderer.render(template.subject, variables),
|
|
77
83
|
html,
|
|
84
|
+
variables,
|
|
85
|
+
template: templateId,
|
|
78
86
|
...(template.replyTo ? {
|
|
79
87
|
replyTo: template.replyTo,
|
|
80
88
|
} : {}),
|
|
@@ -93,11 +93,11 @@ export class InviteManager {
|
|
|
93
93
|
if (isNew) {
|
|
94
94
|
await this.mailer.sendNewUserInvitedMail(
|
|
95
95
|
trx,
|
|
96
|
-
{ email, project: project.name, projectSlug: project.slug, password: generatedPassword, token: resetToken },
|
|
96
|
+
{ email, project: project.name, projectSlug: project.slug, password: generatedPassword, token: resetToken, memberships },
|
|
97
97
|
customMailOptions,
|
|
98
98
|
)
|
|
99
99
|
} else {
|
|
100
|
-
await this.mailer.sendExistingUserInvitedEmail(trx, { email, project: project.name, projectSlug: project.slug }, customMailOptions)
|
|
100
|
+
await this.mailer.sendExistingUserInvitedEmail(trx, { email, project: project.name, projectSlug: project.slug, memberships }, customMailOptions)
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
return new ResponseOk(new InviteResult(person, isNew))
|
package/src/utils/Mailer.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { createTransport, Transporter, TransportOptions } from 'nodemailer'
|
|
2
|
-
import { Options as MailMessage } from 'nodemailer/lib/mailer'
|
|
3
2
|
import * as SMTPTransport from 'nodemailer/lib/smtp-transport'
|
|
4
3
|
import * as SMTPPool from 'nodemailer/lib/smtp-pool'
|
|
5
4
|
import * as SendmailTransport from 'nodemailer/lib/sendmail-transport'
|
|
6
5
|
import * as SESTransport from 'nodemailer/lib/ses-transport'
|
|
7
6
|
import { logger } from '@contember/logger'
|
|
7
|
+
import { MailTemplateIdentifier } from '../model'
|
|
8
8
|
|
|
9
|
-
export
|
|
9
|
+
export interface MailMessage {
|
|
10
|
+
to: string
|
|
11
|
+
subject: string
|
|
12
|
+
html: string
|
|
13
|
+
from?: string
|
|
14
|
+
replyTo?: string
|
|
15
|
+
variables?: Record<string, any>
|
|
16
|
+
template?: MailTemplateIdentifier
|
|
17
|
+
}
|
|
10
18
|
|
|
11
19
|
export interface SentInfo {
|
|
12
20
|
error?: any
|
|
@@ -17,11 +25,20 @@ export interface SentInfo {
|
|
|
17
25
|
pending?: string[]
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
export type MailerOptions =
|
|
21
|
-
|
|
28
|
+
export type MailerOptions =
|
|
29
|
+
& (
|
|
30
|
+
SMTPTransport.Options | SMTPPool.Options | SendmailTransport.Options | SESTransport.Options | TransportOptions
|
|
31
|
+
| {
|
|
32
|
+
webhook: string | {
|
|
33
|
+
url: string
|
|
34
|
+
headers?: Record<string, string>
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
& {
|
|
22
39
|
from?: string
|
|
23
40
|
}
|
|
24
|
-
|
|
41
|
+
|
|
25
42
|
|
|
26
43
|
export interface Mailer {
|
|
27
44
|
send(message: MailMessage): Promise<SentInfo>
|
|
@@ -30,7 +47,7 @@ export interface Mailer {
|
|
|
30
47
|
export class NodeMailer implements Mailer {
|
|
31
48
|
constructor(private readonly from: string | undefined, private readonly transport: Transporter) {}
|
|
32
49
|
|
|
33
|
-
public async send(message: MailMessage): Promise<SentInfo> {
|
|
50
|
+
public async send({ variables, template, ...message }: MailMessage): Promise<SentInfo> {
|
|
34
51
|
try {
|
|
35
52
|
return await this.transport.sendMail({ ...message, from: message.from || this.from })
|
|
36
53
|
} catch (e) {
|
|
@@ -40,7 +57,40 @@ export class NodeMailer implements Mailer {
|
|
|
40
57
|
}
|
|
41
58
|
}
|
|
42
59
|
|
|
60
|
+
export class WebhookMailer implements Mailer {
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
private readonly from: string | undefined,
|
|
64
|
+
private readonly url: string,
|
|
65
|
+
private readonly headers: Record<string, string> = {},
|
|
66
|
+
) {
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async send(message: MailMessage): Promise<SentInfo> {
|
|
70
|
+
const response = await fetch(this.url, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
body: JSON.stringify({ ...message, from: message.from || this.from }),
|
|
73
|
+
headers: {
|
|
74
|
+
...this.headers,
|
|
75
|
+
'Content-Type': 'application/json',
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
logger.error('Mail sending failed', { mail: message, response })
|
|
80
|
+
return { error: response.statusText }
|
|
81
|
+
}
|
|
82
|
+
await response.text()
|
|
83
|
+
return {}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
43
88
|
export const createMailer = (options: MailerOptions): Mailer => {
|
|
44
89
|
const { from, ...transportOptions } = options
|
|
90
|
+
if ('webhook' in transportOptions) {
|
|
91
|
+
const config = typeof transportOptions.webhook === 'string' ? { url: transportOptions.webhook } : transportOptions.webhook
|
|
92
|
+
return new WebhookMailer(from, config.url, config.headers)
|
|
93
|
+
}
|
|
94
|
+
|
|
45
95
|
return new NodeMailer(from, createTransport(transportOptions as any))
|
|
46
96
|
}
|