Skip to content

Commit 0f83ade

Browse files
VayrasVayras
authored andcommitted
draft for automated email service
1 parent ec11aea commit 0f83ade

12 files changed

+2253
-181
lines changed

config/dev.config.example.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,14 @@ app:
3939
auth:
4040
callbackPath: '/auth/discord/callback'
4141
dashboardRedirectPath: '/health'
42-
sessionTtlSeconds: 604800 # 7 days
42+
sessionTtlSeconds: 604800 # 7 days
43+
email:
44+
smtp:
45+
host: smtp.gmail.com # SMTP server host (e.g., smtp.gmail.com, smtp.sendgrid.net)
46+
port: 587 # SMTP port (587 for TLS, 465 for SSL)
47+
secure: false # true for SSL (port 465), false for TLS (port 587)
48+
user: [email protected] # SMTP username/email
49+
pass: your-app-password # SMTP password or app-specific password
50+
from:
51+
email: [email protected] # Sender email address
52+
name: Bitshala # Sender name

package-lock.json

Lines changed: 1504 additions & 180 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@
4444
"@nestjs/throttler": "^6.4.0",
4545
"@nestjs/typeorm": "^10.0.2",
4646
"@nestjs/websockets": "^10.3.7",
47+
"@types/nodemailer": "^7.0.3",
4748
"axios": "^1.7.2",
4849
"currency.js": "^2.0.4",
4950
"js-yaml": "^4.1.0",
5051
"nest-winston": "^1.10.2",
52+
"nodemailer": "^7.0.10",
5153
"reflect-metadata": "^0.1.13",
5254
"rxjs": "^7.2.0",
5355
"typeorm": "^0.3.20",

src/cohorts/cohorts.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { DiscordClientModule } from '@/discord-client/discord.client.module';
99
import { CohortWaitlist } from '@/entities/cohort-waitlist.entity';
1010
import { APITask } from '@/entities/api-task.entity';
1111
import { User } from '@/entities/user.entity';
12+
import { EmailModule } from '@/services/email/email.module';
1213

1314
@Module({
1415
imports: [
@@ -21,6 +22,7 @@ import { User } from '@/entities/user.entity';
2122
]),
2223
DbTransactionModule,
2324
DiscordClientModule,
25+
EmailModule,
2426
],
2527
providers: [CohortsService],
2628
controllers: [CohortsController],

src/cohorts/cohorts.service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { CohortType } from '@/common/enum';
2525
import { CohortWaitlist } from '@/entities/cohort-waitlist.entity';
2626
import { APITask } from '@/entities/api-task.entity';
2727
import { TaskType } from '@/task-processor/task.enums';
28+
import { AutomatedEmailService } from '@/services/email/automated-email.service';
2829

2930
@Injectable()
3031
export class CohortsService {
@@ -47,6 +48,7 @@ export class CohortsService {
4748
private readonly dbTransactionService: DbTransactionService,
4849
private readonly discordClient: DiscordClient,
4950
private readonly configService: ConfigService,
51+
private readonly automatedEmailService: AutomatedEmailService,
5052
) {
5153
this.masteringBitcoinDiscordRoleId =
5254
this.configService.getOrThrow<string>(
@@ -464,6 +466,15 @@ export class CohortsService {
464466
waitlistEntry.type = body.type;
465467

466468
await this.cohortWaitlistRepository.save(waitlistEntry);
469+
470+
// Send automated welcome email to the user
471+
const userName =
472+
user.name || user.discordGlobalName || user.discordUserName;
473+
await this.automatedEmailService.sendWaitlistWelcomeEmail(
474+
user.email,
475+
userName,
476+
body.type,
477+
);
467478
}
468479

469480
async getUserWaitlist(user: User): Promise<UserCohortWaitlistResponseDto> {

src/configuration.model.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,50 @@ class AppConfig {
169169
auth: AppAuthConfig;
170170
}
171171

172+
class EmailSmtpConfig {
173+
@IsString()
174+
@IsNotEmpty()
175+
host: string;
176+
177+
@IsInt()
178+
@Min(1)
179+
@Max(65535)
180+
port: number;
181+
182+
@IsBoolean()
183+
secure: boolean;
184+
185+
@IsString()
186+
@IsNotEmpty()
187+
user: string;
188+
189+
@IsString()
190+
@IsNotEmpty()
191+
pass: string;
192+
}
193+
194+
class EmailFromConfig {
195+
@IsString()
196+
@IsNotEmpty()
197+
email: string;
198+
199+
@IsString()
200+
@IsNotEmpty()
201+
name: string;
202+
}
203+
204+
class EmailConfig {
205+
@IsDefined()
206+
@ValidateNested()
207+
@Type(() => EmailSmtpConfig)
208+
smtp: EmailSmtpConfig;
209+
210+
@IsDefined()
211+
@ValidateNested()
212+
@Type(() => EmailFromConfig)
213+
from: EmailFromConfig;
214+
}
215+
172216
export class Config {
173217
@IsDefined()
174218
@ValidateNested()
@@ -189,4 +233,9 @@ export class Config {
189233
@ValidateNested()
190234
@Type(() => AppConfig)
191235
app: AppConfig;
236+
237+
@IsOptional()
238+
@ValidateNested()
239+
@Type(() => EmailConfig)
240+
email?: EmailConfig;
192241
}

0 commit comments

Comments
 (0)