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,36 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { FilterQuery, Model, UpdateQuery } from 'mongoose';
import { BlameDocumentModel } from '../schema/blame-document.schema';
@Injectable()
export class BlameDocumentDbService {
constructor(
@InjectModel(BlameDocumentModel.name)
private readonly blameDocumentModel: Model<BlameDocumentModel>,
) {}
async create(createDto: Partial<BlameDocumentModel>): Promise<BlameDocumentModel> {
const newDocument = new this.blameDocumentModel(createDto);
return newDocument.save();
}
async findOne(filter: FilterQuery<BlameDocumentModel>): Promise<BlameDocumentModel | null> {
return this.blameDocumentModel.findOne(filter).lean();
}
async findAll(filter: FilterQuery<BlameDocumentModel>): Promise<BlameDocumentModel[]> {
return this.blameDocumentModel.find(filter).lean();
}
async findById(id: string): Promise<BlameDocumentModel | null> {
return this.blameDocumentModel.findById(id).lean();
}
async findByIdAndUpdate(
id: string,
updateDto: UpdateQuery<BlameDocumentModel>,
): Promise<BlameDocumentModel | null> {
return this.blameDocumentModel.findByIdAndUpdate(id, updateDto, { new: true }).lean();
}
}

View File

@@ -0,0 +1,26 @@
import { InjectModel } from "@nestjs/mongoose";
import { Model, Types } from "mongoose";
import { BlameVideosModel } from "src/request-management/entities/schema/blame-video.schema";
export class BlameVideoDbService {
constructor(
@InjectModel(BlameVideosModel.name)
private readonly blameVideoModel: Model<BlameVideosModel>,
) {}
async create(video: BlameVideosModel): Promise<BlameVideosModel> {
return await this.blameVideoModel.create(video);
}
async findOne(id: string): Promise<BlameVideosModel> {
return await this.blameVideoModel.findOne(new Types.ObjectId(id));
}
async findOneByRequestId(id: string): Promise<BlameVideosModel> {
return await this.blameVideoModel.findById(id);
}
async findById(id: string): Promise<BlameVideosModel | null> {
return this.blameVideoModel.findById(id).lean();
}
}

View File

@@ -0,0 +1,22 @@
import { InjectModel } from "@nestjs/mongoose";
import { Model, Types } from "mongoose";
import { BlameVoicesModel } from "../schema/blame-voice.schema";
export class BlameVoiceDbService {
constructor(
@InjectModel(BlameVoicesModel.name)
private readonly blameVoiceModel: Model<BlameVoicesModel>,
) {}
async create(Voice: BlameVoicesModel): Promise<BlameVoicesModel> {
return await this.blameVoiceModel.create(Voice);
}
async findOne(id: string): Promise<BlameVoicesModel> {
return await this.blameVoiceModel.findOne(new Types.ObjectId(id));
}
async findById(id: string): Promise<BlameVoicesModel | null> {
return this.blameVoiceModel.findById(id).lean();
}
}

View File

@@ -0,0 +1,94 @@
import { InjectModel } from "@nestjs/mongoose";
import { FilterQuery, Model, Types, UpdateQuery } from "mongoose";
import { RequestManagementModel } from "../schema/request-management.schema";
export class RequestManagementDbService {
constructor(
@InjectModel(RequestManagementModel.name)
private readonly requestModel: Model<RequestManagementModel>,
) {}
async create(
request: RequestManagementModel,
): Promise<RequestManagementModel> {
return await this.requestModel.create(request);
}
async findOne(id: string) {
return await this.requestModel.findOne(new Types.ObjectId(id));
}
async findAll(filter?: FilterQuery<RequestManagementModel>) {
return await this.requestModel.find({ ...filter });
}
async findAllAndPagination(query, currentPage, resPerPage?) {
const responsePerPage = resPerPage | 1;
const skipPge = responsePerPage * (Number(currentPage) - 1);
return await this.requestModel
.find(query)
.limit(responsePerPage)
.skip(skipPge);
}
async countByFilter(
filter: FilterQuery<RequestManagementModel>,
): Promise<number> {
return await this.requestModel.countDocuments(filter);
}
async findAndUpdate(
filter: FilterQuery<RequestManagementModel>,
update: UpdateQuery<RequestManagementModel>,
) {
return await this.requestModel.findByIdAndUpdate(filter, update);
}
async findOneAndUpdate(
filter: FilterQuery<RequestManagementModel>,
update: UpdateQuery<RequestManagementModel>,
options: {
new?: boolean;
upsert?: boolean;
runValidators?: boolean;
} = {},
): Promise<RequestManagementModel | null> {
try {
const updatedDoc = await this.requestModel.findOneAndUpdate(
filter,
update,
{
new: options.new ?? true, // Return updated doc by default
upsert: options.upsert ?? false, // Dont create new if not found
runValidators: options.runValidators ?? true,
},
);
return updatedDoc;
} catch (error) {
console.error(
`❌ [RequestManagementDbService] findOneAndUpdate failed`,
error,
);
throw error;
}
}
async findByIdAndUpdate(
id: string,
update: UpdateQuery<RequestManagementModel>,
) {
return await this.requestModel.findByIdAndUpdate(
{ _id: new Types.ObjectId(id) },
update,
);
}
async aggregate(filter?) {
return await this.requestModel.aggregate(filter);
}
async updateMany(filter?, update?) {
return await this.requestModel.updateMany(filter, update);
}
}

View File

@@ -0,0 +1,23 @@
import { InjectModel } from "@nestjs/mongoose";
import { Model, Types } from "mongoose";
import { UserSignModel } from "../schema/sign.schema";
export class UserSignDbService {
constructor(
@InjectModel(UserSignModel.name)
private readonly blameVoiceModel: Model<UserSignModel>,
) {}
async create(sign: UserSignModel): Promise<UserSignModel> {
return await this.blameVoiceModel.create(sign);
}
async findOne(id: string): Promise<UserSignModel> {
return await this.blameVoiceModel.findOne(new Types.ObjectId(id));
}
async findById(id: string): Promise<UserSignModel | null> {
// Use your correct Model type
return this.blameVoiceModel.findById(id).lean();
}
}

View File

@@ -0,0 +1,26 @@
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose";
export enum BlameDocumentType {
NationalCertificate = "nationalCertificate",
CarCertificate = "carCertificate",
DrivingLicence = "drivingLicense",
CarGreenCard = "carGreenCard",
}
@Schema({ collection: "blame-documents", versionKey: false, timestamps: true })
export class BlameDocumentModel {
@Prop({ required: true })
path: string;
@Prop({ required: true })
requestId: Types.ObjectId;
@Prop({ required: true })
fileName: string;
@Prop({ required: true, type: String, enum: BlameDocumentType })
documentType: BlameDocumentType;
}
export const BlameDocumentSchema =
SchemaFactory.createForClass(BlameDocumentModel);

View File

@@ -0,0 +1,15 @@
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose";
@Schema({ collection: "blame-videos", versionKey: false })
export class BlameVideosModel {
@Prop()
path: string;
@Prop()
requestId: Types.ObjectId;
@Prop()
fileName: string;
}
export const BlameVideoSchema = SchemaFactory.createForClass(BlameVideosModel);

View File

@@ -0,0 +1,24 @@
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose";
export enum UploadContext {
INITIAL = 'INITIAL_UPLOAD',
EXPERT_RESEND = 'EXPERT_RESEND',
}
@Schema({ collection: "blame-voice", versionKey: false })
export class BlameVoicesModel {
@Prop()
path: string;
@Prop()
requestId: Types.ObjectId;
@Prop()
fileName: string;
@Prop({ type: String, enum: UploadContext, default: UploadContext.INITIAL })
context?: UploadContext;
}
export const BlameVoiceSchema = SchemaFactory.createForClass(BlameVoicesModel);

View File

@@ -0,0 +1,465 @@
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose";
import {
ResendFirstPartyDto,
ResendSecondPartyDto,
} from "src/expert-blame/dto/reply.dto";
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum";
import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
export class InitialForm {
@Prop({ required: false, default: false })
expertOpinion?: boolean;
@Prop({ required: false, default: false })
imDamaged?: boolean;
@Prop({ required: false, default: false })
imGuilty?: boolean;
}
export class FirstPartyFile {
@Prop()
descriptions?: string[];
@Prop()
voices?: string[];
@Prop()
firstPartyVideoId?: string;
// CAR_BODY specific accident details
@Prop({ type: Date })
accidentDate?: Date;
@Prop()
accidentTime?: string;
@Prop()
weatherCondition?: string;
@Prop()
roadCondition?: string;
@Prop()
lightCondition?: string;
}
export class CarDetail {
carName?: string;
carModel?: string;
carType?: string;
isNewCar?: boolean;
insurerBirthday?: string;
driverBirthday?: string | null;
}
export class InsuranceDetail {
insuranceId?: string;
insuranceCompany?: string;
financialCommitmentCeiling?: string;
startDate?: string;
endDate?: string;
}
// CAR_BODY specific insurance details for the damaged party
export class CarBodyInsuranceDetail {
@Prop()
policyNumber?: string;
@Prop()
startDate?: string;
@Prop()
endDate?: string;
@Prop()
insurerCompany?: string;
@Prop({ type: [String] })
coverages?: string[];
}
export class SecondPartyFile {
@Prop()
descriptions?: string[];
@Prop()
voices?: string[];
@Prop()
secondPartyVideoId?: string;
}
export class FirstPartyClient {
clientName?: string;
clientId?: Types.ObjectId;
}
export class SecondPartyClient {
clientName?: string;
clientId?: Types.ObjectId;
}
export class FirstPartyDetail {
@Prop({ type: String })
firstPartyId?: Types.ObjectId;
@Prop({ type: String })
firstPartyPhoneNumber?: string;
@Prop({ type: String })
firstPartyClientId?: string;
@Prop({
type: AddPlateDto,
description: "first add plate and send plate id to this field",
})
firstPartyPlate?: AddPlateDto;
@Prop({ type: CarDetail })
firstPartyCarDetail?: CarDetail;
@Prop({ type: InsuranceDetail })
firstPartyInsuranceDetail?: InsuranceDetail;
// Only used for CAR_BODY type requests car body insurance info
@Prop({ type: CarBodyInsuranceDetail })
firstPartyCarBodyInsuranceDetail?: CarBodyInsuranceDetail;
@Prop({ type: FirstPartyFile })
firstPartyClient?: FirstPartyClient;
@Prop({ type: FirstPartyFile })
firstPartyFile?: FirstPartyFile;
@Prop({ type: FirstPartyFile })
firstPartyInitialForm?: InitialForm;
@Prop({ type: Boolean })
firstPartyCertificate: boolean | null;
}
export class SecondPartyDetail {
@Prop({ type: String })
secondPartyId?: Types.ObjectId;
@Prop({ type: String })
secondPartyPhoneNumber?: string;
@Prop({ type: SecondPartyClient })
secondPartyClient?: SecondPartyClient;
@Prop({
type: AddPlateDto,
description: "secondParty add plate and send plate id to this field",
})
secondPartyPlate?: AddPlateDto;
@Prop()
secondPartyCarDetail?: CarDetail;
@Prop({ type: InsuranceDetail })
secondPartyInsuranceDetail?: InsuranceDetail;
@Prop()
secondPartyPlateId?: string;
@Prop({ type: InitialForm })
secondPartyInitialForm: InitialForm;
@Prop({ type: SecondPartyFile })
secondPartyFiles?: SecondPartyFile;
}
export class LocationDto {
@Prop({ type: Number, required: true })
lat: number;
@Prop({ type: Number, required: true })
lon: number;
}
export class AccidentWayIF {
@Prop({ required: true })
id: string;
@Prop({ required: true })
label: string;
}
export class AccidentReasonIF {
@Prop({ required: true })
id: string;
@Prop({ required: true })
label: string;
@Prop({ required: true })
fanavaran: number;
}
export class FieldsInterface {
@Prop({ required: true, type: AccidentWayIF })
accidentWay: AccidentWayIF;
@Prop({ required: true, type: AccidentReasonIF })
accidentReason: AccidentReasonIF;
@Prop({ required: true, type: AccidentWayIF })
accidentType: AccidentWayIF;
}
export class SignDetail {
@Prop()
fileId: string;
@Prop()
fileName: string;
@Prop()
fileUrl: string;
}
export class PartiesComment {
@Prop({ required: true, type: Boolean })
isAccept?: boolean;
@Prop({ required: true })
signDetail?: SignDetail;
}
export class SubmitReply {
@Prop({ required: true })
description: string;
@Prop({ required: true, type: String })
guiltyUserId: Types.ObjectId;
@Prop({ required: true })
fields: FieldsInterface;
@Prop({ required: true, default: () => new Date() })
submitTime: Date;
@Prop({ type: PartiesComment })
firstPartyComment?: PartiesComment;
@Prop({ type: PartiesComment })
secondPartyComment?: PartiesComment;
@Prop()
systemResponse: string;
}
export class ExpertResendParties {
userId?: Types.ObjectId;
description?: string;
}
export class ExpertResendReply {
// TODO change DTOs to dynamic Exmple SubmitReply Parties comment
@Prop()
firstParty?: ResendFirstPartyDto;
@Prop()
secondParty?: ResendSecondPartyDto;
}
export class ActorLockDetails {
@Prop({ type: String })
fullName?: string;
@Prop({ type: Types.ObjectId })
actorId?: Types.ObjectId;
}
export enum CreationMethod {
NORMAL = "NORMAL",
LINK = "LINK",
IN_PERSON = "IN_PERSON",
}
export enum FilledBy {
CUSTOMER = "customer",
EXPERT = "expert",
}
export class ExpertLinkInfo {
@Prop({ type: String, required: true })
token: string;
@Prop({ type: Date, required: true })
expiresAt: Date;
@Prop({ type: Boolean, default: false })
isUsed: boolean;
@Prop({ type: Boolean, default: false })
isExpired: boolean;
@Prop({ type: Date })
generatedAt: Date;
@Prop({ type: String })
sentTo?: string; // Phone number or email
@Prop({ type: Number, default: 0 })
openCount: number;
@Prop({ type: Date })
lastOpenedAt?: Date;
@Prop({ type: Date })
usedAt?: Date;
}
export class FileHistoryEvent {
@Prop({ type: String, required: true })
eventType: string; // "LINK_SENT", "LINK_OPENED", "FILLED_BY_CUSTOMER", "FILLED_BY_EXPERT", "ASSIGNED_TO_EXPERT", "EVALUATION_STARTED", "EVALUATION_COMPLETED", "USER_ACCEPTED", "USER_REJECTED", "SENT_TO_FANAVARAN", "CODE_RECEIVED"
@Prop({ type: Types.ObjectId })
actorId?: Types.ObjectId;
@Prop({ type: String })
actorName?: string;
@Prop({ type: String })
actorType?: string; // "expert", "damage_expert", "customer"
@Prop({ type: Date, default: () => new Date() })
timestamp: Date;
@Prop({ type: Object })
metadata?: any; // Additional event-specific data
}
@Schema({ _id: false })
export class FileRating {
@Prop({ type: Number, min: 0, max: 5 })
collisionMethodAccuracy: number;
@Prop({ type: Number, min: 0, max: 5 })
evaluationTimeliness: number;
@Prop({ type: Number, min: 0, max: 5 })
accidentCauseAccuracy: number;
@Prop({ type: Number, min: 0, max: 5 })
guiltyVehicleIdentification: number;
}
// TODO clean all sub Object to other files
export type RequestManagementDocs = RequestManagementModel & Document;
@Schema({
collection: "requests-management",
versionKey: false,
timestamps: true,
autoIndex: true,
_id: true,
})
export class RequestManagementModel {
@Prop({ type: String, required: false })
type: string;
@Prop({ type: String, required: true, index: { unique: true } })
requestNumber: string;
@Prop()
blameStatus: ReqBlameStatus | StepsEnum;
@Prop()
steps?: StepsEnum[];
@Prop()
userComment?: string;
@Prop()
currentStep: StepsEnum;
@Prop()
nextStep?: StepsEnum;
@Prop({ description: "first step" })
initialForm?: InitialForm;
@Prop({ required: false })
firstPartyDetails?: FirstPartyDetail;
@Prop({ required: false, type: Types.ObjectId })
sanHubId?: Types.ObjectId;
@Prop({ required: false })
secondPartyDetails?: SecondPartyDetail;
@Prop({ required: false, description: "third step call" })
firstPartyLocation?: LocationDto;
@Prop({ required: false })
secondPartyLocation?: LocationDto;
@Prop({ type: Date, default: Date.now })
createdAt?: Date | string;
@Prop({ type: Date, default: Date.now })
updatedAt?: Date;
@Prop()
lockFile?: boolean;
@Prop({ type: Date })
lockTime?: Date | string | number;
@Prop({ type: Date })
unlockTime?: Date | number;
@Prop()
actorLocked?: ActorLockDetails;
@Prop()
actorsChecker?: [];
@Prop({ required: false })
expertSubmitReply?: SubmitReply | null;
@Prop()
expertResendReply?: ExpertResendReply;
@Prop()
expertSubmitReplyFinal?: ExpertResendReply;
@Prop({ type: FileRating, required: false })
rating?: FileRating;
@Prop({ type: Date, required: false })
autoCloseTriggerTime?: Date;
@Prop({ type: Boolean, default: false })
isHandledStatsUpdated?: boolean;
// Expert-initiated file tracking
@Prop({ type: Boolean, default: false })
expertInitiated?: boolean;
@Prop({ type: Types.ObjectId })
initiatedBy?: Types.ObjectId; // Expert who started the process
@Prop({ type: String, enum: FilledBy })
filledBy?: FilledBy; // Who filled the information
@Prop({ type: String, enum: CreationMethod, default: CreationMethod.NORMAL })
creationMethod?: CreationMethod; // How the file was created
@Prop({ type: ExpertLinkInfo })
expertLink?: ExpertLinkInfo; // Link information if created via link
@Prop({ type: [FileHistoryEvent], default: [] })
history?: FileHistoryEvent[]; // Timeline of events
@Prop({ type: Types.ObjectId })
assignedExpertId?: Types.ObjectId; // Expert assigned for evaluation (should be same as initiatedBy for expert-initiated files)
}
export const RequestManagementSchema = SchemaFactory.createForClass(
RequestManagementModel,
);

View File

@@ -0,0 +1,19 @@
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose";
@Schema({ collection: "blame-sign", versionKey: false })
export class UserSignModel {
@Prop()
path: string;
@Prop()
userId: string;
@Prop()
requestId: Types.ObjectId;
@Prop()
fileName: string;
}
export const UserSignSchema = SchemaFactory.createForClass(UserSignModel);