@contember/engine-tenant-api 2.0.0-rc.2 → 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.
Files changed (40) hide show
  1. package/dist/development/index.cjs +1 -0
  2. package/dist/development/index.cjs.map +1 -1
  3. package/dist/development/index.js +2 -1
  4. package/dist/development/src/model/mailing/UserMailer.cjs +18 -12
  5. package/dist/development/src/model/mailing/UserMailer.cjs.map +1 -1
  6. package/dist/development/src/model/mailing/UserMailer.js +18 -12
  7. package/dist/development/src/model/mailing/UserMailer.js.map +1 -1
  8. package/dist/development/src/model/service/InviteManager.cjs +2 -2
  9. package/dist/development/src/model/service/InviteManager.cjs.map +1 -1
  10. package/dist/development/src/model/service/InviteManager.js +2 -2
  11. package/dist/development/src/model/service/InviteManager.js.map +1 -1
  12. package/dist/development/src/utils/Mailer.cjs +29 -1
  13. package/dist/development/src/utils/Mailer.cjs.map +1 -1
  14. package/dist/development/src/utils/Mailer.js +29 -1
  15. package/dist/development/src/utils/Mailer.js.map +1 -1
  16. package/dist/production/index.cjs +1 -0
  17. package/dist/production/index.cjs.map +1 -1
  18. package/dist/production/index.js +2 -1
  19. package/dist/production/src/model/mailing/UserMailer.cjs +18 -12
  20. package/dist/production/src/model/mailing/UserMailer.cjs.map +1 -1
  21. package/dist/production/src/model/mailing/UserMailer.js +18 -12
  22. package/dist/production/src/model/mailing/UserMailer.js.map +1 -1
  23. package/dist/production/src/model/service/InviteManager.cjs +2 -2
  24. package/dist/production/src/model/service/InviteManager.cjs.map +1 -1
  25. package/dist/production/src/model/service/InviteManager.js +2 -2
  26. package/dist/production/src/model/service/InviteManager.js.map +1 -1
  27. package/dist/production/src/utils/Mailer.cjs +29 -1
  28. package/dist/production/src/utils/Mailer.cjs.map +1 -1
  29. package/dist/production/src/utils/Mailer.js +29 -1
  30. package/dist/production/src/utils/Mailer.js.map +1 -1
  31. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  32. package/dist/types/model/mailing/UserMailer.d.ts +4 -1
  33. package/dist/types/model/mailing/UserMailer.d.ts.map +1 -1
  34. package/dist/types/tsconfig.tsbuildinfo +1 -1
  35. package/dist/types/utils/Mailer.d.ts +24 -4
  36. package/dist/types/utils/Mailer.d.ts.map +1 -1
  37. package/package.json +12 -12
  38. package/src/model/mailing/UserMailer.ts +23 -15
  39. package/src/model/service/InviteManager.ts +2 -2
  40. 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
- export { type MailMessage };
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 = Partial<(SMTPTransport.Options | SMTPPool.Options | SendmailTransport.Options | SESTransport.Options | TransportOptions) & {
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,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAC9D,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;AAG5D,OAAO,EAAE,KAAK,WAAW,EAAE,CAAA;AAE3B,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,GAAG,OAAO,CAClC,CAAC,aAAa,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,GAAG,iBAAiB,CAAC,OAAO,GAAG,YAAY,CAAC,OAAO,GAAG,gBAAgB,CAAC,GAAG;IAClH,IAAI,CAAC,EAAE,MAAM,CAAA;CACb,CACD,CAAA;AAED,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,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAQ1D;AAED,eAAO,MAAM,YAAY,YAAa,aAAa,KAAG,MAGrD,CAAA"}
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.0-rc.2",
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.0-rc.2",
30
- "@contember/config-loader": "2.0.0-rc.2",
31
- "@contember/database": "2.0.0-rc.2",
32
- "@contember/database-migrations": "2.0.0-rc.2",
33
- "@contember/dic": "2.0.0-rc.2",
34
- "@contember/graphql-utils": "2.0.0-rc.2",
35
- "@contember/logger": "2.0.0-rc.2",
36
- "@contember/schema": "2.0.0-rc.2",
37
- "@contember/schema-utils": "2.0.0-rc.2",
38
- "@contember/typesafe": "2.0.0-rc.2",
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.0-rc.2",
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 template = (await this.getCustomTemplate(dbContext, { type: MailType.newUserInvited, ...customMailOptions })) || {
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.sendTemplate(template, mailArguments)
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 template = (await this.getCustomTemplate(dbContext, { type: MailType.existingUserInvited, ...customMailOptions })) || {
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.sendTemplate(template, mailArguments)
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 template = (await this.getCustomTemplate(dbContext, { type: MailType.passwordReset, ...customMailOptions })) || {
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.sendTemplate(template, mailArguments)
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 template = (await this.getCustomTemplate(dbContext, { type: MailType.passwordlessSignIn, ...customMailOptions })) || {
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.sendTemplate(template, mailArguments)
71
+ await this.sendMail(templateId, template, mailArguments)
67
72
  }
68
73
 
69
- private async sendTemplate(
74
+ private async sendMail(
75
+ templateId: MailTemplateIdentifier,
70
76
  template: Pick<MailTemplateData, 'subject' | 'content' | 'replyTo'>,
71
- mailArguments: Record<string, any>,
77
+ variables: Record<string, any>,
72
78
  ) {
73
- const html = await this.templateRenderer.render(template.content, mailArguments)
79
+ const html = await this.templateRenderer.render(template.content, variables)
74
80
  await this.mailer.send({
75
- to: mailArguments.email,
76
- subject: await this.templateRenderer.render(template.subject, mailArguments),
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))
@@ -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 { type MailMessage }
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 = Partial<
21
- (SMTPTransport.Options | SMTPPool.Options | SendmailTransport.Options | SESTransport.Options | TransportOptions) & {
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
  }