Initial commit after migration to gitea

This commit is contained in:
2026-01-18 11:27:43 +03:30
parent a21039410c
commit ea4b8eb543
196 changed files with 45567 additions and 9 deletions

View File

@@ -0,0 +1,10 @@
import { forwardRef, Module } from "@nestjs/common";
import { RequestManagementModule } from "src/request-management/request-management.module";
import { AutoCloseRequestService } from "./cron.service";
@Module({
imports: [forwardRef(() => RequestManagementModule)],
providers: [AutoCloseRequestService],
exports: [AutoCloseRequestService],
})
export class CronModule {}

View File

@@ -0,0 +1,95 @@
import { Injectable, Logger } from "@nestjs/common";
import { Cron, CronExpression } from "@nestjs/schedule";
import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service";
import { SubmitReply } from "src/request-management/entities/schema/request-management.schema";
import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum";
@Injectable()
export class AutoCloseRequestService {
private readonly logger = new Logger(AutoCloseRequestService.name);
constructor(private readonly requestDb: RequestManagementDbService) {}
async scheduleAutoClose(params: { requestId: string; runAt: Date }) {
await this.requestDb.findAndUpdate(
{ _id: params.requestId },
{ $set: { autoCloseTriggerTime: params.runAt } },
);
this.logger.log(
`Scheduled auto-close for ${params.requestId} at ${params.runAt.toISOString()}`,
);
}
// Runs every hour
@Cron(CronExpression.EVERY_HOUR)
async handleAutoClose() {
const now = new Date();
const threshold = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const requestsToClose = await this.requestDb.findAll({
blameStatus: { $ne: ReqBlameStatus.CloseRequest },
$or: [
{ "expertSubmitReply.firstPartyComment.isAccept": true },
{ "expertSubmitReply.secondPartyComment.isAccept": true },
{ "expertSubmitReplyFinal.firstPartyComment.isAccept": true },
{ "expertSubmitReplyFinal.secondPartyComment.isAccept": true },
],
autoCloseTriggerTime: { $lte: threshold },
});
for (const request of requestsToClose) {
const reply = request.expertSubmitReplyFinal || request.expertSubmitReply;
const isSubmitReply = (reply: any): reply is SubmitReply =>
reply &&
typeof reply === "object" &&
("firstPartyComment" in reply || "secondPartyComment" in reply);
if (isSubmitReply(reply)) {
const firstAccepted = reply.firstPartyComment?.isAccept;
const secondAccepted = reply.secondPartyComment?.isAccept;
if (!firstAccepted || !secondAccepted) {
await this.requestDb.findByIdAndUpdate(request._id.toString(), {
blameStatus: ReqBlameStatus.CloseRequest,
});
this.logger.log(`Auto-closed request ${request._id}`);
}
}
}
}
@Cron(CronExpression.EVERY_DAY_AT_2AM)
async handleBlameFileCleanup() {
this.logger.log("Running scheduled blame file cleanup job...");
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const staleStatuses = [
ReqBlameStatus.PendingForFirstParty,
ReqBlameStatus.PendingForSecondParty,
];
const query = {
createdAt: { $lt: sevenDaysAgo },
blameStatus: { $in: staleStatuses },
};
try {
const result = await this.requestDb.updateMany(query, {
$set: { blameStatus: ReqBlameStatus.CloseRequest },
});
if (result.modifiedCount > 0) {
this.logger.log(
`Successfully closed ${result.modifiedCount} stale blame files.`,
);
} else {
this.logger.log("No stale blame files found to close.");
}
} catch (error) {
this.logger.error("Error during scheduled blame file cleanup:", error);
}
}
}

View File

@@ -0,0 +1,9 @@
import { Module } from "@nestjs/common";
import { HashService } from "./hash.service";
@Module({
imports: [],
providers: [HashService],
exports: [HashService],
})
export class HashModule {}

View File

@@ -0,0 +1,14 @@
import { Injectable } from "@nestjs/common";
import * as bcrypt from "bcrypt";
@Injectable()
export class HashService {
async hash(password: string): Promise<string> {
const salt = await bcrypt.genSalt(10);
return bcrypt.hash(password, salt);
}
async compare(password: string, hash: string) {
return bcrypt.compare(password, hash);
}
}

View File

@@ -0,0 +1,26 @@
import { Module } from "@nestjs/common";
import { MailerModule } from "@nestjs-modules/mailer";
import { MailService } from "./mail.service";
@Module({
providers: [MailService],
exports: [MailService],
imports: [
MailerModule.forRoot({
transport: {
service: "gmail",
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
user: "balali.arash@gmail.com",
pass: "macujakosnsqbgdm",
},
},
defaults: {
from: "KSG <modules@nestjs.com>",
},
}),
],
})
export class MailModule {}

View File

@@ -0,0 +1,41 @@
import { Injectable } from "@nestjs/common";
import { MailerService } from "@nestjs-modules/mailer";
@Injectable()
export class MailService {
constructor(private readonly mailerService: MailerService) {}
async sendMail(email, otp: string): Promise<any> {
let status: object;
await this.mailerService
.sendMail({
to: email,
from: "noreply@yara724.com",
subject: "YARA724 Company",
text: "welcome",
html: `
<h1 style="text-align:center">yara724 verificateion </h1>
<h3 style="background:#FFA500;width:150px; margin:0 auto; padding:10px; font-size:25px; border-radius:15px; text-align:center;">
<div style="color:white">
${otp}
</div>
</h3>
`,
})
.then((success) => {
if (success) {
status = {
message: "email was sent successfully",
code: 200,
emailSent: true,
};
}
})
.catch((err) => {
if (err) {
status = { message: "please try again", code: 500, emailSent: false };
}
});
return status;
}
}

View File

@@ -0,0 +1,8 @@
import { Module } from "@nestjs/common";
import { OtpService } from "./otp.service";
@Module({
providers: [OtpService],
exports: [OtpService],
})
export class OtpModule {}

View File

@@ -0,0 +1,16 @@
import { Injectable } from "@nestjs/common";
// import crypto from "crypto"
const crypto = require("node:crypto");
@Injectable()
export class OtpService {
createDigit(digits: number): string {
const max = Math.pow(10, digits);
const randomBytes = crypto.randomBytes(Math.ceil(digits / 2));
const randomNumber = parseInt(randomBytes.toString("hex"), 16) % max;
return randomNumber.toString().padStart(digits, "0");
}
create() {
return this.createDigit(5);
}
}

View File

@@ -0,0 +1,29 @@
import {
PipeTransform,
Injectable,
BadRequestException,
ArgumentMetadata,
} from "@nestjs/common";
@Injectable()
export class ParseJsonPipe implements PipeTransform {
transform(value: string | undefined, metadata: ArgumentMetadata): any {
if (value === undefined) {
return undefined;
}
if (typeof value !== "string") {
throw new BadRequestException(
`Validation failed: Expected a stringified JSON for query parameter '${metadata.data}'.`,
);
}
try {
return JSON.parse(value);
} catch (e) {
throw new BadRequestException(
`Invalid JSON format in query parameter '${metadata.data}'.`,
);
}
}
}

View File

@@ -0,0 +1,15 @@
import { KavenegarModule } from "@fraybabak/kavenegar_nest";
import { Module } from "@nestjs/common";
import { SmsManagerService } from "./sms-manager.service";
@Module({
imports: [
KavenegarModule.forRoot({
apikey:
"75776C717969412B4B52306A5956462F4A714E6F6C65544D6A2B654B7566786E",
}),
],
providers: [SmsManagerService],
exports: [SmsManagerService],
})
export class SmsManagerModule {}

View File

@@ -0,0 +1,30 @@
import { KavenegarService } from "@fraybabak/kavenegar_nest";
import { Injectable } from "@nestjs/common";
export interface SendMessage {
message: string;
// sender: string;
receptor: string;
}
export interface VerifyLookUpMessage {
template: string;
token: string;
receptor: string;
}
@Injectable()
export class SmsManagerService {
constructor(private readonly sender: KavenegarService) {}
async sendMessage(data: SendMessage) {
return await this.sender.Send({
sender: "10008663",
...data,
});
}
async verifyLookUp(data: VerifyLookUpMessage) {
return await this.sender.verifyLookup(data);
}
}