initial commit

This commit is contained in:
2026-06-09 14:07:37 +03:30
parent 30ac533800
commit 996a4fcda7
121 changed files with 20557 additions and 3 deletions

View File

@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from '../users/schemas/user.schema';
import { DatabaseSeederService } from './database-seeder.service';
@Module({
imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
providers: [DatabaseSeederService],
})
export class DatabaseSeederModule {}

View File

@@ -0,0 +1,158 @@
import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { Model, Types } from 'mongoose';
import { User, UserDocument } from '../users/schemas/user.schema';
type ExtendedJsonValue =
| string
| number
| boolean
| null
| ExtendedJsonValue[]
| { [key: string]: ExtendedJsonValue };
interface SeedUser {
_id?: Types.ObjectId;
fullName: string;
username: string;
email: string;
mobile?: string;
password: string;
role: string;
clientType: string;
appName: string;
clientName: string;
isActive: boolean;
isBlocked: boolean;
apiKey?: string;
allowedInquiries: string[];
requestLimitPerMinute: number;
requestLimitPerDay: number;
totalRequests?: number;
passwordChangedAt?: Date;
createdBy?: Types.ObjectId;
createdAt?: Date;
updatedAt?: Date;
lastLoginAt?: Date;
refreshToken?: string;
}
@Injectable()
export class DatabaseSeederService implements OnApplicationBootstrap {
private readonly logger = new Logger(DatabaseSeederService.name);
private readonly usersSeedFile = 'inquiry-gateway.users.json';
constructor(@InjectModel(User.name) private readonly userModel: Model<UserDocument>) {}
async onApplicationBootstrap(): Promise<void> {
await this.seedUsers();
}
private async seedUsers(): Promise<void> {
const users = this.loadSeedUsers();
let inserted = 0;
let skipped = 0;
for (const user of users) {
const existing = await this.userModel.exists({
$or: [{ _id: user._id }, { username: user.username }, { email: user.email }],
});
if (existing) {
skipped += 1;
continue;
}
await this.userModel.create(user);
inserted += 1;
}
if (inserted > 0) {
this.logger.log(`Seeded ${inserted} baseline user(s); skipped ${skipped} existing user(s).`);
return;
}
this.logger.log(`Baseline users already exist; skipped ${skipped} user(s).`);
}
private loadSeedUsers(): SeedUser[] {
const seedPath = this.resolveSeedPath();
const raw = readFileSync(seedPath, 'utf8');
const parsed = JSON.parse(raw) as ExtendedJsonValue;
const normalized = this.normalizeExtendedJson(parsed);
if (!Array.isArray(normalized)) {
throw new Error(`${this.usersSeedFile} must contain an array of users`);
}
return normalized.map((user) => {
if (!this.isSeedUser(user)) {
throw new Error(`${this.usersSeedFile} contains an invalid user document`);
}
return {
...user,
username: user.username.toLowerCase().trim(),
email: user.email.toLowerCase().trim(),
};
});
}
private resolveSeedPath(): string {
const candidates = [
join(__dirname, '..', 'public', this.usersSeedFile),
join(process.cwd(), 'src', 'public', this.usersSeedFile),
];
const seedPath = candidates.find((candidate) => existsSync(candidate));
if (!seedPath) {
throw new Error(`Unable to find seed file: ${this.usersSeedFile}`);
}
return seedPath;
}
private normalizeExtendedJson(value: ExtendedJsonValue): unknown {
if (Array.isArray(value)) {
return value.map((item) => this.normalizeExtendedJson(item));
}
if (value === null || typeof value !== 'object') {
return value;
}
if (typeof value.$oid === 'string') {
return new Types.ObjectId(value.$oid);
}
if (typeof value.$date === 'string') {
return new Date(value.$date);
}
return Object.entries(value).reduce<Record<string, unknown>>((acc, [key, child]) => {
acc[key] = this.normalizeExtendedJson(child);
return acc;
}, {});
}
private isSeedUser(value: unknown): value is SeedUser {
if (!value || typeof value !== 'object') {
return false;
}
const user = value as Partial<SeedUser>;
return (
typeof user.fullName === 'string' &&
typeof user.username === 'string' &&
typeof user.email === 'string' &&
typeof user.password === 'string' &&
typeof user.role === 'string' &&
typeof user.clientType === 'string' &&
Array.isArray(user.allowedInquiries) &&
typeof user.requestLimitPerMinute === 'number' &&
typeof user.requestLimitPerDay === 'number'
);
}
}