forked from Chatbot/v3-api
first commit v3 initialiazed a temporary repository for darmanet client
This commit is contained in:
57
src/database/model/admin.model.ts
Normal file
57
src/database/model/admin.model.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { RateModel } from './rate.model';
|
||||
import { UsersBaseModel } from './users-base.model';
|
||||
|
||||
export type AdminDocument = AdminModel & Document;
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'admin',
|
||||
})
|
||||
export class AdminModel extends UsersBaseModel {
|
||||
@Prop({ type: Types.ObjectId, ref: 'FileUploadModel', required: false })
|
||||
avatar?: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
password: string;
|
||||
|
||||
@Prop({ default: [] })
|
||||
rate: RateModel[];
|
||||
|
||||
@Prop({ default: null })
|
||||
resetToken: string | null;
|
||||
|
||||
@Prop({ default: true })
|
||||
isActive: Boolean;
|
||||
|
||||
@Prop({ default: false })
|
||||
onlineStatus: Boolean;
|
||||
|
||||
@Prop({ default: false })
|
||||
available: Boolean;
|
||||
|
||||
@Prop({ default: 0 })
|
||||
activeSessions: number;
|
||||
|
||||
@Prop({ default: 5 })
|
||||
maxSessions: number;
|
||||
|
||||
/** Extra permissions beyond the role template (ADR 0001). */
|
||||
@Prop({ type: [String], default: [] })
|
||||
permissionGrants: string[];
|
||||
|
||||
/** Blocked permissions from the role template (ADR 0001). */
|
||||
@Prop({ type: [String], default: [] })
|
||||
permissionDenies: string[];
|
||||
}
|
||||
|
||||
export const AdminSchema = SchemaFactory.createForClass(AdminModel);
|
||||
AdminSchema.clearIndexes();
|
||||
AdminSchema.pre('save', function (next) {
|
||||
this.updatedAt = new Date();
|
||||
// this.password = crypto.createHash('sha256').update(this.password).digest('hex'),
|
||||
|
||||
next();
|
||||
});
|
||||
74
src/database/model/audit-log.model.ts
Normal file
74
src/database/model/audit-log.model.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: false },
|
||||
versionKey: false,
|
||||
collection: 'audit_logs',
|
||||
})
|
||||
export class AuditLogModel extends Document {
|
||||
@Prop({ type: Types.ObjectId, ref: 'UserModel', required: false })
|
||||
userId?: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
action: string; // e.g., 'joinRoom', 'sendMessage', 'expertOnline', etc.
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
resource?: string; // e.g., 'Chat', 'Session', 'Expert', 'Message', etc.
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
resourceId?: string; // e.g., sessionId, messageId, expertId, etc.
|
||||
|
||||
@Prop({ type: Object, required: false })
|
||||
oldValues?: Record<string, any>; // Previous state before change
|
||||
|
||||
@Prop({ type: Object, required: false })
|
||||
newValues?: Record<string, any>; // New state after change
|
||||
|
||||
@Prop({ type: Object, required: false })
|
||||
changes?: Record<string, { from: any; to: any }>; // Detailed changes
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
ipAddress?: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
userAgent?: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
method?: string; // For WebSocket: event name, for HTTP: GET, POST, etc.
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
endpoint?: string; // For WebSocket: roomId or path, for HTTP: route path
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
statusCode?: number; // HTTP status code or success indicator
|
||||
|
||||
@Prop({ type: Object, required: false })
|
||||
metadata?: Record<string, any>; // Additional context data
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export const AuditLogSchema = SchemaFactory.createForClass(AuditLogModel);
|
||||
|
||||
// Create index for common queries
|
||||
AuditLogSchema.index({ userId: 1, createdAt: -1 });
|
||||
AuditLogSchema.index({ action: 1, createdAt: -1 });
|
||||
AuditLogSchema.index({ resource: 1, resourceId: 1 });
|
||||
AuditLogSchema.index({ createdAt: -1 });
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
42
src/database/model/botReact.model.ts
Normal file
42
src/database/model/botReact.model.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
import { ReactEnum } from 'src/common/types/react.type';
|
||||
import { Sender } from 'src/common/types/sender.type';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt' },
|
||||
collection: 'reacts',
|
||||
versionKey: false,
|
||||
_id: true,
|
||||
})
|
||||
|
||||
export class ReactsModel {
|
||||
@Prop({ _id: true, default: new Types.ObjectId() })
|
||||
sessionId: Types.ObjectId;
|
||||
|
||||
@Prop({ _id: true, default: new Types.ObjectId() })
|
||||
messageId: Types.ObjectId;
|
||||
|
||||
@Prop({ _id: true, default: new Types.ObjectId() })
|
||||
userId: Types.ObjectId;
|
||||
|
||||
@Prop({ default: false })
|
||||
question: string;
|
||||
|
||||
@Prop({ default: false })
|
||||
answer: string;
|
||||
|
||||
@Prop({ default: null, type: String, enum: Sender })
|
||||
sender: Sender;
|
||||
|
||||
@Prop({ default: null, enum: ReactEnum })
|
||||
react: string;
|
||||
|
||||
@Prop({ type: [String], required: true })
|
||||
createdAt: [string, string]; // [time, date]
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdISO: Date;
|
||||
}
|
||||
|
||||
export const ReactSchema = SchemaFactory.createForClass(ReactsModel);
|
||||
69
src/database/model/business-hours.model.ts
Normal file
69
src/database/model/business-hours.model.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document } from 'mongoose';
|
||||
|
||||
export interface TimeInterval {
|
||||
start: string; // HH:mm format
|
||||
end: string; // HH:mm format
|
||||
}
|
||||
|
||||
export interface WeeklyWindow {
|
||||
day: 'saturday' | 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday';
|
||||
intervals: TimeInterval[];
|
||||
}
|
||||
|
||||
export interface Exception {
|
||||
date: string; // YYYY-MM-DD format
|
||||
intervals: TimeInterval[]; // Empty array means closed
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface BusinessHoursConfig {
|
||||
timezone: string; // IANA timezone (e.g., 'Asia/Tehran')
|
||||
globalToggle: boolean; // Master switch
|
||||
weeklyWindows: WeeklyWindow[];
|
||||
exceptions: Exception[];
|
||||
policy?: {
|
||||
onClose: 'allow_existing_until_end' | 'hard_close';
|
||||
};
|
||||
messageTemplate?: {
|
||||
key: string;
|
||||
default: string;
|
||||
};
|
||||
}
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'business_hours',
|
||||
_id: true,
|
||||
})
|
||||
export class BusinessHoursModel extends Document {
|
||||
@Prop({ type: Object, required: true })
|
||||
config: BusinessHoursConfig;
|
||||
|
||||
@Prop({ type: [String], default: [] })
|
||||
createdBy: string[]; // Array of admin usernames
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
updatedBy?: string; // Last admin username who updated
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
|
||||
@Prop({ type: Boolean, default: true })
|
||||
isActive: boolean; // Only one should be active at a time
|
||||
}
|
||||
|
||||
export const BusinessHoursSchema = SchemaFactory.createForClass(BusinessHoursModel);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
29
src/database/model/categories.model.ts
Normal file
29
src/database/model/categories.model.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'categories',
|
||||
_id: true,
|
||||
})
|
||||
export class CategoriesModel {
|
||||
@Prop({ type: String })
|
||||
title: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
enTitle: string;
|
||||
|
||||
@Prop({ type: Number })
|
||||
priority: number;
|
||||
|
||||
@Prop({ type: String })
|
||||
icon: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const CategoriesSchema = SchemaFactory.createForClass(CategoriesModel);
|
||||
52
src/database/model/chat-message-attachment.model.ts
Normal file
52
src/database/model/chat-message-attachment.model.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import type { ChatAttachmentFileType } from 'src/storage/storage.types';
|
||||
|
||||
@Schema({
|
||||
collection: 'chat_message_attachments',
|
||||
timestamps: { createdAt: 'uploadedAt', updatedAt: false },
|
||||
versionKey: false,
|
||||
})
|
||||
export class ChatMessageAttachmentModel extends Document {
|
||||
@Prop({ type: Types.ObjectId, required: true })
|
||||
messageId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Types.ObjectId, required: true })
|
||||
sessionId: Types.ObjectId;
|
||||
|
||||
@Prop({ required: true })
|
||||
uploaderId: string;
|
||||
|
||||
@Prop({ required: true, enum: ['User', 'Expert'] })
|
||||
uploaderRole: 'User' | 'Expert';
|
||||
|
||||
@Prop({ required: true, enum: ['voice', 'image', 'document'] })
|
||||
fileType: ChatAttachmentFileType;
|
||||
|
||||
@Prop({ required: true })
|
||||
storageKey: string;
|
||||
|
||||
@Prop({ required: true, default: 'private' })
|
||||
bucket: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
originalFilename: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
mimeType: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
size: number;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
durationSec?: number;
|
||||
|
||||
@Prop({ required: true, default: 'ready', enum: ['ready'] })
|
||||
status: 'ready';
|
||||
}
|
||||
|
||||
export const ChatMessageAttachmentSchema = SchemaFactory.createForClass(
|
||||
ChatMessageAttachmentModel,
|
||||
);
|
||||
|
||||
ChatMessageAttachmentSchema.index({ sessionId: 1, messageId: 1 }, { unique: true });
|
||||
39
src/database/model/chat.model.ts
Normal file
39
src/database/model/chat.model.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Prop, Schema } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
import { MessagesModel } from './messages.model';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
})
|
||||
export class ChatModel {
|
||||
@Prop({ _id: true, default: new Types.ObjectId() })
|
||||
sessionId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String })
|
||||
chatTitle: string;
|
||||
|
||||
@Prop({ default: false })
|
||||
chatClosed: boolean;
|
||||
|
||||
@Prop({ default: false })
|
||||
connectedToExpert: boolean;
|
||||
|
||||
@Prop({ default: false })
|
||||
onlineChatClosed: boolean;
|
||||
|
||||
@Prop({ default: null })
|
||||
expertRate: number | null;
|
||||
|
||||
@Prop({ type: String })
|
||||
expert: string;
|
||||
|
||||
@Prop({ type: [MessagesModel], default: [] })
|
||||
messages: MessagesModel[];
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
31
src/database/model/client.model.ts
Normal file
31
src/database/model/client.model.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, HydratedDocument } from 'mongoose';
|
||||
|
||||
export interface ClientInterface {
|
||||
name_fa: string;
|
||||
|
||||
name_en: string;
|
||||
|
||||
apiKey: string;
|
||||
|
||||
status: string;
|
||||
}
|
||||
|
||||
export type ClientDocument = HydratedDocument<ClientModel>;
|
||||
|
||||
@Schema({ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } })
|
||||
export class ClientModel extends Document {
|
||||
@Prop({ required: true })
|
||||
name: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
enName: string;
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
apiKey: string;
|
||||
|
||||
@Prop({ enum: ['active', 'inactive'], default: 'active' })
|
||||
status: string;
|
||||
}
|
||||
|
||||
export const ClientSchema = SchemaFactory.createForClass(ClientModel);
|
||||
77
src/database/model/dictionaries.model.ts
Normal file
77
src/database/model/dictionaries.model.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { QuestionsModel } from './questions.model';
|
||||
|
||||
export interface UpdatedBy {
|
||||
name: string; // User's mobile number
|
||||
updatedAt: {
|
||||
time: string; // Time of update
|
||||
date: string; // Date of update
|
||||
};
|
||||
}
|
||||
|
||||
@Schema({
|
||||
timestamps: false,
|
||||
versionKey: false,
|
||||
collection: 'dictionaries',
|
||||
_id: true,
|
||||
})
|
||||
export class DictionariesModel {
|
||||
@Prop({ type: String })
|
||||
title: string;
|
||||
|
||||
@Prop({ type: 'string' })
|
||||
category: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
type: 'Regulation' | 'Method';
|
||||
|
||||
@Prop()
|
||||
questions: QuestionsModel[];
|
||||
|
||||
@Prop({ type: Boolean })
|
||||
isActive: boolean;
|
||||
|
||||
@Prop({ type: [String] })
|
||||
fileId: string[];
|
||||
|
||||
@Prop({ type: String })
|
||||
icon: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
aiCollectionId: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
aiCollectionName: string;
|
||||
|
||||
/** Cached from AI get-collections-with-descriptions / get-collection-description. */
|
||||
@Prop({ type: String, required: false })
|
||||
description?: string;
|
||||
|
||||
@Prop({ type: [String], default: [] })
|
||||
keywords?: string[];
|
||||
|
||||
/**
|
||||
* Sync bookkeeping vs AI SoT.
|
||||
* ok | missing | deactivated | stale
|
||||
*/
|
||||
@Prop({ type: String, required: false, default: 'ok' })
|
||||
aiSyncStatus?: string;
|
||||
|
||||
@Prop({ type: Date, required: false })
|
||||
lastSyncedAt?: Date;
|
||||
|
||||
@Prop({ type: Array })
|
||||
createdAt: [];
|
||||
|
||||
@Prop({ type: Date })
|
||||
createdISO: Date;
|
||||
|
||||
@Prop({ type: Date })
|
||||
updatedAt: Date;
|
||||
|
||||
@Prop({ type: [Object] })
|
||||
updatedBy: UpdatedBy[];
|
||||
}
|
||||
|
||||
export const DictionariesSchema =
|
||||
SchemaFactory.createForClass(DictionariesModel);
|
||||
34
src/database/model/dictionary-file-asset.model.ts
Normal file
34
src/database/model/dictionary-file-asset.model.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
|
||||
@Schema({
|
||||
collection: 'dictionary_file_assets',
|
||||
timestamps: { createdAt: 'uploadedAt', updatedAt: false },
|
||||
versionKey: false,
|
||||
})
|
||||
export class DictionaryFileAssetModel extends Document {
|
||||
@Prop({ required: true })
|
||||
category: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'AdminModel', required: true })
|
||||
uploadedBy: Types.ObjectId;
|
||||
|
||||
@Prop({ required: true })
|
||||
originalFilename: string;
|
||||
|
||||
/** Key inside the private bucket (no bucket name). */
|
||||
@Prop({ required: true, unique: true })
|
||||
storageKey: string;
|
||||
|
||||
@Prop({ required: true, default: 'private' })
|
||||
bucket: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
mimeType: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
size: number;
|
||||
}
|
||||
|
||||
export const DictionaryFileAssetSchema =
|
||||
SchemaFactory.createForClass(DictionaryFileAssetModel);
|
||||
54
src/database/model/expert-prepared-message.model.ts
Normal file
54
src/database/model/expert-prepared-message.model.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document } from 'mongoose';
|
||||
import {
|
||||
ExpertPreparedMessageCategory,
|
||||
EXPERT_PREPARED_MESSAGE_CATEGORIES,
|
||||
} from 'src/common/types/expert-prepared-message-category.type';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'expert_prepared_messages',
|
||||
_id: true,
|
||||
})
|
||||
export class ExpertPreparedMessageModel extends Document {
|
||||
@Prop({ type: String, required: true, trim: true })
|
||||
message: string;
|
||||
|
||||
@Prop({
|
||||
type: String,
|
||||
required: true,
|
||||
enum: EXPERT_PREPARED_MESSAGE_CATEGORIES,
|
||||
})
|
||||
category: ExpertPreparedMessageCategory;
|
||||
|
||||
@Prop({ type: String, trim: true, default: '' })
|
||||
label: string;
|
||||
|
||||
@Prop({ type: Boolean, default: true })
|
||||
isEnabled: boolean;
|
||||
|
||||
@Prop({ type: Number, default: 0 })
|
||||
sortOrder: number;
|
||||
|
||||
@Prop({ type: String, default: 'fa', trim: true })
|
||||
locale: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
createdBy?: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
updatedBy?: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const ExpertPreparedMessageSchema = SchemaFactory.createForClass(
|
||||
ExpertPreparedMessageModel,
|
||||
);
|
||||
|
||||
ExpertPreparedMessageSchema.index({ isEnabled: 1, category: 1, sortOrder: 1 });
|
||||
42
src/database/model/externalTokens.model.ts
Normal file
42
src/database/model/externalTokens.model.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'externalTokens',
|
||||
_id: true,
|
||||
})
|
||||
export class ExternalTokensModel {
|
||||
@Prop({ type: String })
|
||||
token: string;
|
||||
|
||||
@Prop({ type: 'string' })
|
||||
url: string;
|
||||
|
||||
@Prop({ type: Boolean })
|
||||
isActive: boolean;
|
||||
|
||||
@Prop({ type: String })
|
||||
method: 'access' | 'refresh';
|
||||
|
||||
@Prop({ type: String })
|
||||
tokenType: 'Bearer' | 'Basic';
|
||||
|
||||
@Prop({ type: String })
|
||||
expiresIn: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
scope: string;
|
||||
|
||||
@Prop({ type: Number })
|
||||
timestamps: Number;
|
||||
|
||||
@Prop({ type: Array })
|
||||
createdAt: [];
|
||||
|
||||
@Prop({ type: Array })
|
||||
updatedAt: [];
|
||||
}
|
||||
|
||||
export const ExternalTokenSchema =
|
||||
SchemaFactory.createForClass(ExternalTokensModel);
|
||||
34
src/database/model/fileUpload.model.ts
Normal file
34
src/database/model/fileUpload.model.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document } from 'mongoose';
|
||||
|
||||
export type FileUploadDocument = FileUploadModel & Document;
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'file',
|
||||
_id: true,
|
||||
})
|
||||
export class FileUploadModel {
|
||||
@Prop({ required: true })
|
||||
originalName: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
filename: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
filePath: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
mimetype: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
size: number;
|
||||
|
||||
@Prop({ required: true })
|
||||
type: string; // e.g., 'image', 'document'
|
||||
|
||||
@Prop({ required: true })
|
||||
category: string; // e.g., 'profile', 'invoice'
|
||||
}
|
||||
|
||||
export const FileUploadSchema = SchemaFactory.createForClass(FileUploadModel);
|
||||
91
src/database/model/installment.model.ts
Normal file
91
src/database/model/installment.model.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'installments',
|
||||
_id: true,
|
||||
})
|
||||
export class InstallmentModel {
|
||||
@Prop({ type: Number, required: true, index: true })
|
||||
policyId: number;
|
||||
|
||||
@Prop({ type: Types.ObjectId, required: true, index: true })
|
||||
userId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String, required: true, unique: true })
|
||||
installmentId: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
paymentMethod: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
installmentYear: string;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
installmentNumber: number;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
installmentPaymentAmount: number;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
remainingAmount: number;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
installmentDate: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
installmentPaymentDate: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
installmentStatus: string;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
totalInstallments: number;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
paidInstallments: number;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
upcomingInstallmentCount: number;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
overdueInstallmentCount: number;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
bankId: string;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
totalPremiumCollected: number;
|
||||
|
||||
@Prop({ type: Boolean, default: true, index: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Prop({ type: String, required: false, index: true })
|
||||
syncBatchId: string;
|
||||
|
||||
@Prop({ type: Date, required: false })
|
||||
lastSyncedAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
lastUpdated: Date;
|
||||
|
||||
@Prop({ type: Date })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const InstallmentSchema = SchemaFactory.createForClass(InstallmentModel);
|
||||
|
||||
// Create compound indexes for efficient queries
|
||||
InstallmentSchema.index({ policyId: 1, userId: 1 });
|
||||
InstallmentSchema.index({ userId: 1 });
|
||||
InstallmentSchema.index({ policyId: 1, installmentStatus: 1 });
|
||||
InstallmentSchema.index({ installmentId: 1 }, { unique: true });
|
||||
InstallmentSchema.index({ userId: 1, isActive: 1 });
|
||||
InstallmentSchema.index({ userId: 1, syncBatchId: 1 });
|
||||
|
||||
// Made with Bob
|
||||
26
src/database/model/messages.model.ts
Normal file
26
src/database/model/messages.model.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Prop, Schema } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
import { ReactEnum } from 'src/common/types/react.type';
|
||||
import { Sender } from 'src/common/types/sender.type';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt' },
|
||||
versionKey: false,
|
||||
_id: true,
|
||||
})
|
||||
export class MessagesModel {
|
||||
@Prop({ _id: true, default: new Types.ObjectId() })
|
||||
messageId: Types.ObjectId;
|
||||
|
||||
@Prop({ default: false })
|
||||
text: string;
|
||||
|
||||
@Prop({ default: null, type: Sender })
|
||||
sender: Sender;
|
||||
|
||||
@Prop({ default: null, enum: ReactEnum })
|
||||
react: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
}
|
||||
105
src/database/model/policy.model.ts
Normal file
105
src/database/model/policy.model.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'policies',
|
||||
_id: true,
|
||||
})
|
||||
export class PolicyModel {
|
||||
@Prop({ type: Types.ObjectId, required: true, index: true })
|
||||
userId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Number, required: true, index: true })
|
||||
policyId: number;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
policyNumber: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
insuranceLineName: string;
|
||||
|
||||
@Prop({ type: Number, required: true })
|
||||
insuranceLineCode: number;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
policyHolderName: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
insuredName: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
policyIssueDate: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
policyBeginDate: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
policyEndDate: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
policyStatus: string;
|
||||
|
||||
@Prop({ type: Number, required: true })
|
||||
policyStatusCode: number;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
uniquePolicyCode: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
referrerName: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
issuingUnit: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
policyHolderCode: string;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
agentCode: number;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
policyType: string; // 'life', 'car', 'health', 'fire', 'cargo', 'equipment', 'travel', 'liability', 'personalAccident'
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
address: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
licensePlate: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
vehicleType: string;
|
||||
|
||||
@Prop({ type: Object, required: false })
|
||||
additionalData: Record<string, unknown>;
|
||||
|
||||
@Prop({ type: Boolean, default: true, index: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Prop({ type: String, required: false, index: true })
|
||||
syncBatchId: string;
|
||||
|
||||
@Prop({ type: Date, required: false })
|
||||
lastSyncedAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
lastUpdated: Date;
|
||||
|
||||
@Prop({ type: Date })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const PolicySchema = SchemaFactory.createForClass(PolicyModel);
|
||||
|
||||
// Create compound index for efficient queries
|
||||
PolicySchema.index({ userId: 1, policyId: 1 }, { unique: true });
|
||||
PolicySchema.index({ policyId: 1 });
|
||||
PolicySchema.index({ userId: 1, policyStatusCode: 1 });
|
||||
PolicySchema.index({ userId: 1, isActive: 1 });
|
||||
PolicySchema.index({ userId: 1, syncBatchId: 1 });
|
||||
|
||||
// Made with Bob
|
||||
56
src/database/model/questionModification.model.ts
Normal file
56
src/database/model/questionModification.model.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
export interface requestedBy {
|
||||
name: string; // User's mobile number
|
||||
_id: Types.ObjectId;
|
||||
}
|
||||
|
||||
@Schema({ collection: 'questionModification' })
|
||||
export class QuestionModificationModel {
|
||||
@Prop({ type: Types.ObjectId, ref: 'DictionariesModel' })
|
||||
dictionaryId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Types.ObjectId })
|
||||
questionId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String })
|
||||
oldQuestion: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
oldAnswer: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
newQuestion: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
newAnswer: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId })
|
||||
requestedBy: requestedBy; // expert's mobile or ID
|
||||
|
||||
@Prop({ type: String })
|
||||
status: 'pending' | 'approved' | 'rejected';
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Array })
|
||||
createdAtPersian: [];
|
||||
|
||||
@Prop({ type: Date })
|
||||
reviewedAt?: Date;
|
||||
|
||||
@Prop({ type: Array })
|
||||
reviewedAtPersian: [];
|
||||
|
||||
@Prop({ type: Types.ObjectId })
|
||||
reviewedBy?: requestedBy; // admin's ID
|
||||
|
||||
@Prop({ type: String })
|
||||
rejectionReason?: string;
|
||||
}
|
||||
|
||||
export const QuestionModificationSchema = SchemaFactory.createForClass(
|
||||
QuestionModificationModel,
|
||||
);
|
||||
44
src/database/model/questions.model.ts
Normal file
44
src/database/model/questions.model.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Prop, Schema } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
export interface UpdatedBy {
|
||||
name: string; // User's mobile number
|
||||
updatedAt: {
|
||||
time: string; // Time of update
|
||||
date: string; // Date of update
|
||||
};
|
||||
}
|
||||
@Schema({
|
||||
timestamps: false,
|
||||
versionKey: false,
|
||||
_id: true,
|
||||
})
|
||||
export class QuestionsModel {
|
||||
@Prop({ _id: true, default: new Types.ObjectId() })
|
||||
_id: Types.ObjectId;
|
||||
|
||||
@Prop({ type: 'string' })
|
||||
question: string;
|
||||
|
||||
@Prop({ type: 'string' })
|
||||
answer: string;
|
||||
|
||||
/** AI sync/export item id (UUID). Required for AI-first PUT/DELETE. */
|
||||
@Prop({ type: String, required: false })
|
||||
aiItemId?: string;
|
||||
|
||||
@Prop({ type: Boolean, default: false })
|
||||
deleted: boolean;
|
||||
|
||||
@Prop({ type: Array })
|
||||
createdAt: any[];
|
||||
|
||||
@Prop({ type: Number })
|
||||
createdISO: number;
|
||||
|
||||
@Prop({ type: Array })
|
||||
updatedAt: any[];
|
||||
|
||||
@Prop({ type: [Object] })
|
||||
updatedBy: UpdatedBy[];
|
||||
}
|
||||
20
src/database/model/rate.model.ts
Normal file
20
src/database/model/rate.model.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Prop, Schema } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
})
|
||||
export class RateModel {
|
||||
@Prop({ _id: true, default: new Types.ObjectId() })
|
||||
sessionId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String })
|
||||
rate: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAtDate: Date;
|
||||
}
|
||||
33
src/database/model/reassigned-logs.model.ts
Normal file
33
src/database/model/reassigned-logs.model.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'reassigned_logs',
|
||||
})
|
||||
export class ReassignedLogsModel extends Document {
|
||||
@Prop({ type: Types.ObjectId, ref: 'SessionModel', required: true })
|
||||
sessionId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'UserModel', required: true })
|
||||
userId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
from: string; // Expert email (from expert)
|
||||
|
||||
@Prop({ type: String, required: false, default: null })
|
||||
to: string | null; // Expert email (to expert), or null if no expert available
|
||||
|
||||
@Prop({ type: String, required: false, default: 'auto' })
|
||||
source: 'manual' | 'auto';
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
mode?: 'selective' | 'random';
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export const ReassignedLogsSchema = SchemaFactory.createForClass(ReassignedLogsModel);
|
||||
|
||||
125
src/database/model/sessions.model.ts
Normal file
125
src/database/model/sessions.model.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { Sender } from 'src/common/types/sender.type';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'sessions', // Name of the new collection
|
||||
})
|
||||
export class SessionModel extends Document {
|
||||
@Prop({ type: Types.ObjectId, ref: 'UserModel', required: true })
|
||||
userId: Types.ObjectId; // Reference to the user who owns this session
|
||||
|
||||
@Prop({ required: true })
|
||||
chatTitle: string;
|
||||
|
||||
@Prop({ required: true, default: false })
|
||||
connectedToExpert: boolean;
|
||||
|
||||
/** AI offered a human handoff; user still chooses whether to open online chat. */
|
||||
@Prop({ required: false, default: false })
|
||||
escalationOffered: boolean;
|
||||
|
||||
@Prop({ required: true, default: false })
|
||||
onlineChatClosed: boolean;
|
||||
|
||||
@Prop({ type: Date, required: false, default: null })
|
||||
onlineStartDate: Date;
|
||||
|
||||
@Prop({ type: Date, required: false, default: null })
|
||||
onlineEndDate: Date;
|
||||
|
||||
@Prop({ required: true, default: false })
|
||||
chatClosed: boolean;
|
||||
|
||||
@Prop({ required: false })
|
||||
expert: string;
|
||||
|
||||
/** Manual expert transfers per session (max 1). Auto-reassign does not increment this. */
|
||||
@Prop({ required: true, default: 0 })
|
||||
transferCount: number;
|
||||
|
||||
@Prop({ required: false })
|
||||
expertRate: number;
|
||||
|
||||
@Prop({ required: false })
|
||||
adminRateToExpert: boolean;
|
||||
|
||||
@Prop({ required: false })
|
||||
adminRateToBot: boolean;
|
||||
|
||||
@Prop({ required: false })
|
||||
roomId: string;
|
||||
|
||||
@Prop({ type: [String], required: true })
|
||||
createdAt: [string, string]; // [time, date]
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdISO: Date;
|
||||
|
||||
@Prop({
|
||||
type: [
|
||||
{
|
||||
messageId: { type: Types.ObjectId, required: true },
|
||||
text: { type: String, required: true },
|
||||
sender: { type: String, required: true },
|
||||
react: { type: String, default: 'Nothing' },
|
||||
runId: { type: String, required: false },
|
||||
aiStatus: { type: String, required: false },
|
||||
escalation: { type: Object, required: false },
|
||||
createdAt: { type: [String], required: true }, // [time, date]
|
||||
createdISO: { type: Date, required: true },
|
||||
edited: { type: Boolean, default: false },
|
||||
messageType: {
|
||||
type: String,
|
||||
enum: ['text', 'image', 'voice', 'document'],
|
||||
default: 'text',
|
||||
},
|
||||
voiceDurationSec: { type: Number, required: false },
|
||||
voiceMimeType: { type: String, required: false },
|
||||
/** Image/document or generic attachment MIME (voice still uses voiceMimeType for compatibility). */
|
||||
mimeType: { type: String, required: false },
|
||||
replyTo: {
|
||||
type: {
|
||||
messageId: { type: Types.ObjectId, required: true },
|
||||
textPreview: { type: String, default: '' },
|
||||
sender: { type: String, required: true },
|
||||
createdISO: { type: Date, required: false },
|
||||
unavailable: { type: Boolean, default: false },
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
})
|
||||
messages: {
|
||||
messageId: Types.ObjectId;
|
||||
text: string;
|
||||
sender: Sender;
|
||||
react: string;
|
||||
createdAt: [string, string];
|
||||
createdISO: Date;
|
||||
edited: boolean;
|
||||
runId?: string;
|
||||
aiStatus?: string;
|
||||
escalation?: {
|
||||
summary?: string;
|
||||
handoff_context?: Record<string, unknown>;
|
||||
};
|
||||
messageType?: 'text' | 'image' | 'voice' | 'document';
|
||||
voiceDurationSec?: number;
|
||||
voiceMimeType?: string;
|
||||
mimeType?: string;
|
||||
replyTo?: {
|
||||
messageId: Types.ObjectId;
|
||||
textPreview: string;
|
||||
sender: Sender;
|
||||
createdISO?: Date;
|
||||
unavailable: boolean;
|
||||
};
|
||||
}[];
|
||||
}
|
||||
|
||||
export const SessionSchema = SchemaFactory.createForClass(SessionModel);
|
||||
33
src/database/model/staff-role.model.ts
Normal file
33
src/database/model/staff-role.model.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document } from 'mongoose';
|
||||
|
||||
export type StaffRoleDocument = StaffRoleModel & Document;
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'staff_roles',
|
||||
})
|
||||
export class StaffRoleModel {
|
||||
/** Unique role slug stored on admin.role (e.g. admin, expert, content_manager). */
|
||||
@Prop({ required: true, unique: true, index: true })
|
||||
name: string;
|
||||
|
||||
@Prop({ required: false })
|
||||
displayName?: string;
|
||||
|
||||
@Prop({ type: [String], default: [] })
|
||||
permissions: string[];
|
||||
|
||||
/** System roles cannot be deleted or renamed. */
|
||||
@Prop({ default: false })
|
||||
isSystem: boolean;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const StaffRoleSchema = SchemaFactory.createForClass(StaffRoleModel);
|
||||
35
src/database/model/user-insurance-snapshot.model.ts
Normal file
35
src/database/model/user-insurance-snapshot.model.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'user_insurance_snapshots',
|
||||
_id: true,
|
||||
})
|
||||
export class UserInsuranceSnapshotModel {
|
||||
@Prop({ type: Types.ObjectId, required: true, unique: true, index: true })
|
||||
userId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
syncBatchId: string;
|
||||
|
||||
@Prop({ type: Date, required: true })
|
||||
syncedAt: Date;
|
||||
|
||||
@Prop({ type: Object, required: true })
|
||||
user_insurance_data: Record<string, unknown>;
|
||||
|
||||
@Prop({ type: Object, required: true })
|
||||
user_installments_data: Record<string, unknown>;
|
||||
|
||||
@Prop({ type: Date })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const UserInsuranceSnapshotSchema = SchemaFactory.createForClass(
|
||||
UserInsuranceSnapshotModel,
|
||||
);
|
||||
37
src/database/model/user.model.ts
Normal file
37
src/database/model/user.model.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as crypto from 'node:crypto';
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { ChatModel } from './chat.model';
|
||||
import { UsersBaseModel } from './users-base.model';
|
||||
|
||||
@Schema({
|
||||
timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
|
||||
versionKey: false,
|
||||
collection: 'user',
|
||||
_id: true,
|
||||
})
|
||||
export class UserModel extends UsersBaseModel {
|
||||
@Prop({ default: [] })
|
||||
chat: ChatModel[];
|
||||
|
||||
@Prop({
|
||||
required: false,
|
||||
select: true,
|
||||
set: (otp: string) =>
|
||||
otp ? crypto.createHash('sha256').update(otp).digest('hex') : null,
|
||||
expires: 120, // The OTP will automatically be removed after 120 seconds
|
||||
})
|
||||
otp: string | null;
|
||||
|
||||
@Prop({ type: Date, default: null })
|
||||
otpCreatedAt: Date | null;
|
||||
|
||||
@Prop({ type: Number, default: 0 })
|
||||
otpAttempts: number;
|
||||
}
|
||||
|
||||
export const UserSchema = SchemaFactory.createForClass(UserModel);
|
||||
|
||||
UserSchema.pre('save', function (next) {
|
||||
this.updatedAt = new Date();
|
||||
next();
|
||||
});
|
||||
76
src/database/model/users-base.model.ts
Normal file
76
src/database/model/users-base.model.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import * as crypto from 'node:crypto';
|
||||
import { Prop, Schema } from '@nestjs/mongoose';
|
||||
import { Role } from 'src/common/types/role.type';
|
||||
|
||||
@Schema()
|
||||
export class UsersBaseModel {
|
||||
@Prop()
|
||||
name: string;
|
||||
|
||||
@Prop()
|
||||
family: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
role: Role;
|
||||
|
||||
@Prop()
|
||||
fatherName: string;
|
||||
|
||||
@Prop()
|
||||
shenasnameseri: string;
|
||||
|
||||
@Prop()
|
||||
shenasnameserial: string;
|
||||
|
||||
@Prop()
|
||||
birthDate: string;
|
||||
|
||||
@Prop()
|
||||
gender: string;
|
||||
|
||||
@Prop({
|
||||
required: false,
|
||||
match: /^[0-9]{11}$/,
|
||||
default: undefined,
|
||||
})
|
||||
mobile: string;
|
||||
|
||||
@Prop()
|
||||
birthday: string;
|
||||
|
||||
@Prop()
|
||||
nationalCode: string;
|
||||
|
||||
@Prop({ match: /^((?!\.)[\w\-_.]*[^.])(@\w+)(\.\w+(\.\w+)?[^.\W])$/ })
|
||||
email: string;
|
||||
|
||||
@Prop()
|
||||
address: string;
|
||||
|
||||
@Prop({
|
||||
default: function () {
|
||||
// Generate 6-digit number or use mobile
|
||||
return (
|
||||
this.mobile || Math.floor(100000 + Math.random() * 900000).toString()
|
||||
);
|
||||
},
|
||||
})
|
||||
username: string;
|
||||
|
||||
@Prop({ type: Date })
|
||||
last_login: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
|
||||
static validateOtp(otpInput: string, storedOtpHash: string): boolean {
|
||||
const inputOtpHash = crypto
|
||||
.createHash('sha256')
|
||||
.update(otpInput)
|
||||
.digest('hex');
|
||||
return inputOtpHash === storedOtpHash;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user