Skip to content

Commit

Permalink
fix: show different mails for non registered users
Browse files Browse the repository at this point in the history
  • Loading branch information
yxuo committed Feb 8, 2024
1 parent 84c7ca4 commit 5d96c66
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 11 deletions.
74 changes: 74 additions & 0 deletions src/cron-jobs/cron-jobs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { HttpStatus, Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { SchedulerRegistry } from '@nestjs/schedule';
import { CronJob, CronJobParameters } from 'cron';
import { InviteStatus } from 'src/mail-history-statuses/entities/mail-history-status.entity';
import { InviteStatusEnum } from 'src/mail-history-statuses/mail-history-status.enum';
import { MailHistory } from 'src/mail-history/entities/mail-history.entity';
import { MailHistoryService } from 'src/mail-history/mail-history.service';
Expand All @@ -10,6 +11,7 @@ import { appSettings } from 'src/settings/app.settings';
import { SettingEntity } from 'src/settings/entities/setting.entity';
import { ISettingData } from 'src/settings/interfaces/setting-data.interface';
import { SettingsService } from 'src/settings/settings.service';
import { User } from 'src/users/entities/user.entity';
import { UsersService } from 'src/users/users.service';
import {
formatErrorMessage as formatErrorLog,
Expand All @@ -24,6 +26,7 @@ export enum CrobJobsEnum {
bulkSendInvites = 'bulkSendInvites',
sendStatusReport = 'sendStatusReport',
pollDb = 'pollDb',
bulkResendInvites = 'bulkResendInvites',
}

interface ICronJob {
Expand Down Expand Up @@ -95,6 +98,13 @@ export class CronJobsService implements OnModuleInit {
onTick: () => this.pollDb(),
},
},
{
name: CrobJobsEnum.bulkResendInvites,
cronJobParameters: {
cronTime: '* * * * *', //'45 14 * * *', // 14:45 GMT = 11:45BRT (GMT-3)
onTick: async () => this.bulkResendInvites(),
},
},
);

for (const jobConfig of this.jobsConfig) {
Expand Down Expand Up @@ -556,4 +566,68 @@ export class CronJobsService implements OnModuleInit {
isSettingValid: true,
};
}

async bulkResendInvites() {
const THIS_METHOD = `${this.bulkResendInvites.name}()`;
const notRegisteredUsers = await this.usersService.getNotRegisteredUsers();

if (notRegisteredUsers.length === 0) {
this.logger.log(
formatLog('Não há usuários para enviar, abortando...', THIS_METHOD),
);
return;
}
this.logger.log(
formatLog(
String(
'Enviando emails específicos para ' +
`${notRegisteredUsers.length} usuários não totalmente registrados`,
),
THIS_METHOD,
),
);
for (const user of notRegisteredUsers) {
await this.resendInvite(user, THIS_METHOD);
}
}

async resendInvite(user: User, outerMethod: string) {
const THIS_METHOD = `${outerMethod} > ${this.resendInvite.name}`;
try {
const mailSentInfo = await this.mailService.reSendEmailBank({
to: user.email as string,
data: {
hash: user.aux_inviteHash as string,
inviteStatus: user.aux_inviteStatus as InviteStatus,
},
});

// Success
if (mailSentInfo.success) {
this.logger.log(formatLog('Email enviado com sucesso.', THIS_METHOD));
}

// SMTP error
else {
this.logger.error(
formatErrorLog(
'Email enviado retornou erro.',
mailSentInfo,
new Error(),
THIS_METHOD,
),
);
}
} catch (httpException) {
// API error
this.logger.error(
formatErrorLog(
'Email falhou ao enviar.',
httpException,
httpException as Error,
THIS_METHOD,
),
);
}
}
}
10 changes: 10 additions & 0 deletions src/database/seeds/mail-history/mail-history-seed-data.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable } from '@nestjs/common';
import { subDays } from 'date-fns';
import { InviteStatus } from 'src/mail-history-statuses/entities/mail-history-status.entity';
import { InviteStatusEnum } from 'src/mail-history-statuses/mail-history-status.enum';
import { IMailSeedData } from 'src/mail-history/interfaces/mail-history-data.interface';
Expand All @@ -22,6 +23,15 @@ export class MailHistorySeedDataService {
inviteStatus: new InviteStatus(InviteStatusEnum.used),
} as IMailSeedData),
);
for (let i = 0; i < mailSeedData.length; i++) {
const mail = mailSeedData[i];
if (mail.email === 'registered.user@example.com') {
mail[i] = {
...mail,
sentAt: subDays(new Date(), 16),
} as IMailSeedData;
}
}
return mailSeedData;
}
}
13 changes: 13 additions & 0 deletions src/database/seeds/user/user-seed-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ export class UserSeedDataService {
status: { id: StatusEnum.active } as Status,
inviteStatus: new InviteStatus(InviteStatusEnum.used),
},
{
fullName: 'Used registered user',
email: 'registered.user@example.com',
password: 'secret',
permitCode: '319274392832024',
role: { id: RoleEnum.user } as Role,
status: { id: StatusEnum.active } as Status,
inviteStatus: new InviteStatus(InviteStatusEnum.used),
bankCode: 104,
bankAgency: '1234',
bankAccount: '12345',
bankAccountDigit: '1',
},
] as UserDataInterface[])
: []),
];
Expand Down
1 change: 0 additions & 1 deletion src/database/seeds/user/user-seed.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export class UserSeedService {

async run() {
const userFixtures = await this.userSeedDataService.getDataFromConfig();
this.logger.log(`run() ${userFixtures.length} items`);
for (const item of userFixtures) {
const foundItem = await this.userSeedRepository.findOne({
where: {
Expand Down
10 changes: 9 additions & 1 deletion src/mail-history/mail-history.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Provider } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import { DataSource, EntityManager, Repository } from 'typeorm';
import { MailHistory } from './entities/mail-history.entity';
import { MailHistoryService } from './mail-history.service';

Expand Down Expand Up @@ -32,12 +32,20 @@ describe('InviteService', () => {
query: jest.fn(),
},
} as Provider;
const entityManagerMock = {
provide: EntityManager,
useValue: {
createQueryBuilder: jest.fn(),
transaction: jest.fn(),
},
} as Provider;
const module: TestingModule = await Test.createTestingModule({
providers: [
MailHistoryService,
mailHistoryRepositoryMock,
configServiceMock,
dataSourceMock,
entityManagerMock,
],
}).compile();

Expand Down
7 changes: 3 additions & 4 deletions src/mail-history/mail-history.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
import { ConfigService } from '@nestjs/config';
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import * as crypto from 'crypto';
import { startOfDay } from 'date-fns';
import { IMailHistoryStatusCount } from 'src/mail-history-statuses/interfaces/mail-history-status-group.interface';
Expand All @@ -13,8 +13,8 @@ import { formatLog } from 'src/utils/logging';
import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { NullableType } from 'src/utils/types/nullable.type';
import {
DataSource,
DeepPartial,
EntityManager,
Equal,
MoreThanOrEqual,
Repository,
Expand All @@ -31,8 +31,7 @@ export class MailHistoryService {
@InjectRepository(MailHistory)
private inviteRepository: Repository<MailHistory>,
private configService: ConfigService,
@InjectDataSource()
private readonly dataSource: DataSource,
private readonly entityManager: EntityManager,
) {}

async create(
Expand Down
68 changes: 68 additions & 0 deletions src/mail/mail-templates/user-daily-conclude.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=">
<title>{{title}}</title>
<style>
#tableStyle,
#tableStyle td,
#tableStyle th {
border: 1px solid;
border-collapse: collapse;
color: #505050;
}
#tableStyle th,
#tableStyle td {
padding: 5px;
text-align: left;
}
</style>
</head>

<body style="margin:0;font-family:arial">
<table style="border:0;width:100%">
<tr style="background:rgba(150,150,150,0.2)">
<td style="padding:12px;color:#004a80;text-align:left;font-size:16px;font-weight:600;">
SMTR - Prefeitura do Município do Rio de Janeiro - CCT Mobilidade RIO
</td>
</tr>
<tr>
<td style="padding:20px;color:#505050;font-size:16px;font-weight:100">
<table id="tableStyle">
<thead style="text-align: left;background-color: red;" >
<tr>
<th><b><span style="color:white">IMPORTANTE!</span></b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>
<p>
O Sistema CCT Mobilidade RIO detectou que seus <span style="color:red">Dados Bancários</span> ainda não foram cadastrados.
Por favor, solicitamos que atualize o cadastro pelo link abaixo!
</p>
<p><a href="{{userLink}}"> {{userLink}}</a></p>
<p>Solicitamos que anote e guarde sua senha com segurança, para evitar futuros problemas de acesso.</p>
<p>
Caso aconteça, entre em contato com o suporte <a href="https://secretariamunicipaldetransportes.movidesk.com/form/6594/">https://secretariamunicipaldetransportes.movidesk.com/form/6594/</a>.

</p>
<p>Obrigado!</p>
</b></td>
</tr>
</tbody>
</table>
<p style="font-size: 12px;">
Essa é uma mensagem automática do sistema, sem monitoramento. <br>
Por favor, não responder!
</p>

</td>
</tr>
</table>
</body>

</html>
58 changes: 58 additions & 0 deletions src/mail/mail.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { I18nContext } from 'nestjs-i18n';
import { AllConfigType } from 'src/config/config.type';
import { InviteStatus } from 'src/mail-history-statuses/entities/mail-history-status.entity';
import { IMailHistoryStatusCount } from 'src/mail-history-statuses/interfaces/mail-history-status-group.interface';
import { InviteStatusEnum } from 'src/mail-history-statuses/mail-history-status.enum';
import { SmtpStatus } from 'src/utils/enums/smtp-status.enum';
import { formatLog } from 'src/utils/logging';
import { MaybeType } from '../utils/types/maybe.type';
Expand Down Expand Up @@ -212,4 +214,60 @@ export class MailService {
throw httpException;
}
}

/**
* @throws `HttpException`
*/
async reSendEmailBank(
mailData: MailData<{
inviteStatus: InviteStatus;
hash?: string;
}>,
): Promise<MailSentInfo> {
const mailTitle =
'SMTR - Prefeitura do Município do Rio de Janeiro - Comunicado Importante!';
const from = this.configService.get('mail.senderNotification', {
infer: true,
});
const frontendDomain = this.configService.get('app.frontendDomain', {
infer: true,
});
const inviteStatus = mailData.data.inviteStatus;
if (!from) {
throw new HttpException(
{
error: HttpStatus.INTERNAL_SERVER_ERROR,
details: {
env: `Env 'MAIL_SENDER_NOTIFICATION' not found (got: '${from}')`,
},
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}

try {
const appName = this.configService.get('app.name', {
infer: true,
});
const response = await this.safeSendMail({
from,
to: mailData.to,
subject: mailTitle,
text: mailTitle,
template: 'user-daily-conclude',
context: {
title: 'Confirme seu email',
userLink:
inviteStatus.id === InviteStatusEnum.used
? `${frontendDomain}sign-in`
: `${frontendDomain}conclude-registration/${mailData.data.hash}`,
headerTitle: appName,
},
});

return response;
} catch (httpException) {
throw httpException;
}
}
}
3 changes: 3 additions & 0 deletions src/users/entities/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ export class User extends EntityHelper {

aux_inviteStatus?: InviteStatus | null;

@Exclude()
aux_inviteHash?: string | null;

aux_bank?: Bank | null;

/**
Expand Down
4 changes: 4 additions & 0 deletions src/users/interfaces/user-data.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ export interface UserDataInterface {
role: Role;
status: Status;
inviteStatus?: InviteStatus;
bankCode?: number;
bankAgency?: string;
bankAccount?: string;
bankAccountDigit?: string;
}
Loading

0 comments on commit 5d96c66

Please sign in to comment.