Skip to content

Commit 36ae799

Browse files
authored
th-97: Implement mail service (#107)
* th-97: + mail service * th-97: + views (only 'plain' view at the moment) * th-97: * comment fixes * th-97: * fix PR comments * th-97: * Resolved PR comments
1 parent d348a5c commit 36ae799

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+432
-25
lines changed

backend/.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ JWT_SECRET=very_secret_secret
1111
JWT_ISSUER=jwt_issuer
1212
JWT_ACCESS_LIFETIME=24h
1313

14+
#
15+
# SENDGRID
16+
#
17+
SENDGRID_API_KEY=YOUR_API_KEY
18+
SENDGRID_USER=apikey
19+
SENDGRID_SENDER_EMAIL=YOUR_VERIFIED_SENDER_EMAIL
20+
SMTP_TLS=true
21+
1422
#
1523
# DATABASE
1624
#

backend/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@types/bcryptjs": "2.4.3",
3636
"@types/convict": "6.1.3",
3737
"@types/swagger-jsdoc": "6.0.1",
38+
"@types/nodemailer": "6.4.9",
3839
"bcryptjs": "2.4.3",
3940
"convict": "6.2.4",
4041
"dotenv": "16.3.1",
@@ -43,6 +44,8 @@
4344
"fastify": "4.21.0",
4445
"fastify-plugin": "4.5.1",
4546
"jose": "4.14.4",
47+
"handlebars": "4.7.8",
48+
"nodemailer": "6.9.4",
4649
"pg": "8.11.3",
4750
"pino": "8.15.0",
4851
"pino-pretty": "10.2.0",

backend/src/libs/exceptions/database/database-connection.exception.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { type Constructor } from '~/libs/types/types.js';
1+
import { type ErrorConstructor } from '~/libs/types/types.js';
22

33
class DatabaseConnectionError extends Error {
4-
public constructor({ message, cause }: Constructor) {
4+
public constructor({ message, cause }: ErrorConstructor) {
55
super(message, {
66
cause,
77
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
export { DatabaseConnectionError } from './database/database.js';
2+
export { MailerConnectionError } from './mailer/mailer.js';
23
export { NotFoundError } from './not-found-error/not-found-error.exception.js';
34
export {
45
ApplicationError,
6+
ConfigValidationError,
57
HttpError,
68
ValidationError,
79
} from 'shared/build/index.js';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { type ErrorConstructor } from '~/libs/types/types.js';
2+
3+
class MailerConnectionError extends Error {
4+
public constructor({ message, cause }: ErrorConstructor) {
5+
super(message, {
6+
cause,
7+
});
8+
}
9+
}
10+
11+
export { MailerConnectionError };
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { MailerConnectionError } from './mailer-connection.exception.js';

backend/src/libs/exceptions/not-found-error/not-found-error.exception.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import { type ValueOf } from '~/libs/types/types.js';
1+
import { AppErrorMessage, HttpCode } from '~/libs/enums/enums.js';
2+
import { type ErrorConstructor, type ValueOf } from '~/libs/types/types.js';
23

3-
import { AppErrorMessage, HttpCode } from '../../enums/enums.js';
44
import { ApplicationError } from '../exceptions.js';
55

6-
type Constructor = {
7-
message?: string;
8-
cause?: unknown;
9-
};
6+
type NotFoundErrorConstructor = Partial<ErrorConstructor>;
107

118
class NotFoundError extends ApplicationError {
129
public status: ValueOf<typeof HttpCode>;
1310

14-
public constructor({ message, cause }: Constructor) {
11+
public constructor({ message, cause }: NotFoundErrorConstructor) {
1512
super({
1613
message: message ?? AppErrorMessage.ENTITY_NOT_FOUND,
1714
cause,

backend/src/libs/packages/config/config.package.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import convict, { type Config as TConfig } from 'convict';
22
import { config } from 'dotenv';
33

44
import { AppEnvironment } from '~/libs/enums/enums.js';
5+
import { ConfigValidationError } from '~/libs/exceptions/exceptions.js';
56
import { type ILogger } from '~/libs/packages/logger/logger.js';
67

8+
import { FormatRegex } from './libs/enums/enums.js';
79
import { type IConfig } from './libs/interfaces/interfaces.js';
810
import { type EnvironmentSchema } from './libs/types/types.js';
911

@@ -28,6 +30,28 @@ class Config implements IConfig {
2830
}
2931

3032
private get envSchema(): TConfig<EnvironmentSchema> {
33+
convict.addFormat({
34+
name: 'boolean_string',
35+
validate: (value: string, schema: convict.SchemaObj) => {
36+
if (value !== 'true' && value !== 'false') {
37+
throw new ConfigValidationError({
38+
message: `Invalid ${schema.env ?? ''} format`,
39+
});
40+
}
41+
},
42+
});
43+
44+
convict.addFormat({
45+
name: 'email',
46+
validate: (value: string, schema: convict.SchemaObj) => {
47+
if (!FormatRegex.EMAIL.test(value)) {
48+
throw new ConfigValidationError({
49+
message: `Invalid ${schema.env ?? ''} format`,
50+
});
51+
}
52+
},
53+
});
54+
3155
return convict<EnvironmentSchema>({
3256
APP: {
3357
ENVIRONMENT: {
@@ -83,6 +107,32 @@ class Config implements IConfig {
83107
default: null,
84108
},
85109
},
110+
MAILER: {
111+
SENDGRID_API_KEY: {
112+
doc: 'Twilio SendGrid API key',
113+
format: String,
114+
env: 'SENDGRID_API_KEY',
115+
default: null,
116+
},
117+
SENDGRID_USER: {
118+
doc: 'Twilio SendGrid SMTP username',
119+
format: String,
120+
env: 'SENDGRID_USER',
121+
default: 'apikey',
122+
},
123+
SMTP_TLS: {
124+
doc: 'Whether SMTP connection uses TLS',
125+
env: 'SMTP_TLS',
126+
format: 'boolean_string',
127+
default: true,
128+
},
129+
SENDGRID_SENDER_EMAIL: {
130+
doc: 'Sendgrid verified sender email',
131+
env: 'SENDGRID_SENDER_EMAIL',
132+
format: 'email',
133+
default: null,
134+
},
135+
},
86136
});
87137
}
88138
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { FormatRegex } from 'shared/build/index.js';

backend/src/libs/packages/config/libs/types/environment-schema.type.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ type EnvironmentSchema = {
1616
POOL_MIN: number;
1717
POOL_MAX: number;
1818
};
19+
MAILER: {
20+
SENDGRID_API_KEY: string;
21+
SENDGRID_USER: string;
22+
SMTP_TLS: boolean;
23+
SENDGRID_SENDER_EMAIL: string;
24+
};
1925
};
2026

2127
export { type EnvironmentSchema };

0 commit comments

Comments
 (0)