Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic emails #20

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ SENTRY_DSN=SENTRY_DSN
AADIS_URL=AADIS_URL
POSTMARK_API_TOKEN=POSTMARK_API_TOKEN
POSTMARK_FEEDBACK_TEMPLATE_ID=POSTMARK_FEEDBACK_TEMPLATE_ID
POSTMARK_RECEIVED_REPORT_TEMPLATE_ID=POSTMARK_RECEIVED_REPORT_TEMPLATE_ID
POSTMARK_IN_INVESTIGATION_REPORT_TEMPLATE_ID=POSTMARK_IN_INVESTIGATION_REPORT_TEMPLATE_ID
POSTMARK_INVESTIGATED_REPORT_TEMPLATE_ID=POSTMARK_INVESTIGATED_REPORT_TEMPLATE_ID
ADMIN_EMAIL=ADMIN_EMAIL
3 changes: 2 additions & 1 deletion src/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Dump, DumpSchema } from '../repositories/dumps/schemas';
import { ReportRepository } from '../repositories/reports/report.repository';
import { DumpRepository } from '../repositories/dumps/dump.repository';
import { CloudinaryModule } from '../cloudinary/cloudinary.module';
import { PostmarkService } from 'src/report/postmark.service';

@Module({
imports: [
Expand All @@ -15,6 +16,6 @@ import { CloudinaryModule } from '../cloudinary/cloudinary.module';
CloudinaryModule,
],
controllers: [AdminController],
providers: [AdminService, ReportRepository, DumpRepository],
providers: [AdminService, ReportRepository, DumpRepository, PostmarkService],
})
export class AdminModule {}
1 change: 1 addition & 0 deletions src/admin/admin.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export class AdminService {
report.imageUrls,
report.historyData.map(AdminService.docToHistoryData),
report.statusRecords.map(AdminService.docToStatusRecords),
report.emailFeedbackStage,
);
}

Expand Down
5 changes: 5 additions & 0 deletions src/admin/dto/full-report.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export class FullReportDto {
@ApiProperty({ type: [StatusRecordsDto] })
statusRecords: StatusRecordsDto[];

@ApiProperty({ format: 'double' })
emailFeedbackStage: number;

constructor(
_id: string,
name: string,
Expand All @@ -85,6 +88,7 @@ export class FullReportDto {
imageUrls: string[],
historyData: HistoryDataDto[],
statusRecords: StatusRecordsDto[],
emailFeedbackStage: number,
) {
this._id = _id;
this.name = name;
Expand All @@ -105,5 +109,6 @@ export class FullReportDto {
this.imageUrls = imageUrls;
this.historyData = historyData;
this.statusRecords = statusRecords;
this.emailFeedbackStage = emailFeedbackStage;
}
}
5 changes: 5 additions & 0 deletions src/report/dto/create-report.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IsNotEmpty,
} from 'class-validator';
import { ReportCategory } from '../../common/dto/report-category';
import { ToBoolean } from 'src/common/transform/boolean.transform';

export class CreateReportDto {
@IsNotEmpty()
Expand All @@ -24,6 +25,10 @@ export class CreateReportDto {
@IsEmail()
email: string;

@ApiProperty()
@ToBoolean()
automaticEmailsEnabled: boolean;

@ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } })
images: any[];
}
64 changes: 63 additions & 1 deletion src/report/postmark.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export class PostmarkService {
email: string,
description: string
): Promise<String> {

const templatedMessage: TemplatedMessage = {
TemplateId: Number(process.env["POSTMARK_FEEDBACK_TEMPLATE_ID"]),
From: process.env["ADMIN_EMAIL"]!,
Expand All @@ -29,4 +28,67 @@ export class PostmarkService {
const message = this.client.sendEmailWithTemplate(templatedMessage);
return 'Successfully sent';
}

async sendReceivedReportEmail(
email: string,
link: string,
): Promise<String> {
const templatedMessage: TemplatedMessage = {
TemplateId: Number(process.env["POSTMARK_RECEIVED_REPORT_TEMPLATE_ID"]),
From: process.env["ADMIN_EMAIL"]!,
To: email,
TemplateModel: {
link: link,
}
};
const message = this.client.sendEmailWithTemplate(templatedMessage);
return 'Successfully sent';
}

async sendInInvestigationReportEmail(
email: string,
link: string,
): Promise<String> {
const templatedMessage: TemplatedMessage = {
TemplateId: Number(process.env["POSTMARK_IN_INVESTIGATION_REPORT_TEMPLATE_ID"]),
From: process.env["ADMIN_EMAIL"]!,
To: email,
TemplateModel: {
link: link,
}
};
const message = this.client.sendEmailWithTemplate(templatedMessage);
return 'Successfully sent';
}

async sendInvestigatedReportEmail(
email: string,
link: string,
): Promise<String> {
const templatedMessage: TemplatedMessage = {
TemplateId: Number(process.env["POSTMARK_INVESTIGATED_REPORT_TEMPLATE_ID"]),
From: process.env["ADMIN_EMAIL"]!,
To: email,
TemplateModel: {
link: link,
}
};
const message = this.client.sendEmailWithTemplate(templatedMessage);
return 'Successfully sent';
}

generateReportUrl(refId: string | number): string {
let id = '';
if (typeof refId === 'number') {
id = String(refId);
}else{
id = refId;
}
if (id.length > 8) {
throw new Error('refId cannot be longer than 8 characters.');
}
const paddedZeros = '0'.repeat(8 - id.length);
const reportName = "TLP-A"+paddedZeros+id.toUpperCase();
return "https://tvarkaulietuva.lt/pranesimas?id="+reportName;
}
}
1 change: 0 additions & 1 deletion src/report/report.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { FilesInterceptor } from "@nestjs/platform-express";
import { ReportStatisticsDto } from "./dto/report-statistics.dto";
import { ReportCategory } from "../common/dto/report-category";
import { PostmarkService } from "./postmark.service";
import { MessageSendingResponse } from "postmark/dist/client/models";
import { CreateFeedbackReportDto } from "./dto/create-feedback-report.dto";

@Controller("reports")
Expand Down
80 changes: 69 additions & 11 deletions src/repositories/reports/report.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import { HistoryDataDto } from '../../admin/dto/history-data.dto';
import { HistoryEditsDto } from '../../admin/dto/history-edits.dto';
import { ReportCategory } from '../../common/dto/report-category';
import { PostmarkService } from 'src/report/postmark.service';

export class ReportRepository {
constructor(
@InjectModel(Report.name) private reportModel: Model<Report>,
private cloudinary: CloudinaryService,
private readonly postmarkService: PostmarkService,
) {}

getVisibleReports(category?: ReportCategory): Promise<Report[]> {
Expand Down Expand Up @@ -79,6 +81,9 @@
}
const reports = await this.reportModel.find().exec();
const reportCount = reports.length;
if(reports != null && createReport.automaticEmailsEnabled != false) {
await this.postmarkService.sendReceivedReportEmail(createReport.email, this.postmarkService.generateReportUrl(reportCount + 1));
}
const newReport = new this.reportModel({
name: createReport.name,
type: createReport.category,
Expand All @@ -93,6 +98,8 @@
isDeleted: false,
imageUrls: imageUrls,
officerImageUrls: [],
emailFeedbackStage: 1,
automaticEmailsEnabled: createReport.automaticEmailsEnabled,
historyData: [
{
user: createReport.email,
Expand All @@ -102,6 +109,10 @@
field: 'status',
change: 'gautas',
},
{
field: 'emailFeedbackStage',
change: '1',
},
],
},
],
Expand All @@ -112,6 +123,7 @@
},
],
});

return await newReport.save();
}

Expand Down Expand Up @@ -185,19 +197,65 @@
);
}
}
await this.reportModel.updateOne(
{
refId: updateReport.refId,
},
{
$push: {
statusRecords: {
status: updateReport.status,
date: new Date(),

if(updateReport.status == 'tiriamas' && report.emailFeedbackStage < 2 && report.automaticEmailsEnabled){
await this.postmarkService.sendInInvestigationReportEmail(report.email, this.postmarkService.generateReportUrl(updateReport.refId));
await this.reportModel.updateOne(
{
refId: updateReport.refId,
},
Comment on lines +204 to +206

Check failure

Code scanning / CodeQL

Database query built from user-controlled sources High

This query object depends on a
user-provided value
.

Copilot Autofix AI 10 days ago

To fix the problem, we need to ensure that the user-provided data is properly sanitized or validated before being used in the database query. For MongoDB queries, using the $eq operator can help ensure that the user input is interpreted as a literal value and not as a query object.

In this case, we will modify the query to use the $eq operator for the refId field. This change will ensure that the refId is treated as a literal value, preventing potential NoSQL injection attacks.

Suggested changeset 1
src/repositories/reports/report.repository.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/repositories/reports/report.repository.ts b/src/repositories/reports/report.repository.ts
--- a/src/repositories/reports/report.repository.ts
+++ b/src/repositories/reports/report.repository.ts
@@ -204,3 +204,3 @@
             {
-              refId: updateReport.refId,
+              refId: { $eq: updateReport.refId },
             },
@@ -225,3 +225,3 @@
             {
-              refId: updateReport.refId,
+              refId: { $eq: updateReport.refId },
             },
@@ -245,3 +245,3 @@
             {
-              refId: updateReport.refId,
+              refId: { $eq: updateReport.refId },
             },
@@ -265,3 +265,3 @@
           {
-            refId: updateReport.refId,
+            refId: { $eq: updateReport.refId },
           },
EOF
@@ -204,3 +204,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
@@ -225,3 +225,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
@@ -245,3 +245,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
@@ -265,3 +265,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
{
$push: {
statusRecords: {
status: updateReport.status,
date: new Date(),
},
},
$set: {
emailFeedbackStage: 2,
}
},
},
);
);
historyEntry.edits.push(
new HistoryEditsDto('emailFeedbackStage', '2'),
);
}else if((updateReport.status == 'išspręsta' || updateReport.status == 'nepasitvirtino') && report.emailFeedbackStage < 3 && report.automaticEmailsEnabled){
await this.postmarkService.sendInvestigatedReportEmail(report.email, this.postmarkService.generateReportUrl(updateReport.refId));
await this.reportModel.updateOne(
{
refId: updateReport.refId,
},
Comment on lines +225 to +227

Check failure

Code scanning / CodeQL

Database query built from user-controlled sources High

This query object depends on a
user-provided value
.

Copilot Autofix AI 10 days ago

To fix the problem, we need to ensure that the user-provided data is properly sanitized before being used in MongoDB queries. The best way to achieve this is by using the $eq operator to ensure that the user input is interpreted as a literal value and not as a query object. This will prevent NoSQL injection attacks.

Suggested changeset 1
src/repositories/reports/report.repository.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/repositories/reports/report.repository.ts b/src/repositories/reports/report.repository.ts
--- a/src/repositories/reports/report.repository.ts
+++ b/src/repositories/reports/report.repository.ts
@@ -204,3 +204,3 @@
             {
-              refId: updateReport.refId,
+              refId: { $eq: updateReport.refId },
             },
@@ -225,3 +225,3 @@
             {
-              refId: updateReport.refId,
+              refId: { $eq: updateReport.refId },
             },
@@ -245,3 +245,3 @@
             {
-              refId: updateReport.refId,
+              refId: { $eq: updateReport.refId },
             },
@@ -265,3 +265,3 @@
           {
-            refId: updateReport.refId,
+            refId: { $eq: updateReport.refId },
           },
EOF
@@ -204,3 +204,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
@@ -225,3 +225,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
@@ -245,3 +245,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
@@ -265,3 +265,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
{
$push: {
statusRecords: {
status: updateReport.status,
date: new Date(),
},
},
$set: {
emailFeedbackStage: 3,
}
},
);
historyEntry.edits.push(
new HistoryEditsDto('emailFeedbackStage', '3'),
);
}else {
await this.reportModel.updateOne(
{
refId: updateReport.refId,
},
Comment on lines +245 to +247

Check failure

Code scanning / CodeQL

Database query built from user-controlled sources High

This query object depends on a
user-provided value
.

Copilot Autofix AI 10 days ago

To fix the problem, we need to ensure that the user-provided data in updateReportDto is properly sanitized before being used in the MongoDB query. The best way to achieve this is by using the $eq operator to ensure that the user input is interpreted as a literal value and not as a query object. This will prevent any potential NoSQL injection attacks.

We will modify the query object in the updateOne method to use the $eq operator for the refId field. This change will be made in the src/repositories/reports/report.repository.ts file.

Suggested changeset 1
src/repositories/reports/report.repository.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/repositories/reports/report.repository.ts b/src/repositories/reports/report.repository.ts
--- a/src/repositories/reports/report.repository.ts
+++ b/src/repositories/reports/report.repository.ts
@@ -245,3 +245,3 @@
             {
-              refId: updateReport.refId,
+              refId: { $eq: updateReport.refId },
             },
@@ -265,3 +265,3 @@
           {
-            refId: updateReport.refId,
+            refId: { $eq: updateReport.refId },
           },
EOF
@@ -245,3 +245,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
@@ -265,3 +265,3 @@
{
refId: updateReport.refId,
refId: { $eq: updateReport.refId },
},
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
{
$push: {
statusRecords: {
status: updateReport.status,
date: new Date(),
},
},
},
);
}

historyEntry.edits.push(
new HistoryEditsDto('status', updateReport.status),
);
Expand Down
6 changes: 6 additions & 0 deletions src/repositories/reports/schemas/report.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export class Report {
@Prop({ type: Array<string>, default: [] })
imageUrls: string[];

@Prop({ type: Number, required: true })
emailFeedbackStage: number;

@Prop({ type: Boolean, required: false, default: false })
automaticEmailsEnabled: boolean;

@Prop({
type: [
{
Expand Down
Loading