@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
989 lines (841 loc) • 27 kB
JavaScript
"use strict";
// Shared TypeORM configuration for all Node.js backend templates
// This provides comprehensive database integration with migrations and entity management
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTypeORMServerIntegration = exports.getTypeORMReadmeSection = exports.getTypeORMDockerCompose = exports.getTypeORMHealthCheck = exports.getTypeORMConfig = void 0;
const getTypeORMConfig = () => ({
dependencies: {
'typeorm': '^0.3.19',
'reflect-metadata': '^0.2.1',
'class-validator': '^0.14.1',
'class-transformer': '^0.5.1',
'bcrypt': '^5.1.1',
'uuid': '^9.0.1',
// Database drivers (users can choose which to include)
'pg': '^8.11.3', // PostgreSQL
'mysql2': '^3.9.1', // MySQL
'sqlite3': '^5.1.7' // SQLite
},
devDependencies: {
'@types/bcrypt': '^5.0.2',
'@types/uuid': '^9.0.7',
'@types/pg': '^8.11.2',
'ts-node': '^10.9.2'
},
scripts: {
'db:generate': 'typeorm-ts-node-commonjs migration:generate -d src/config/database.ts',
'db:migrate': 'typeorm-ts-node-commonjs migration:run -d src/config/database.ts',
'db:migrate:revert': 'typeorm-ts-node-commonjs migration:revert -d src/config/database.ts',
'db:schema:sync': 'typeorm-ts-node-commonjs schema:sync -d src/config/database.ts',
'db:schema:drop': 'typeorm-ts-node-commonjs schema:drop -d src/config/database.ts',
'db:seed': 'tsx src/database/seeds/index.ts',
'entity:create': 'typeorm-ts-node-commonjs entity:create'
},
files: {
'src/config/database.ts': `import { DataSource } from 'typeorm';
import { User } from '../entities/User';
import { Profile } from '../entities/Profile';
import { Post } from '../entities/Post';
import * as dotenv from 'dotenv';
dotenv.config();
export const AppDataSource = new DataSource({
type: (process.env.DB_TYPE as any) || 'postgres',
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
username: process.env.DB_USERNAME || 'postgres',
password: process.env.DB_PASSWORD || 'password',
database: process.env.DB_DATABASE || 'mydb',
// Development settings
synchronize: process.env.NODE_ENV === 'development',
logging: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
// Entity configuration
entities: [User, Profile, Post],
migrations: ['src/database/migrations/*.ts'],
subscribers: ['src/database/subscribers/*.ts'],
// Additional options
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
extra: {
connectionLimit: 10,
acquireTimeout: 60000,
timeout: 60000,
},
});
// Database health check
export const checkDatabaseConnection = async (): Promise<boolean> => {
try {
if (!AppDataSource.isInitialized) {
await AppDataSource.initialize();
}
await AppDataSource.query('SELECT 1');
return true;
} catch (error) {
console.error('Database connection failed:', error);
return false;
}
};
// Graceful shutdown
export const closeDatabaseConnection = async (): Promise<void> => {
if (AppDataSource.isInitialized) {
await AppDataSource.destroy();
}
};`,
'src/entities/User.ts': `import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
OneToMany,
Index,
} from 'typeorm';
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
import { Profile } from './Profile';
import { Post } from './Post';
export enum UserRole {
USER = 'user',
ADMIN = 'admin',
MODERATOR = 'moderator',
}
export class User {
id: string;
email: string;
name: string;
password: string;
role: UserRole;
isActive: boolean;
lastLoginAt: Date;
createdAt: Date;
updatedAt: Date;
// Relations
profile: Profile;
posts: Post[];
// Virtual properties
get isAdmin(): boolean {
return this.role === UserRole.ADMIN;
}
get isModerator(): boolean {
return this.role === UserRole.MODERATOR;
}
}`,
'src/entities/Profile.ts': `import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { IsOptional, IsUrl, IsPhoneNumber } from 'class-validator';
import { User } from './User';
export class Profile {
id: string;
bio: string;
avatar: string;
website: string;
location: string;
birthday: Date;
phone: string;
isVerified: boolean;
isPublic: boolean;
createdAt: Date;
updatedAt: Date;
// Relations
user: User;
userId: string;
}`,
'src/entities/Post.ts': `import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
Index,
BeforeInsert,
BeforeUpdate,
} from 'typeorm';
import { IsNotEmpty, MinLength, MaxLength } from 'class-validator';
import { User } from './User';
export enum PostStatus {
DRAFT = 'draft',
PUBLISHED = 'published',
ARCHIVED = 'archived',
}
export class Post {
id: string;
title: string;
slug: string;
content: string;
excerpt: string;
status: PostStatus;
featuredImage: string;
tags: string[];
viewCount: number;
likeCount: number;
publishedAt: Date;
createdAt: Date;
updatedAt: Date;
// Relations
author: User;
authorId: string;
// Hooks
generateSlug() {
if (this.title && !this.slug) {
this.slug = this.title
.toLowerCase()
.replace(/[^a-z0-9 -]/g, '')
.replace(/\\s+/g, '-')
.replace(/-+/g, '-')
.trim('-');
}
}
generateExcerpt() {
if (this.content && !this.excerpt) {
this.excerpt = this.content.substring(0, 200).trim() + '...';
}
}
updatePublishedAt() {
if (this.status === PostStatus.PUBLISHED && !this.publishedAt) {
this.publishedAt = new Date();
}
}
// Virtual properties
get isPublished(): boolean {
return this.status === PostStatus.PUBLISHED;
}
get isDraft(): boolean {
return this.status === PostStatus.DRAFT;
}
get isArchived(): boolean {
return this.status === PostStatus.ARCHIVED;
}
}`,
'src/database/seeds/index.ts': `import 'reflect-metadata';
import { AppDataSource } from '../../config/database';
import { User, UserRole } from '../../entities/User';
import { Profile } from '../../entities/Profile';
import { Post, PostStatus } from '../../entities/Post';
import * as bcrypt from 'bcrypt';
async function seed() {
console.log('🌱 Starting database seeding...');
try {
// Initialize database connection
await AppDataSource.initialize();
const userRepository = AppDataSource.getRepository(User);
const profileRepository = AppDataSource.getRepository(Profile);
const postRepository = AppDataSource.getRepository(Post);
// Create admin user
const adminPassword = await bcrypt.hash('admin123', 12);
let adminUser = await userRepository.findOne({ where: { email: 'admin@example.com' } });
if (!adminUser) {
adminUser = userRepository.create({
email: 'admin@example.com',
name: 'Admin User',
password: adminPassword,
role: UserRole.ADMIN,
isActive: true,
lastLoginAt: new Date(),
});
await userRepository.save(adminUser);
// Create admin profile
const adminProfile = profileRepository.create({
bio: 'System administrator with full access privileges',
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=admin',
website: 'https://admin.example.com',
location: 'System',
isVerified: true,
isPublic: true,
userId: adminUser.id,
});
await profileRepository.save(adminProfile);
console.log('✅ Created admin user:', { id: adminUser.id, email: adminUser.email });
}
// Create regular user
const userPassword = await bcrypt.hash('user123', 12);
let regularUser = await userRepository.findOne({ where: { email: 'user@example.com' } });
if (!regularUser) {
regularUser = userRepository.create({
email: 'user@example.com',
name: 'Regular User',
password: userPassword,
role: UserRole.USER,
isActive: true,
});
await userRepository.save(regularUser);
// Create user profile
const userProfile = profileRepository.create({
bio: 'Regular user for testing and demonstrations',
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=user',
location: 'Testing Environment',
isVerified: false,
isPublic: true,
userId: regularUser.id,
});
await profileRepository.save(userProfile);
console.log('✅ Created regular user:', { id: regularUser.id, email: regularUser.email });
}
// Create moderator user
const moderatorPassword = await bcrypt.hash('moderator123', 12);
let moderatorUser = await userRepository.findOne({ where: { email: 'moderator@example.com' } });
if (!moderatorUser) {
moderatorUser = userRepository.create({
email: 'moderator@example.com',
name: 'Moderator User',
password: moderatorPassword,
role: UserRole.MODERATOR,
isActive: true,
});
await userRepository.save(moderatorUser);
// Create moderator profile
const moderatorProfile = profileRepository.create({
bio: 'Content moderator with review privileges',
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=moderator',
location: 'Moderation Center',
isVerified: true,
isPublic: true,
userId: moderatorUser.id,
});
await profileRepository.save(moderatorProfile);
console.log('✅ Created moderator user:', { id: moderatorUser.id, email: moderatorUser.email });
}
// Create sample posts
const samplePosts = [
{
title: 'Getting Started with TypeORM',
content: 'TypeORM is a powerful Object-Relational Mapping library that provides type safety and excellent developer experience. In this post, we will explore the basics of setting up TypeORM with a Node.js application.',
status: PostStatus.PUBLISHED,
tags: ['typeorm', 'nodejs', 'typescript', 'database'],
authorId: regularUser.id,
},
{
title: 'Advanced Entity Relationships',
content: 'Understanding entity relationships is crucial for building robust database schemas. This post covers one-to-one, one-to-many, and many-to-many relationships in TypeORM.',
status: PostStatus.PUBLISHED,
tags: ['typeorm', 'relationships', 'entities'],
authorId: regularUser.id,
},
{
title: 'Database Migrations Best Practices',
content: 'Managing database schema changes in production requires careful planning and execution. Learn about migration strategies and best practices.',
status: PostStatus.DRAFT,
tags: ['migrations', 'database', 'production'],
authorId: adminUser.id,
},
{
title: 'Query Optimization Techniques',
content: 'Optimizing database queries is essential for application performance. This comprehensive guide covers indexing, query analysis, and optimization strategies.',
status: PostStatus.PUBLISHED,
tags: ['optimization', 'performance', 'queries'],
authorId: moderatorUser.id,
},
{
title: 'Archived Legacy Post',
content: 'This is an example of an archived post that is no longer active but preserved for historical purposes.',
status: PostStatus.ARCHIVED,
tags: ['archived', 'legacy'],
authorId: adminUser.id,
},
];
for (const postData of samplePosts) {
const existingPost = await postRepository.findOne({ where: { title: postData.title } });
if (!existingPost) {
const post = postRepository.create(postData);
await postRepository.save(post);
}
}
const postCount = await postRepository.count();
console.log(\`✅ Created sample posts: \${postCount} posts total\`);
console.log('🎉 Database seeding completed successfully!');
console.log('');
console.log('📋 Test Credentials:');
console.log(' Admin: admin@example.com / admin123');
console.log(' User: user@example.com / user123');
console.log(' Moderator: moderator@example.com / moderator123');
} catch (error) {
console.error('❌ Error seeding database:', error);
process.exit(1);
} finally {
await AppDataSource.destroy();
}
}
// Run seeding
seed().catch((error) => {
console.error('Seeding failed:', error);
process.exit(1);
});`,
'src/services/userService.ts': `import { Repository } from 'typeorm';
import { AppDataSource } from '../config/database';
import { User, UserRole } from '../entities/User';
import { Profile } from '../entities/Profile';
import * as bcrypt from 'bcrypt';
export interface CreateUserData {
email: string;
password: string;
name: string;
role?: UserRole;
}
export interface UpdateUserData {
name?: string;
email?: string;
bio?: string;
avatar?: string;
website?: string;
location?: string;
phone?: string;
}
export interface PaginatedUsers {
users: User[];
total: number;
page: number;
limit: number;
pages: number;
}
export class UserService {
private userRepository: Repository<User>;
private profileRepository: Repository<Profile>;
constructor() {
this.userRepository = AppDataSource.getRepository(User);
this.profileRepository = AppDataSource.getRepository(Profile);
}
async findById(id: string): Promise<User | null> {
return await this.userRepository.findOne({
where: { id },
relations: ['profile', 'posts'],
select: {
id: true,
email: true,
name: true,
role: true,
isActive: true,
lastLoginAt: true,
createdAt: true,
updatedAt: true,
// Exclude password
},
});
}
async findByEmail(email: string, includePassword = false): Promise<User | null> {
const select: any = {
id: true,
email: true,
name: true,
role: true,
isActive: true,
lastLoginAt: true,
createdAt: true,
updatedAt: true,
};
if (includePassword) {
select.password = true;
}
return await this.userRepository.findOne({
where: { email },
relations: ['profile'],
select,
});
}
async createUser(userData: CreateUserData): Promise<User> {
const hashedPassword = await bcrypt.hash(userData.password, 12);
// Create user
const user = this.userRepository.create({
...userData,
password: hashedPassword,
role: userData.role || UserRole.USER,
});
const savedUser = await this.userRepository.save(user);
// Create default profile
const profile = this.profileRepository.create({
bio: \`Welcome \${userData.name}!\`,
avatar: \`https://api.dicebear.com/7.x/avataaars/svg?seed=\${savedUser.id}\`,
isVerified: false,
isPublic: true,
userId: savedUser.id,
});
await this.profileRepository.save(profile);
// Return user with profile
return await this.findById(savedUser.id)!;
}
async updateUser(id: string, updates: UpdateUserData): Promise<User> {
const user = await this.findById(id);
if (!user) {
throw new Error('User not found');
}
// Separate user and profile updates
const { bio, avatar, website, location, phone, ...userUpdates } = updates;
// Update user fields
if (Object.keys(userUpdates).length > 0) {
await this.userRepository.update(id, userUpdates);
}
// Update profile fields
const profileUpdates = { bio, avatar, website, location, phone };
const hasProfileUpdates = Object.values(profileUpdates).some(value => value !== undefined);
if (hasProfileUpdates) {
await this.profileRepository.update(
{ userId: id },
Object.fromEntries(
Object.entries(profileUpdates).filter(([_, value]) => value !== undefined)
)
);
}
// Return updated user
return await this.findById(id)!;
}
async deleteUser(id: string): Promise<void> {
const result = await this.userRepository.delete(id);
if (result.affected === 0) {
throw new Error('User not found');
}
}
async getUsers(
page: number = 1,
limit: number = 10,
search?: string,
role?: UserRole
): Promise<PaginatedUsers> {
const query = this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.profile', 'profile')
.select([
'user.id',
'user.email',
'user.name',
'user.role',
'user.isActive',
'user.lastLoginAt',
'user.createdAt',
'user.updatedAt',
'profile.bio',
'profile.avatar',
'profile.isVerified',
]);
if (search) {
query.andWhere(
'(user.name ILIKE :search OR user.email ILIKE :search OR profile.bio ILIKE :search)',
{ search: \`%\${search}%\` }
);
}
if (role) {
query.andWhere('user.role = :role', { role });
}
query.orderBy('user.createdAt', 'DESC');
const [users, total] = await query
.skip((page - 1) * limit)
.take(limit)
.getManyAndCount();
const pages = Math.ceil(total / limit);
return {
users,
total,
page,
limit,
pages,
};
}
async verifyPassword(email: string, password: string): Promise<User | null> {
const user = await this.findByEmail(email, true);
if (!user || !user.isActive) {
return null;
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return null;
}
// Update last login
await this.userRepository.update(user.id, { lastLoginAt: new Date() });
// Return user without password
return await this.findById(user.id);
}
async getUserStats(): Promise<{
totalUsers: number;
activeUsers: number;
adminUsers: number;
moderatorUsers: number;
recentUsers: number;
}> {
const [
totalUsers,
activeUsers,
adminUsers,
moderatorUsers,
recentUsers,
] = await Promise.all([
this.userRepository.count(),
this.userRepository.count({ where: { isActive: true } }),
this.userRepository.count({ where: { role: UserRole.ADMIN } }),
this.userRepository.count({ where: { role: UserRole.MODERATOR } }),
this.userRepository
.createQueryBuilder('user')
.where('user.createdAt >= :date', {
date: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
})
.getCount(),
]);
return {
totalUsers,
activeUsers,
adminUsers,
moderatorUsers,
recentUsers,
};
}
async updateLastLogin(userId: string): Promise<void> {
await this.userRepository.update(userId, { lastLoginAt: new Date() });
}
async deactivateUser(userId: string): Promise<void> {
await this.userRepository.update(userId, { isActive: false });
}
async activateUser(userId: string): Promise<void> {
await this.userRepository.update(userId, { isActive: true });
}
}`,
'.env.example': `# Database Configuration
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=password
DB_DATABASE=mydb
# Alternative database configurations (uncomment as needed)
# MySQL
# DB_TYPE=mysql
# DB_PORT=3306
# SQLite (for development)
# DB_TYPE=sqlite
# DB_DATABASE=./database.sqlite
# Application Configuration
NODE_ENV=development
PORT=3000
# Authentication
JWT_SECRET=your-super-secret-jwt-key-change-in-production
JWT_EXPIRATION=24h
# Other environment variables...`
},
postInstallCommands: [
'echo "📋 TypeORM Setup Instructions:"',
'echo "1. Configure your database in .env"',
'echo "2. Run: npm run db:migrate (to create tables)"',
'echo "3. Run: npm run db:seed (to add sample data)"',
'echo "4. Optional: npm run entity:create <EntityName> (to create new entities)"',
'echo ""',
'echo "💡 Database Management:"',
'echo "- npm run db:generate <MigrationName> (create migration)"',
'echo "- npm run db:migrate (run migrations)"',
'echo "- npm run db:migrate:revert (revert last migration)"',
'echo "- npm run db:schema:sync (sync schema - development only)"'
]
});
exports.getTypeORMConfig = getTypeORMConfig;
const getTypeORMHealthCheck = () => `
async checkDatabase(): Promise<{ status: string; latency?: number }> {
try {
const start = Date.now();
await AppDataSource.query('SELECT 1');
const latency = Date.now() - start;
return { status: 'ok', latency };
} catch (error) {
console.error('Database health check failed:', error);
return { status: 'error' };
}
}
`;
exports.getTypeORMHealthCheck = getTypeORMHealthCheck;
const getTypeORMDockerCompose = () => `
# Add this to your docker-compose.yml for PostgreSQL
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_USER: \${DB_USERNAME:-postgres}
POSTGRES_PASSWORD: \${DB_PASSWORD:-password}
POSTGRES_DB: \${DB_DATABASE:-mydb}
ports:
- "\${DB_PORT:-5432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \${DB_USERNAME:-postgres} -d \${DB_DATABASE:-mydb}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volumes:
postgres_data:
`;
exports.getTypeORMDockerCompose = getTypeORMDockerCompose;
const getTypeORMReadmeSection = () => `
## Database Integration (TypeORM)
This template includes comprehensive **TypeORM** integration for enterprise-grade database operations.
### Quick Start
1. **Configure Database**:
\`\`\`bash
# Copy environment file
cp .env.example .env
# Edit database settings in .env
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=password
DB_DATABASE=mydb
\`\`\`
2. **Initialize Database**:
\`\`\`bash
# Run migrations to create tables
npm run db:migrate
# Seed database with sample data
npm run db:seed
\`\`\`
### Database Commands
- \`npm run db:migrate\` - Run pending migrations
- \`npm run db:migrate:revert\` - Revert last migration
- \`npm run db:generate <name>\` - Generate new migration
- \`npm run db:schema:sync\` - Sync schema (development only)
- \`npm run db:schema:drop\` - Drop all tables
- \`npm run db:seed\` - Seed database with sample data
- \`npm run entity:create <name>\` - Create new entity
### Entity Schema
The application includes these entities:
- **User**: Authentication and user management with roles
- **Profile**: Extended user information and preferences
- **Post**: Content management with status workflow
### Features
- **🔐 Role-Based Access**: USER, ADMIN, MODERATOR roles
- **✅ Validation**: Class-validator decorators for data integrity
- **🔄 Migrations**: Version-controlled schema changes
- **🌱 Seeding**: Sample data for development and testing
- **🏃 Performance**: Optimized queries with proper indexing
- **🛡️ Security**: Prepared statements and SQL injection prevention
### Supported Databases
- **PostgreSQL** (recommended for production)
- **MySQL/MariaDB**
- **SQLite** (development/testing)
- **Microsoft SQL Server**
- **Oracle** (with additional configuration)
### Entity Relationships
\`\`\`typescript
User (1) ←→ (1) Profile
User (1) ←→ (∞) Post
\`\`\`
### Migration Workflow
1. Modify entities in \`src/entities/\`
2. Generate migration: \`npm run db:generate AddNewFeature\`
3. Review generated migration in \`src/database/migrations/\`
4. Apply migration: \`npm run db:migrate\`
### Production Deployment
1. Set production database credentials in environment
2. Run migrations: \`npm run db:migrate\`
3. Optional: Seed initial data: \`npm run db:seed\`
4. Never use \`db:schema:sync\` in production
`;
exports.getTypeORMReadmeSection = getTypeORMReadmeSection;
const getTypeORMServerIntegration = () => `
// Add this to your main server file
import 'reflect-metadata';
import { AppDataSource } from './config/database';
async function startServer() {
try {
// Initialize database connection
await AppDataSource.initialize();
console.log('✅ Database connection established');
// Start your server here
// ...
} catch (error) {
console.error('❌ Database connection failed:', error);
process.exit(1);
}
}
// Graceful shutdown
process.on('SIGTERM', async () => {
await AppDataSource.destroy();
process.exit(0);
});
startServer();
`;
exports.getTypeORMServerIntegration = getTypeORMServerIntegration;