forked from Shared/esg
initial commit
This commit is contained in:
10
src/database/database-seeder.module.ts
Normal file
10
src/database/database-seeder.module.ts
Normal 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 {}
|
||||
158
src/database/database-seeder.service.ts
Normal file
158
src/database/database-seeder.service.ts
Normal 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'
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user