@tmlmobilidade/go-providers-emails 20260828.1636.54
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/components/Anchor/index.d.ts +8 -0
- package/dist/components/Anchor/index.js +9 -0
- package/dist/components/Anchor/styles.d.ts +5 -0
- package/dist/components/Anchor/styles.js +13 -0
- package/dist/components/CoverLogo/index.d.ts +1 -0
- package/dist/components/CoverLogo/index.js +10 -0
- package/dist/components/CoverLogo/styles.d.ts +5 -0
- package/dist/components/CoverLogo/styles.js +11 -0
- package/dist/components/DebugCode/index.d.ts +5 -0
- package/dist/components/DebugCode/index.js +10 -0
- package/dist/components/DebugCode/styles.d.ts +5 -0
- package/dist/components/DebugCode/styles.js +16 -0
- package/dist/components/Disclaimer/index.d.ts +1 -0
- package/dist/components/Disclaimer/index.js +10 -0
- package/dist/components/Disclaimer/styles.d.ts +7 -0
- package/dist/components/Disclaimer/styles.js +31 -0
- package/dist/components/Greeting/index.d.ts +4 -0
- package/dist/components/Greeting/index.js +9 -0
- package/dist/components/Greeting/styles.d.ts +5 -0
- package/dist/components/Greeting/styles.js +17 -0
- package/dist/components/MainButton/index.d.ts +6 -0
- package/dist/components/MainButton/index.js +9 -0
- package/dist/components/MainButton/styles.d.ts +7 -0
- package/dist/components/MainButton/styles.js +37 -0
- package/dist/components/Paragraph/index.d.ts +8 -0
- package/dist/components/Paragraph/index.js +16 -0
- package/dist/components/Paragraph/styles.d.ts +5 -0
- package/dist/components/Paragraph/styles.js +16 -0
- package/dist/components/Span/index.d.ts +14 -0
- package/dist/components/Span/index.js +16 -0
- package/dist/components/Wrapper/index.d.ts +6 -0
- package/dist/components/Wrapper/index.js +12 -0
- package/dist/components/Wrapper/styles.d.ts +7 -0
- package/dist/components/Wrapper/styles.js +23 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/email.provider.d.ts +29 -0
- package/dist/email.provider.js +118 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/styles/colors.d.ts +27 -0
- package/dist/styles/colors.js +45 -0
- package/dist/styles/font.d.ts +16 -0
- package/dist/styles/font.js +24 -0
- package/dist/styles/sizes.d.ts +8 -0
- package/dist/styles/sizes.js +11 -0
- package/dist/templates/generic-notification.d.ts +20 -0
- package/dist/templates/generic-notification.js +35 -0
- package/dist/templates/index.d.ts +9 -0
- package/dist/templates/index.js +9 -0
- package/dist/templates/new-apex-file-notification.d.ts +19 -0
- package/dist/templates/new-apex-file-notification.js +36 -0
- package/dist/templates/plan-approval-request.d.ts +27 -0
- package/dist/templates/plan-approval-request.js +40 -0
- package/dist/templates/plan-approved.d.ts +25 -0
- package/dist/templates/plan-approved.js +39 -0
- package/dist/templates/reset-password.d.ts +16 -0
- package/dist/templates/reset-password.js +32 -0
- package/dist/templates/sucessful-gtfs-validation.d.ts +20 -0
- package/dist/templates/sucessful-gtfs-validation.js +37 -0
- package/dist/templates/system-error.d.ts +19 -0
- package/dist/templates/system-error.js +33 -0
- package/dist/templates/unsucessful-gtfs-validation.d.ts +22 -0
- package/dist/templates/unsucessful-gtfs-validation.js +38 -0
- package/dist/templates/welcome.d.ts +16 -0
- package/dist/templates/welcome.js +35 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +2 -0
- package/package.json +56 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import { Link } from 'react-email';
|
|
4
|
+
import styles from './styles.js';
|
|
5
|
+
/* * */
|
|
6
|
+
export function Anchor({ href, spaceAfter, spaceBefore, text }) {
|
|
7
|
+
return (_jsxs(_Fragment, { children: [spaceBefore && ' ', _jsx(Link, { href: href, style: styles.link, children: text }), spaceAfter && ' '] }));
|
|
8
|
+
}
|
|
9
|
+
;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function CoverLogo(): import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import { GO_HOMEPAGE_URL } from '../../constants.js';
|
|
4
|
+
import { Column, Img, Link, Row } from 'react-email';
|
|
5
|
+
import styles from './styles.js';
|
|
6
|
+
/* * */
|
|
7
|
+
export function CoverLogo() {
|
|
8
|
+
return (_jsxs(Row, { style: styles.container, children: [_jsx(Column, { children: _jsx(Link, { href: GO_HOMEPAGE_URL, target: "_blank", children: _jsx(Img, { alt: "GO Logo", height: 40, src: "https://storage.carrismetropolitana.pt/static/test/go-header-left.png", width: 88 }) }) }), _jsx(Column, { align: "right", children: _jsx(Link, { href: GO_HOMEPAGE_URL, target: "_blank", children: _jsx(Img, { alt: "TML Logo", height: 40, src: "https://storage.carrismetropolitana.pt/static/test/go-header-right.png", width: 60 }) }) })] }));
|
|
9
|
+
}
|
|
10
|
+
;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import { Span } from '../Span/index.js';
|
|
4
|
+
import { Text } from 'react-email';
|
|
5
|
+
import styles from './styles.js';
|
|
6
|
+
/* * */
|
|
7
|
+
export function DebugCode({ label = 'Olá,', value }) {
|
|
8
|
+
return (_jsxs(Text, { style: styles.text, children: [label, ":", _jsx(Span, { spaceBefore: true, weight: "bold", children: value })] }));
|
|
9
|
+
}
|
|
10
|
+
;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import colors from '../../styles/colors.js';
|
|
3
|
+
import { fontFamily, fontSize, fontWeight } from '../../styles/font.js';
|
|
4
|
+
/* * */
|
|
5
|
+
/* TEXT */
|
|
6
|
+
const text = {
|
|
7
|
+
color: colors.muted.foreground,
|
|
8
|
+
fontFamily: fontFamily.primary,
|
|
9
|
+
fontSize: fontSize.xs,
|
|
10
|
+
fontWeight: fontWeight.normal,
|
|
11
|
+
margin: '0',
|
|
12
|
+
};
|
|
13
|
+
/* * */
|
|
14
|
+
export default {
|
|
15
|
+
text,
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function Disclaimer(): import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import { GO_HOMEPAGE_URL } from '../../constants.js';
|
|
4
|
+
import { Link, Section, Text } from 'react-email';
|
|
5
|
+
import styles from './styles.js';
|
|
6
|
+
/* * */
|
|
7
|
+
export function Disclaimer() {
|
|
8
|
+
return (_jsx(Section, { style: styles.section, children: _jsxs(Text, { style: styles.text, children: ["Esta mensagem foi enviada automaticamente. Verifica sempre o email do remetente antes de carregares em qualquer link ou introduzires informa\u00E7\u00F5es pessoais em qualquer site. Fala connosco se precisares de mais informa\u00E7\u00F5es ou se achares que recebeste esta mensagem por engano. Acede a", ' ', _jsx(Link, { href: GO_HOMEPAGE_URL, style: styles.link, children: GO_HOMEPAGE_URL })] }) }));
|
|
9
|
+
}
|
|
10
|
+
;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { fontFamily, fontSize, fontWeight, lineHeight } from '../../styles/font.js';
|
|
3
|
+
/* * */
|
|
4
|
+
/* TEXT */
|
|
5
|
+
const text = {
|
|
6
|
+
color: '#BEBEC8',
|
|
7
|
+
fontFamily: fontFamily.primary,
|
|
8
|
+
fontSize: fontSize.xs,
|
|
9
|
+
fontWeight: fontWeight.normal,
|
|
10
|
+
lineHeight: lineHeight.normal,
|
|
11
|
+
};
|
|
12
|
+
const section = {
|
|
13
|
+
fontFamily: fontFamily.primary,
|
|
14
|
+
fontSize: fontSize.xs,
|
|
15
|
+
fontWeight: fontWeight.normal,
|
|
16
|
+
lineHeight: lineHeight.normal,
|
|
17
|
+
padding: '30px',
|
|
18
|
+
paddingTop: 0,
|
|
19
|
+
};
|
|
20
|
+
/* * */
|
|
21
|
+
/* LINK */
|
|
22
|
+
const link = {
|
|
23
|
+
color: '#BEBEC8',
|
|
24
|
+
textDecoration: 'underline',
|
|
25
|
+
};
|
|
26
|
+
/* * */
|
|
27
|
+
export default {
|
|
28
|
+
link,
|
|
29
|
+
section,
|
|
30
|
+
text,
|
|
31
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { fontFamily, fontSize, fontWeight } from '../../styles/font.js';
|
|
3
|
+
/* * */
|
|
4
|
+
/* TEXT */
|
|
5
|
+
const text = {
|
|
6
|
+
color: '#000',
|
|
7
|
+
fontFamily: fontFamily.primary,
|
|
8
|
+
fontSize: fontSize.lg,
|
|
9
|
+
fontWeight: fontWeight.bold,
|
|
10
|
+
margin: '0',
|
|
11
|
+
marginBottom: '20px',
|
|
12
|
+
textAlign: 'left',
|
|
13
|
+
};
|
|
14
|
+
/* * */
|
|
15
|
+
export default {
|
|
16
|
+
text,
|
|
17
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import { Button, Section } from 'react-email';
|
|
4
|
+
import styles from './styles.js';
|
|
5
|
+
/* * */
|
|
6
|
+
export function MainButton({ href, label }) {
|
|
7
|
+
return (_jsx(Section, { style: styles.container, children: _jsx(Section, { style: styles.section, children: _jsx(Button, { href: href, style: styles.button, children: label }) }) }));
|
|
8
|
+
}
|
|
9
|
+
;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { fontFamily, fontSize, fontWeight } from '../../styles/font.js';
|
|
3
|
+
/* * */
|
|
4
|
+
const button = {
|
|
5
|
+
color: '#FFFFFF',
|
|
6
|
+
display: 'block',
|
|
7
|
+
fontFamily: fontFamily.primary,
|
|
8
|
+
fontSize: fontSize.lg,
|
|
9
|
+
fontWeight: fontWeight.bold,
|
|
10
|
+
padding: 12,
|
|
11
|
+
textAlign: 'center',
|
|
12
|
+
textTransform: 'uppercase',
|
|
13
|
+
width: '100%',
|
|
14
|
+
};
|
|
15
|
+
const section = {
|
|
16
|
+
backgroundColor: '#005ADC',
|
|
17
|
+
borderColor: '#0041A0',
|
|
18
|
+
borderRadius: 5,
|
|
19
|
+
borderStyle: 'solid',
|
|
20
|
+
borderWidth: 3,
|
|
21
|
+
boxShadow: '0 1px 3px 0 #000A5040',
|
|
22
|
+
boxSizing: 'border-box',
|
|
23
|
+
textAlign: 'center',
|
|
24
|
+
width: '100%',
|
|
25
|
+
};
|
|
26
|
+
const container = {
|
|
27
|
+
paddingBottom: 30,
|
|
28
|
+
paddingTop: 10,
|
|
29
|
+
textAlign: 'center',
|
|
30
|
+
width: '100%',
|
|
31
|
+
};
|
|
32
|
+
/* * */
|
|
33
|
+
export default {
|
|
34
|
+
button,
|
|
35
|
+
container,
|
|
36
|
+
section,
|
|
37
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type PropsWithChildren } from 'react';
|
|
2
|
+
interface ParagraphProps {
|
|
3
|
+
bold?: boolean;
|
|
4
|
+
color?: 'danger' | 'info' | 'muted' | 'prose' | 'success' | 'warning';
|
|
5
|
+
size?: 'lg' | 'md' | 'sm';
|
|
6
|
+
}
|
|
7
|
+
export declare function Paragraph({ bold, children, color, size }: PropsWithChildren<ParagraphProps>): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import colors from '../../styles/colors.js';
|
|
4
|
+
import { fontSize, fontWeight } from '../../styles/font.js';
|
|
5
|
+
import { Text } from 'react-email';
|
|
6
|
+
import styles from './styles.js';
|
|
7
|
+
/* * */
|
|
8
|
+
export function Paragraph({ bold, children, color = 'prose', size = 'lg' }) {
|
|
9
|
+
return (_jsx(Text, { style: {
|
|
10
|
+
...styles.text,
|
|
11
|
+
color: colors[color].foreground,
|
|
12
|
+
fontSize: fontSize[size],
|
|
13
|
+
fontWeight: bold ? fontWeight.bold : fontWeight.normal,
|
|
14
|
+
}, children: children }));
|
|
15
|
+
}
|
|
16
|
+
;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { fontFamily, fontSize, lineHeight } from '../../styles/font.js';
|
|
3
|
+
/* * */
|
|
4
|
+
const text = {
|
|
5
|
+
color: '#000000',
|
|
6
|
+
fontFamily: fontFamily.primary,
|
|
7
|
+
fontSize: fontSize.lg,
|
|
8
|
+
lineHeight: lineHeight.normal,
|
|
9
|
+
margin: 0,
|
|
10
|
+
marginBottom: 15,
|
|
11
|
+
textAlign: 'left',
|
|
12
|
+
};
|
|
13
|
+
/* * */
|
|
14
|
+
export default {
|
|
15
|
+
text,
|
|
16
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type PropsWithChildren } from 'react';
|
|
2
|
+
interface SpanProps {
|
|
3
|
+
color?: 'danger' | 'info' | 'prose' | 'warning';
|
|
4
|
+
size?: 'lg' | 'md' | 'sm';
|
|
5
|
+
spaceAfter?: boolean;
|
|
6
|
+
spaceBefore?: boolean;
|
|
7
|
+
weight?: 'bold' | 'normal';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Use this component to apply styles to inline text
|
|
11
|
+
* within other components, such as Paragraph.
|
|
12
|
+
*/
|
|
13
|
+
export declare function Span({ children, color, size, spaceAfter, spaceBefore, weight }: PropsWithChildren<SpanProps>): import("react").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import colors from '../../styles/colors.js';
|
|
4
|
+
import { fontSize, fontWeight } from '../../styles/font.js';
|
|
5
|
+
/**
|
|
6
|
+
* Use this component to apply styles to inline text
|
|
7
|
+
* within other components, such as Paragraph.
|
|
8
|
+
*/
|
|
9
|
+
export function Span({ children, color, size, spaceAfter, spaceBefore, weight }) {
|
|
10
|
+
return (_jsxs(_Fragment, { children: [spaceBefore && ' ', _jsx("span", { style: {
|
|
11
|
+
color: color ? colors[color].foreground : undefined,
|
|
12
|
+
fontSize: size ? fontSize[size] : undefined,
|
|
13
|
+
fontWeight: weight ? fontWeight[weight] : undefined,
|
|
14
|
+
}, children: children }), spaceAfter && ' '] }));
|
|
15
|
+
}
|
|
16
|
+
;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* * */
|
|
3
|
+
import { CoverLogo } from '../CoverLogo/index.js';
|
|
4
|
+
import { Disclaimer } from '../Disclaimer/index.js';
|
|
5
|
+
import { fontWeight } from '../../styles/font.js';
|
|
6
|
+
import { Body, Container, Font, Head, Html, Preview, Section } from 'react-email';
|
|
7
|
+
import styles from './styles.js';
|
|
8
|
+
/* * */
|
|
9
|
+
export function Wrapper({ children, previewMessage }) {
|
|
10
|
+
return (_jsxs(Html, { children: [_jsxs(Head, { children: [_jsx(Font, { fallbackFontFamily: "Verdana", fontFamily: "Work Sans", fontStyle: "normal", fontWeight: fontWeight.normal, webFont: { format: 'woff2', url: 'https://storage.carrismetropolitana.pt/static/test/work-sans-regular.woff2' } }), _jsx(Font, { fallbackFontFamily: "Verdana", fontFamily: "Work Sans", fontStyle: "normal", fontWeight: fontWeight.bold, webFont: { format: 'woff2', url: 'https://storage.carrismetropolitana.pt/static/test/work-sans-regular.woff2' } }), _jsx(Font, { fallbackFontFamily: "Verdana", fontFamily: "Work Sans", fontStyle: "italic", fontWeight: fontWeight.normal, webFont: { format: 'woff2', url: 'https://storage.carrismetropolitana.pt/static/test/work-sans-italic.woff2' } }), _jsx(Font, { fallbackFontFamily: "Verdana", fontFamily: "Work Sans", fontStyle: "italic", fontWeight: fontWeight.bold, webFont: { format: 'woff2', url: 'https://storage.carrismetropolitana.pt/static/test/work-sans-italic.woff2' } })] }), _jsxs(Body, { style: styles.body, children: [_jsx(Preview, { children: previewMessage }), _jsxs(Container, { style: styles.container, children: [_jsx(CoverLogo, {}), _jsx(Section, { style: styles.content, children: children }), _jsx(Disclaimer, {})] })] })] }));
|
|
11
|
+
}
|
|
12
|
+
;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
/* * */
|
|
3
|
+
const body = {
|
|
4
|
+
backgroundColor: '#FFFFFF',
|
|
5
|
+
padding: 0,
|
|
6
|
+
};
|
|
7
|
+
const container = {
|
|
8
|
+
backgroundColor: '#F6F8FA',
|
|
9
|
+
maxWidth: '600px',
|
|
10
|
+
width: '100%',
|
|
11
|
+
};
|
|
12
|
+
const content = {
|
|
13
|
+
padding: 30,
|
|
14
|
+
paddingBottom: 10,
|
|
15
|
+
paddingTop: 10,
|
|
16
|
+
width: '100%',
|
|
17
|
+
};
|
|
18
|
+
/* * */
|
|
19
|
+
export default {
|
|
20
|
+
body,
|
|
21
|
+
container,
|
|
22
|
+
content,
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const GO_HOMEPAGE_URL = "https://go.tmlmobilidade.pt";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
export declare class EmailProvider {
|
|
3
|
+
private static _instance;
|
|
4
|
+
private _refreshToken;
|
|
5
|
+
private _smtpTransporter;
|
|
6
|
+
/**
|
|
7
|
+
* Return the instance of the EmailProvider.
|
|
8
|
+
*/
|
|
9
|
+
static getInstance(): Promise<EmailProvider>;
|
|
10
|
+
/**
|
|
11
|
+
* Connect to the SMTP server and return the transporter instance.
|
|
12
|
+
*/
|
|
13
|
+
connect(): Promise<nodemailer.Transporter>;
|
|
14
|
+
/**
|
|
15
|
+
* Send an email.
|
|
16
|
+
* @param emailOptions - The email options.
|
|
17
|
+
* @returns A promise that resolves when the email is sent.
|
|
18
|
+
*/
|
|
19
|
+
send(sendMailOptions: nodemailer.SendMailOptions): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Create an SMTP transporter with the current OAuth2 access token.
|
|
22
|
+
*/
|
|
23
|
+
private createSmtpTransporter;
|
|
24
|
+
/**
|
|
25
|
+
* Fetch and cache the Microsoft OAuth2 access token.
|
|
26
|
+
*/
|
|
27
|
+
private getRefreshToken;
|
|
28
|
+
}
|
|
29
|
+
export declare const emailProvider: EmailProvider;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { asyncSingletonProxy } from '@tmlmobilidade/utils';
|
|
3
|
+
import nodemailer from 'nodemailer';
|
|
4
|
+
export class EmailProvider {
|
|
5
|
+
//
|
|
6
|
+
static _instance;
|
|
7
|
+
_refreshToken = { expiresAt: undefined, token: undefined };
|
|
8
|
+
_smtpTransporter;
|
|
9
|
+
/**
|
|
10
|
+
* Return the instance of the EmailProvider.
|
|
11
|
+
*/
|
|
12
|
+
static async getInstance() {
|
|
13
|
+
if (!EmailProvider._instance) {
|
|
14
|
+
const instance = new EmailProvider();
|
|
15
|
+
await instance.connect();
|
|
16
|
+
EmailProvider._instance = instance;
|
|
17
|
+
}
|
|
18
|
+
return EmailProvider._instance;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Connect to the SMTP server and return the transporter instance.
|
|
22
|
+
*/
|
|
23
|
+
async connect() {
|
|
24
|
+
try {
|
|
25
|
+
// Check for required environment variables
|
|
26
|
+
if (!process.env.TML_PROVIDER_EMAIL_SERVER_HOST)
|
|
27
|
+
throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_SERVER_HOST');
|
|
28
|
+
if (!process.env.TML_PROVIDER_EMAIL_SERVER_PORT)
|
|
29
|
+
throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_SERVER_PORT');
|
|
30
|
+
if (!process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_ID)
|
|
31
|
+
throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_CLIENT_ID');
|
|
32
|
+
if (!process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET)
|
|
33
|
+
throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET');
|
|
34
|
+
if (!process.env.TML_PROVIDER_EMAIL_AUTH_ACCESS_URL)
|
|
35
|
+
throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_ACCESS_URL');
|
|
36
|
+
if (!process.env.TML_PROVIDER_EMAIL_AUTH_USER)
|
|
37
|
+
throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_USER');
|
|
38
|
+
if (!process.env.TML_PROVIDER_EMAIL_FROM)
|
|
39
|
+
throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_FROM');
|
|
40
|
+
const accessToken = await this.getRefreshToken();
|
|
41
|
+
// Connect to the SMTP server
|
|
42
|
+
this._smtpTransporter = this.createSmtpTransporter(accessToken);
|
|
43
|
+
return this._smtpTransporter;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new Error('Error connecting to SMTP server', { cause: error });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Send an email.
|
|
51
|
+
* @param emailOptions - The email options.
|
|
52
|
+
* @returns A promise that resolves when the email is sent.
|
|
53
|
+
*/
|
|
54
|
+
async send(sendMailOptions) {
|
|
55
|
+
try {
|
|
56
|
+
const currentAccessToken = this._refreshToken.token;
|
|
57
|
+
const accessToken = await this.getRefreshToken();
|
|
58
|
+
if (accessToken !== currentAccessToken)
|
|
59
|
+
this._smtpTransporter = this.createSmtpTransporter(accessToken);
|
|
60
|
+
await this._smtpTransporter.sendMail({
|
|
61
|
+
...this._smtpTransporter.options,
|
|
62
|
+
...sendMailOptions,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw new Error('Error sending email', { cause: error });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create an SMTP transporter with the current OAuth2 access token.
|
|
71
|
+
*/
|
|
72
|
+
createSmtpTransporter(accessToken) {
|
|
73
|
+
return nodemailer.createTransport({
|
|
74
|
+
auth: {
|
|
75
|
+
accessToken,
|
|
76
|
+
accessUrl: process.env.TML_PROVIDER_EMAIL_AUTH_ACCESS_URL,
|
|
77
|
+
clientId: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_ID,
|
|
78
|
+
clientSecret: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET,
|
|
79
|
+
type: 'OAuth2',
|
|
80
|
+
user: process.env.TML_PROVIDER_EMAIL_AUTH_USER,
|
|
81
|
+
},
|
|
82
|
+
from: process.env.TML_PROVIDER_EMAIL_FROM,
|
|
83
|
+
host: process.env.TML_PROVIDER_EMAIL_SERVER_HOST,
|
|
84
|
+
port: Number(process.env.TML_PROVIDER_EMAIL_SERVER_PORT),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Fetch and cache the Microsoft OAuth2 access token.
|
|
89
|
+
*/
|
|
90
|
+
async getRefreshToken() {
|
|
91
|
+
if (this._refreshToken.token && this._refreshToken.expiresAt && this._refreshToken.expiresAt > Date.now())
|
|
92
|
+
return this._refreshToken.token;
|
|
93
|
+
const requestBody = new URLSearchParams({
|
|
94
|
+
client_id: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_ID,
|
|
95
|
+
client_secret: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET,
|
|
96
|
+
grant_type: 'client_credentials',
|
|
97
|
+
scope: 'https://outlook.office365.com/.default',
|
|
98
|
+
}).toString();
|
|
99
|
+
const response = await fetch(process.env.TML_PROVIDER_EMAIL_AUTH_ACCESS_URL, {
|
|
100
|
+
body: requestBody,
|
|
101
|
+
headers: {
|
|
102
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
103
|
+
},
|
|
104
|
+
method: 'POST',
|
|
105
|
+
});
|
|
106
|
+
const responseText = await response.text();
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new Error(`Token request failed (${response.status}): ${responseText.slice(0, 500)}`);
|
|
109
|
+
}
|
|
110
|
+
const data = JSON.parse(responseText);
|
|
111
|
+
this._refreshToken = {
|
|
112
|
+
expiresAt: Date.now() + (data.expires_in * 1000),
|
|
113
|
+
token: data.access_token,
|
|
114
|
+
};
|
|
115
|
+
return this._refreshToken.token;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export const emailProvider = asyncSingletonProxy(EmailProvider);
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
danger: {
|
|
3
|
+
background: string;
|
|
4
|
+
foreground: string;
|
|
5
|
+
};
|
|
6
|
+
info: {
|
|
7
|
+
background: string;
|
|
8
|
+
foreground: string;
|
|
9
|
+
};
|
|
10
|
+
muted: {
|
|
11
|
+
background: string;
|
|
12
|
+
foreground: string;
|
|
13
|
+
};
|
|
14
|
+
prose: {
|
|
15
|
+
background: string;
|
|
16
|
+
foreground: string;
|
|
17
|
+
};
|
|
18
|
+
success: {
|
|
19
|
+
background: string;
|
|
20
|
+
foreground: string;
|
|
21
|
+
};
|
|
22
|
+
warning: {
|
|
23
|
+
background: string;
|
|
24
|
+
foreground: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export default _default;
|