@diagramers/api
Version:
Diagramers API - A comprehensive Node.js API template with TypeScript, Firebase Functions, and Socket.io
291 lines (255 loc) • 8.45 kB
text/typescript
import { DatabaseConfig } from '../config/interfaces';
import { logger } from '../logging';
import { DatabaseManager } from './connection';
import { UserModel } from '../../modules/users/schemas/user.schema';
import * as bcrypt from 'bcryptjs';
export interface SeedData {
users?: any[];
roles?: any[];
permissions?: any[];
[key: string]: any[];
}
export interface SeederOptions {
force?: boolean; // Force reseed even if data exists
truncate?: boolean; // Truncate tables before seeding
environment?: string; // Environment-specific seeding
}
export class DatabaseSeeder {
private dbManager: DatabaseManager;
private config: DatabaseConfig;
constructor(dbManager: DatabaseManager, config: DatabaseConfig) {
this.dbManager = dbManager;
this.config = config;
}
/**
* Run database seeding
*/
async seed(options: SeederOptions = {}): Promise<void> {
try {
logger.info('[DatabaseSeeder] Starting database seeding...');
// Check if seeding is needed
if (!options.force && await this.hasData()) {
logger.info('[DatabaseSeeder] Database already has data, skipping seeding');
return;
}
// Truncate if requested
if (options.truncate) {
await this.truncate();
}
// Load seed data based on environment
const seedData = await this.loadSeedData(options.environment);
// Seed data
await this.seedUsers(seedData.users || []);
await this.seedRoles(seedData.roles || []);
await this.seedPermissions(seedData.permissions || []);
logger.info('[DatabaseSeeder] Database seeding completed successfully');
} catch (error) {
logger.error('[DatabaseSeeder] Failed to seed database:', error);
throw error;
}
}
/**
* Check if database already has data
*/
private async hasData(): Promise<boolean> {
try {
if (this.config.dialect === 'mongodb') {
const userCount = await UserModel.countDocuments();
return userCount > 0;
} else {
// For SQL databases, you'd check the users table
// This is a placeholder for SQL implementation
return false;
}
} catch (error) {
logger.warn('[DatabaseSeeder] Could not check existing data:', error);
return false;
}
}
/**
* Truncate all tables/collections
*/
private async truncate(): Promise<void> {
try {
logger.info('[DatabaseSeeder] Truncating database...');
if (this.config.dialect === 'mongodb') {
await UserModel.deleteMany({});
// Add other collections as needed
} else {
// For SQL databases, you'd truncate tables
// This is a placeholder for SQL implementation
}
logger.info('[DatabaseSeeder] Database truncated successfully');
} catch (error) {
logger.error('[DatabaseSeeder] Failed to truncate database:', error);
throw error;
}
}
/**
* Load seed data based on environment
*/
private async loadSeedData(environment?: string): Promise<SeedData> {
const env = environment || process.env.NODE_ENV || 'development';
// Default seed data
const defaultData: SeedData = {
users: [
{
email: 'admin@diagramers.com',
username: 'admin',
password: '12345678@',
firstName: 'Admin',
lastName: 'User',
role: 'admin',
isActive: true,
emailVerified: true,
phoneVerified: false,
permissions: ['*']
},
{
email: 'user@diagramers.com',
username: 'user',
password: 'user123',
firstName: 'Regular',
lastName: 'User',
role: 'user',
isActive: true,
emailVerified: true,
phoneVerified: false,
permissions: ['read:own', 'write:own']
}
],
roles: [
{ name: 'admin', description: 'Administrator with full access' },
{ name: 'moderator', description: 'Moderator with limited admin access' },
{ name: 'user', description: 'Regular user' }
],
permissions: [
{ name: '*', description: 'All permissions' },
{ name: 'read:own', description: 'Read own data' },
{ name: 'write:own', description: 'Write own data' },
{ name: 'read:all', description: 'Read all data' },
{ name: 'write:all', description: 'Write all data' }
]
};
// Environment-specific overrides
if (env === 'production') {
// Production should have minimal seed data or none
return {
users: [],
roles: defaultData.roles,
permissions: defaultData.permissions
};
}
if (env === 'testing') {
// Testing environment with test data
return {
users: [
{
email: 'test@diagramers.com',
username: 'testuser',
password: 'test123',
firstName: 'Test',
lastName: 'User',
role: 'user',
isActive: true,
emailVerified: true,
phoneVerified: false,
permissions: ['read:own']
}
],
roles: defaultData.roles,
permissions: defaultData.permissions
};
}
return defaultData;
}
/**
* Seed users
*/
private async seedUsers(users: any[]): Promise<void> {
if (users.length === 0) return;
logger.info(`[DatabaseSeeder] Seeding ${users.length} users...`);
for (const userData of users) {
try {
// Check if user already exists
const existingUser = await UserModel.findOne({
$or: [
{ email: userData.email },
{ username: userData.username }
]
});
if (existingUser) {
logger.info(`[DatabaseSeeder] User ${userData.email} already exists, skipping`);
continue;
}
// Hash password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(userData.password, saltRounds);
// Create user
const user = new UserModel({
...userData,
password: hashedPassword,
email: userData.email.toLowerCase(),
status: 1
});
await user.save();
logger.info(`[DatabaseSeeder] Created user: ${userData.email}`);
} catch (error) {
logger.error(`[DatabaseSeeder] Failed to create user ${userData.email}:`, error);
}
}
}
/**
* Seed roles (placeholder for role-based system)
*/
private async seedRoles(roles: any[]): Promise<void> {
if (roles.length === 0) return;
logger.info(`[DatabaseSeeder] Seeding ${roles.length} roles...`);
// This would create roles in a roles collection/table
// For now, just log the roles
for (const role of roles) {
logger.info(`[DatabaseSeeder] Role: ${role.name} - ${role.description}`);
}
}
/**
* Seed permissions (placeholder for permission-based system)
*/
private async seedPermissions(permissions: any[]): Promise<void> {
if (permissions.length === 0) return;
logger.info(`[DatabaseSeeder] Seeding ${permissions.length} permissions...`);
// This would create permissions in a permissions collection/table
// For now, just log the permissions
for (const permission of permissions) {
logger.info(`[DatabaseSeeder] Permission: ${permission.name} - ${permission.description}`);
}
}
/**
* Create custom seed data
*/
async createSeedData(data: SeedData): Promise<void> {
try {
logger.info('[DatabaseSeeder] Creating custom seed data...');
await this.seedUsers(data.users || []);
await this.seedRoles(data.roles || []);
await this.seedPermissions(data.permissions || []);
logger.info('[DatabaseSeeder] Custom seed data created successfully');
} catch (error) {
logger.error('[DatabaseSeeder] Failed to create custom seed data:', error);
throw error;
}
}
/**
* Reset database (truncate and reseed)
*/
async reset(): Promise<void> {
try {
logger.info('[DatabaseSeeder] Resetting database...');
await this.truncate();
await this.seed({ force: true });
logger.info('[DatabaseSeeder] Database reset completed');
} catch (error) {
logger.error('[DatabaseSeeder] Failed to reset database:', error);
throw error;
}
}
}