@diagramers/cli
Version:
Diagramers CLI - Command-line tools for managing Diagramers projects
1,286 lines (1,196 loc) • 88.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectExtender = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const api_generator_1 = require("./api-generator");
class ProjectExtender {
constructor() {
this.features = [
{
name: 'auth',
description: 'Authentication system with JWT',
files: ['src/features/auth/**/*'],
dependencies: ['jsonwebtoken', 'bcryptjs']
},
{
name: 'email',
description: 'Modular, user-configurable email provider system',
files: ['src/modules/email/**/*']
},
{
name: 'socket',
description: 'Real-time WebSocket communication',
files: ['src/features/socket/**/*'],
dependencies: ['socket.io']
},
{
name: 'cron',
description: 'Scheduled task system',
files: ['src/features/cron/**/*'],
dependencies: ['node-cron']
},
{
name: 'audit',
description: 'Audit logging system',
files: ['src/features/audit/**/*']
}
];
}
async listFeatures() {
console.log(chalk_1.default.blue('�� Available features and templates:'));
console.log('');
console.log(chalk_1.default.green('🔧 Features:'));
this.features.forEach(feature => {
console.log(chalk_1.default.green(` ${feature.name}`));
console.log(chalk_1.default.gray(` ${feature.description}`));
if (feature.dependencies && feature.dependencies.length > 0) {
console.log(chalk_1.default.yellow(` Dependencies: ${feature.dependencies.join(', ')}`));
}
console.log('');
});
console.log(chalk_1.default.blue('📦 Module Templates:'));
console.log(chalk_1.default.green(' --module <name> --crud --fields field1,field2'));
console.log(chalk_1.default.gray(' Generate complete module with CRUD operations'));
console.log('');
console.log(chalk_1.default.blue('🗄️ Database Templates:'));
console.log(chalk_1.default.green(' --table <name> --fields field1,field2 --type mongodb'));
console.log(chalk_1.default.gray(' Generate database table/collection schema'));
console.log('');
console.log(chalk_1.default.blue('🔗 Relation Templates:'));
console.log(chalk_1.default.green(' --relation <name> --type mongodb'));
console.log(chalk_1.default.gray(' Generate database relations and references'));
console.log('');
}
async addFeature(featureName) {
const feature = this.features.find(f => f.name === featureName);
if (!feature) {
throw new Error(`Feature '${featureName}' not found. Use --list to see available features.`);
}
const projectPath = process.cwd();
// Check if feature already exists
const featurePath = path.join(projectPath, 'src', 'features', featureName);
if (await fs.pathExists(featurePath)) {
throw new Error(`Feature '${featureName}' already exists in this project.`);
}
// Create feature directory structure
await this.createFeatureStructure(projectPath, feature);
// Add dependencies if any
if (feature.dependencies && feature.dependencies.length > 0) {
await this.addDependencies(feature.dependencies);
}
// Update main configuration to use the extended feature
await this.updateMainConfiguration(projectPath, feature);
console.log(chalk_1.default.green(`✅ Feature '${featureName}' added successfully!`));
console.log(chalk_1.default.blue(`📝 Main configuration updated to include ${featureName} feature`));
}
async generateModule(moduleName, options = {}) {
const projectPath = process.cwd();
// Check if module already exists
const modulePath = path.join(projectPath, 'src', 'modules', moduleName);
if (await fs.pathExists(modulePath) && !options.force) {
throw new Error(`Module '${moduleName}' already exists. Use --force to override.`);
}
// Parse options
const fields = options.fields || ['name', 'description', 'status'];
const generateRoutes = options.generateRoutes !== false;
const generateDatabase = options.generateDatabase !== false;
const crud = options.crud || false;
console.log(chalk_1.default.blue(`📦 Generating module '${moduleName}'...`));
// Create module structure
await this.createModuleStructure(projectPath, moduleName, { fields, crud, generateRoutes, generateDatabase });
// Generate CRUD operations if requested
if (crud) {
console.log(chalk_1.default.blue('🔧 Generating CRUD operations...'));
await this.generateCRUDOperations(projectPath, moduleName, { fields });
}
// Register module
await this.registerModule(projectPath, moduleName);
// Update configuration
await this.updateDiagramersConfig(projectPath, moduleName);
console.log(chalk_1.default.green(`✅ Module '${moduleName}' generated successfully!`));
console.log(chalk_1.default.blue(`📁 Location: src/modules/${moduleName}/`));
if (generateRoutes) {
console.log(chalk_1.default.blue(`🔗 Routes: /api/${moduleName}s`));
}
if (generateDatabase) {
console.log(chalk_1.default.blue(`🗄️ Database: ${moduleName} collection`));
}
if (crud) {
console.log(chalk_1.default.blue(`🔧 CRUD: Full CRUD operations included`));
}
}
async generateTable(tableName, options = {}) {
const projectPath = process.cwd();
// Parse options
const fields = options.fields || ['name', 'description', 'status'];
const databaseType = options.type || 'mongodb';
const generateSeeder = options.seeder !== false;
const force = options.force || false;
console.log(chalk_1.default.blue(`🗄️ Generating table '${tableName}'...`));
// Create table schema
await this.createTableSchema(projectPath, tableName, { fields, type: databaseType, force });
// Add to seeder if requested
if (generateSeeder) {
console.log(chalk_1.default.blue('🌱 Adding to database seeder...'));
await this.updateSeeder(projectPath, tableName);
}
console.log(chalk_1.default.green(`✅ Table '${tableName}' generated successfully!`));
console.log(chalk_1.default.blue(`📁 Schema: src/modules/${tableName}/schemas/${tableName}.schema.ts`));
console.log(chalk_1.default.blue(`🗄️ Database Type: ${databaseType}`));
if (generateSeeder) {
console.log(chalk_1.default.blue(`🌱 Seeder: Added to src/core/database/seeder.ts`));
}
}
async generateRelations(relationName, options = {}) {
console.log(chalk_1.default.blue(`🔗 Generating relations for '${relationName}'...`));
// Parse options
const relationType = options.type || 'one-to-many';
const moduleName = options.module;
const force = options.force || false;
// For now, we'll use a simple approach - the user needs to specify both tables
if (!options.table1 || !options.table2) {
throw new Error('Both table1 and table2 must be specified for relation generation. Use: --table1 <name> --table2 <name>');
}
// Import and use the relation-generator service
const { generateRelation } = await Promise.resolve().then(() => __importStar(require('./relation-generator')));
await generateRelation(moduleName, options.table1, options.table2, relationType, { force });
console.log(chalk_1.default.green(`✅ Relations generated successfully for '${relationName}'!`));
console.log(chalk_1.default.blue(`🔗 Type: ${relationType}`));
console.log(chalk_1.default.blue(`📦 Module: ${moduleName}`));
console.log(chalk_1.default.blue(`🗄️ Tables: ${options.table1} ↔ ${options.table2}`));
}
async generateEndpoint(moduleName, endpointName, options = {}) {
console.log(chalk_1.default.blue(`🔧 Adding endpoint '${endpointName}' to module '${moduleName}'...`));
// Parse options
const method = options.method || 'GET';
const customPath = options.path || endpointName;
const description = options.description || `${endpointName} endpoint for ${moduleName}`;
const generateService = options.generateService !== false;
const generateController = options.generateController !== false;
const generateRoute = options.generateRoute !== false;
const force = options.force || false;
// Pass options to the api-generator service
await (0, api_generator_1.generateEndpoint)(moduleName, endpointName, {
method,
path: customPath,
description,
generateService,
generateController,
generateRoute,
force
});
console.log(chalk_1.default.green(`✅ Endpoint '${endpointName}' added to module '${moduleName}' successfully!`));
if (generateService) {
console.log(chalk_1.default.blue(`📝 Service method generated`));
}
if (generateController) {
console.log(chalk_1.default.blue(`🎮 Controller method generated`));
}
if (generateRoute) {
console.log(chalk_1.default.blue(`🛣️ Route generated`));
}
}
async addServiceMethod(modulePath, moduleName, endpointName, method, description) {
const singularName = moduleName; // Use exact name provided by developer
const servicePath = path.join(modulePath, 'services', `${singularName}.service.ts`);
if (!await fs.pathExists(servicePath)) {
throw new Error(`Service file not found: ${servicePath}`);
}
let serviceContent = await fs.readFile(servicePath, 'utf8');
const moduleNameCapitalized = this.capitalizeFirst(moduleName);
const endpointNameCapitalized = this.capitalizeFirst(endpointName);
// Generate service method based on HTTP method
let serviceMethod = '';
switch (method) {
case 'GET':
serviceMethod = ` async ${endpointName}(params?: any) {
// TODO: Implement ${endpointName} logic
// Example: return await ${moduleNameCapitalized}Model.find(params);
return { message: '${endpointNameCapitalized} endpoint - implement your logic here', params };
}`;
break;
case 'POST':
serviceMethod = ` async ${endpointName}(data: any) {
// TODO: Implement ${endpointName} logic
// Example: return await ${moduleNameCapitalized}Model.create(data);
return { message: '${endpointNameCapitalized} endpoint - implement your logic here', data };
}`;
break;
case 'PUT':
serviceMethod = ` async ${endpointName}(id: string, data: any) {
// TODO: Implement ${endpointName} logic
// Example: return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, data, { new: true });
return { message: '${endpointNameCapitalized} endpoint - implement your logic here', id, data };
}`;
break;
case 'DELETE':
serviceMethod = ` async ${endpointName}(id: string) {
// TODO: Implement ${endpointName} logic
// Example: return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, { status: 0 }, { new: true });
return { message: '${endpointNameCapitalized} endpoint - implement your logic here', id };
}`;
break;
default:
serviceMethod = ` async ${endpointName}(params?: any) {
// TODO: Implement ${endpointName} logic
return { message: '${endpointNameCapitalized} endpoint - implement your logic here', params };
}`;
}
// Add method before the closing brace
const lastBraceIndex = serviceContent.lastIndexOf('}');
serviceContent = serviceContent.slice(0, lastBraceIndex) + serviceMethod + '\n' + serviceContent.slice(lastBraceIndex);
await fs.writeFile(servicePath, serviceContent);
}
async addControllerMethod(modulePath, moduleName, endpointName, method, customPath, description) {
const singularName = moduleName; // Use exact name provided by developer
const controllerPath = path.join(modulePath, 'controllers', `${singularName}.controller.ts`);
if (!await fs.pathExists(controllerPath)) {
throw new Error(`Controller file not found: ${controllerPath}`);
}
let controllerContent = await fs.readFile(controllerPath, 'utf8');
const moduleNameCapitalized = this.capitalizeFirst(moduleName);
const endpointNameCapitalized = this.capitalizeFirst(endpointName);
// Generate Swagger documentation and controller method
let swaggerDoc = '';
let controllerMethod = '';
switch (method) {
case 'GET':
swaggerDoc = ` /**
* @openapi
* /api/${moduleName}/${customPath}:
* get:
* summary: ${endpointNameCapitalized} ${moduleName}
* description: ${description}
* tags: [${moduleNameCapitalized}s]
* responses:
* 200:
* description: ${endpointNameCapitalized} retrieved successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* Data:
* type: object
* StatusCode:
* type: integer
* example: 1000
* 400:
* description: Bad request
*/`;
controllerMethod = ` async ${endpointName}(req, res) {
const data = await this.service.${endpointName}(req.query);
res.json(data);
}`;
break;
case 'POST':
swaggerDoc = ` /**
* @openapi
* /api/${moduleName}/${customPath}:
* post:
* summary: ${endpointNameCapitalized} ${moduleName}
* description: ${description}
* tags: [${moduleNameCapitalized}s]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* # Add your request body properties here
* responses:
* 201:
* description: ${endpointNameCapitalized} created successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* Data:
* type: object
* StatusCode:
* type: integer
* example: 1000
* 400:
* description: Bad request
*/`;
controllerMethod = ` async ${endpointName}(req, res) {
const data = await this.service.${endpointName}(req.body);
res.json(data);
}`;
break;
case 'PUT':
swaggerDoc = ` /**
* @openapi
* /api/${moduleName}/${customPath}/{id}:
* put:
* summary: ${endpointNameCapitalized} ${moduleName}
* description: ${description}
* tags: [${moduleNameCapitalized}s]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: ${moduleNameCapitalized} ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* # Add your request body properties here
* responses:
* 200:
* description: ${endpointNameCapitalized} updated successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* Data:
* type: object
* StatusCode:
* type: integer
* example: 1000
* 404:
* description: ${moduleNameCapitalized} not found
*/`;
controllerMethod = ` async ${endpointName}(req, res) {
const data = await this.service.${endpointName}(req.params.id, req.body);
res.json(data);
}`;
break;
case 'DELETE':
swaggerDoc = ` /**
* @openapi
* /api/${moduleName}/${customPath}/{id}:
* delete:
* summary: ${endpointNameCapitalized} ${moduleName}
* description: ${description}
* tags: [${moduleNameCapitalized}s]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: ${moduleNameCapitalized} ID
* responses:
* 200:
* description: ${endpointNameCapitalized} successful
* content:
* application/json:
* schema:
* type: object
* properties:
* Data:
* type: object
* StatusCode:
* type: integer
* example: 1000
* 404:
* description: ${moduleNameCapitalized} not found
*/`;
controllerMethod = ` async ${endpointName}(req, res) {
const data = await this.service.${endpointName}(req.params.id);
res.json(data);
}`;
break;
default:
swaggerDoc = ` /**
* @openapi
* /api/${moduleName}/${customPath}:
* ${method.toLowerCase()}:
* summary: ${endpointNameCapitalized} ${moduleName}
* description: ${description}
* tags: [${moduleNameCapitalized}s]
* responses:
* 200:
* description: ${endpointNameCapitalized} successful
* content:
* application/json:
* schema:
* type: object
* properties:
* Data:
* type: object
* StatusCode:
* type: integer
* example: 1000
*/`;
controllerMethod = ` async ${endpointName}(req, res) {
const data = await this.service.${endpointName}(req.query);
res.json(data);
}`;
}
// Add method before the closing brace
const lastBraceIndex = controllerContent.lastIndexOf('}');
const methodWithDoc = swaggerDoc + '\n' + controllerMethod;
controllerContent = controllerContent.slice(0, lastBraceIndex) + methodWithDoc + '\n' + controllerContent.slice(lastBraceIndex);
await fs.writeFile(controllerPath, controllerContent);
}
async addRoute(modulePath, moduleName, endpointName, method, customPath) {
const singularName = moduleName; // Use exact name provided by developer
const routePath = path.join(modulePath, 'routes', `${singularName}.routes.ts`);
if (!await fs.pathExists(routePath)) {
throw new Error(`Route file not found: ${routePath}`);
}
let routeContent = await fs.readFile(routePath, 'utf8');
// Generate route based on HTTP method
let routeLine = '';
switch (method) {
case 'GET':
routeLine = `router.get('/${customPath}', (req, res) => controller.${endpointName}(req, res));`;
break;
case 'POST':
routeLine = `router.post('/${customPath}', (req, res) => controller.${endpointName}(req, res));`;
break;
case 'PUT':
routeLine = `router.put('/${customPath}/:id', (req, res) => controller.${endpointName}(req, res));`;
break;
case 'DELETE':
routeLine = `router.delete('/${customPath}/:id', (req, res) => controller.${endpointName}(req, res));`;
break;
default:
routeLine = `router.${method.toLowerCase()}('/${customPath}', (req, res) => controller.${endpointName}(req, res));`;
}
// Add route before the export statement
const exportIndex = routeContent.indexOf('export default router;');
if (exportIndex !== -1) {
routeContent = routeContent.slice(0, exportIndex) + routeLine + '\n\n' + routeContent.slice(exportIndex);
}
else {
// If no export found, add at the end
routeContent += '\n' + routeLine + '\n';
}
await fs.writeFile(routePath, routeContent);
}
async createFeatureStructure(projectPath, feature) {
const featurePath = path.join(projectPath, 'src', 'features', feature.name);
// Create basic feature structure
const structure = {
'controllers': 'Feature controllers',
'services': 'Feature business logic',
'schemas': 'Feature validation schemas',
'routes': 'Feature API routes',
'types': 'Feature TypeScript types',
'providers': 'Feature providers and implementations'
};
for (const [dir, description] of Object.entries(structure)) {
const dirPath = path.join(featurePath, dir);
await fs.ensureDir(dirPath);
// Create index file
const indexContent = `// ${description} for ${feature.name} feature
export * from './${feature.name}-${dir.slice(0, -1)}';
`;
await fs.writeFile(path.join(dirPath, 'index.ts'), indexContent);
}
// Create main feature file with core functions
const mainFeatureContent = await this.generateFeatureCore(feature.name);
await fs.writeFile(path.join(featurePath, `${feature.name}.feature.ts`), mainFeatureContent);
// Create feature configuration
const configContent = this.generateFeatureConfig(feature.name);
await fs.writeFile(path.join(featurePath, 'config.ts'), configContent);
// Create controllers, services, and other core files
await this.createFeatureCoreFiles(featurePath, feature.name);
// Track path refinements
await this.trackPathRefinements(projectPath, feature.name, 'feature');
}
async createFeatureCoreFiles(featurePath, featureName) {
const capitalizedName = this.capitalizeFirst(featureName);
// Create controller
const controllerContent = this.generateFeatureController(featureName, capitalizedName);
await fs.writeFile(path.join(featurePath, 'controllers', `${featureName}-controller.ts`), controllerContent);
// Create service
const serviceContent = this.generateFeatureService(featureName, capitalizedName);
await fs.writeFile(path.join(featurePath, 'services', `${featureName}-service.ts`), serviceContent);
// Create types
const typesContent = this.generateFeatureTypes(featureName, capitalizedName);
await fs.writeFile(path.join(featurePath, 'types', `${featureName}-types.ts`), typesContent);
// Create schemas
const schemasContent = this.generateFeatureSchemas(featureName, capitalizedName);
await fs.writeFile(path.join(featurePath, 'schemas', `${featureName}-schemas.ts`), schemasContent);
}
generateFeatureController(featureName, capitalizedName) {
switch (featureName) {
case 'auth':
return `import { Request, Response } from 'express';
import { AuthService } from '../services/auth-service';
import { Result } from '../../../shared/types/result';
import { logger } from '../../../core/logging';
export class AuthController {
constructor(private authService: AuthService) {}
async login(req: Request, res: Response): Promise<void> {
try {
const { email, password } = req.body;
const result = await this.authService.login(email, password);
res.json(result);
} catch (error) {
logger.error('Login error:', error);
res.status(500).json(Result.error('Login failed'));
}
}
async register(req: Request, res: Response): Promise<void> {
try {
const userData = req.body;
const result = await this.authService.register(userData);
res.json(result);
} catch (error) {
logger.error('Registration error:', error);
res.status(500).json(Result.error('Registration failed'));
}
}
async logout(req: Request, res: Response): Promise<void> {
try {
const token = req.headers.authorization?.replace('Bearer ', '');
const result = await this.authService.logout(token);
res.json(result);
} catch (error) {
logger.error('Logout error:', error);
res.status(500).json(Result.error('Logout failed'));
}
}
async refreshToken(req: Request, res: Response): Promise<void> {
try {
const { refreshToken } = req.body;
const result = await this.authService.refreshToken(refreshToken);
res.json(result);
} catch (error) {
logger.error('Token refresh error:', error);
res.status(500).json(Result.error('Token refresh failed'));
}
}
async forgotPassword(req: Request, res: Response): Promise<void> {
try {
const { email } = req.body;
const result = await this.authService.forgotPassword(email);
res.json(result);
} catch (error) {
logger.error('Forgot password error:', error);
res.status(500).json(Result.error('Password reset failed'));
}
}
async resetPassword(req: Request, res: Response): Promise<void> {
try {
const { token, newPassword } = req.body;
const result = await this.authService.resetPassword(token, newPassword);
res.json(result);
} catch (error) {
logger.error('Reset password error:', error);
res.status(500).json(Result.error('Password reset failed'));
}
}
async verifyEmail(req: Request, res: Response): Promise<void> {
try {
const { token } = req.body;
const result = await this.authService.verifyEmail(token);
res.json(result);
} catch (error) {
logger.error('Email verification error:', error);
res.status(500).json(Result.error('Email verification failed'));
}
}
}`;
case 'email':
return `import { Request, Response } from 'express';
import { EmailService } from '../services/email-service';
import { Result } from '../../../shared/types/result';
import { logger } from '../../../core/logging';
export class EmailController {
constructor(private emailService: EmailService) {}
async sendEmail(req: Request, res: Response): Promise<void> {
try {
const { to, subject, content, template } = req.body;
const result = await this.emailService.sendEmail({ to, subject, content, template });
res.json(result);
} catch (error) {
logger.error('Send email error:', error);
res.status(500).json(Result.error('Email sending failed'));
}
}
async sendTemplateEmail(req: Request, res: Response): Promise<void> {
try {
const { to, templateName, data } = req.body;
const result = await this.emailService.sendTemplateEmail(to, templateName, data);
res.json(result);
} catch (error) {
logger.error('Template email error:', error);
res.status(500).json(Result.error('Template email failed'));
}
}
async getTemplates(req: Request, res: Response): Promise<void> {
try {
const result = await this.emailService.getTemplates();
res.json(result);
} catch (error) {
logger.error('Get templates error:', error);
res.status(500).json(Result.error('Failed to get templates'));
}
}
async createTemplate(req: Request, res: Response): Promise<void> {
try {
const templateData = req.body;
const result = await this.emailService.createTemplate(templateData);
res.json(result);
} catch (error) {
logger.error('Create template error:', error);
res.status(500).json(Result.error('Template creation failed'));
}
}
}`;
default:
return `import { Request, Response } from 'express';
import { ${capitalizedName}Service } from '../services/${featureName}-service';
import { Result } from '../../../shared/types/result';
import { logger } from '../../../core/logging';
export class ${capitalizedName}Controller {
constructor(private ${featureName}Service: ${capitalizedName}Service) {}
async index(req: Request, res: Response): Promise<void> {
try {
const result = await this.${featureName}Service.getStatus();
res.json(result);
} catch (error) {
logger.error('${capitalizedName} index error:', error);
res.status(500).json(Result.error('${capitalizedName} service error'));
}
}
// Add your custom controller methods here
async customMethod(req: Request, res: Response): Promise<void> {
try {
const result = await this.${featureName}Service.customMethod(req.body);
res.json(result);
} catch (error) {
logger.error('${capitalizedName} custom method error:', error);
res.status(500).json(Result.error('Custom method failed'));
}
}
}`;
}
}
generateFeatureService(featureName, capitalizedName) {
switch (featureName) {
case 'auth':
return `import { Result } from '../../../shared/types/result';
import { logger } from '../../../core/logging';
import { AuthConfig } from '../config';
export class AuthService {
constructor(private config: AuthConfig) {}
async login(email: string, password: string): Promise<Result> {
try {
// Implement login logic here
logger.info('Login attempt for:', email);
// Example implementation
if (!email || !password) {
return Result.badRequest('Email and password are required');
}
// Add your authentication logic here
// - Validate credentials
// - Generate JWT tokens
// - Return user data and tokens
return Result.success({ message: 'Login successful' });
} catch (error) {
logger.error('Login service error:', error);
return Result.error('Login failed');
}
}
async register(userData: any): Promise<Result> {
try {
logger.info('Registration attempt for:', userData.email);
// Add your registration logic here
// - Validate user data
// - Hash password
// - Create user record
// - Send verification email
return Result.success({ message: 'Registration successful' });
} catch (error) {
logger.error('Registration service error:', error);
return Result.error('Registration failed');
}
}
async logout(token: string): Promise<Result> {
try {
// Add your logout logic here
// - Invalidate token
// - Clear session
return Result.success({ message: 'Logout successful' });
} catch (error) {
logger.error('Logout service error:', error);
return Result.error('Logout failed');
}
}
async refreshToken(refreshToken: string): Promise<Result> {
try {
// Add your token refresh logic here
// - Validate refresh token
// - Generate new access token
return Result.success({ message: 'Token refreshed' });
} catch (error) {
logger.error('Token refresh service error:', error);
return Result.error('Token refresh failed');
}
}
async forgotPassword(email: string): Promise<Result> {
try {
// Add your forgot password logic here
// - Validate email
// - Generate reset token
// - Send reset email
return Result.success({ message: 'Password reset email sent' });
} catch (error) {
logger.error('Forgot password service error:', error);
return Result.error('Password reset failed');
}
}
async resetPassword(token: string, newPassword: string): Promise<Result> {
try {
// Add your password reset logic here
// - Validate token
// - Update password
return Result.success({ message: 'Password reset successful' });
} catch (error) {
logger.error('Reset password service error:', error);
return Result.error('Password reset failed');
}
}
async verifyEmail(token: string): Promise<Result> {
try {
// Add your email verification logic here
// - Validate token
// - Mark email as verified
return Result.success({ message: 'Email verified' });
} catch (error) {
logger.error('Email verification service error:', error);
return Result.error('Email verification failed');
}
}
}`;
default:
return `import { Result } from '../../../shared/types/result';
import { logger } from '../../../core/logging';
import { ${capitalizedName}Config } from '../config';
export class ${capitalizedName}Service {
constructor(private config: ${capitalizedName}Config) {}
async getStatus(): Promise<Result> {
try {
return Result.success({
status: 'ok',
feature: '${featureName}',
enabled: this.config.enabled
});
} catch (error) {
logger.error('${capitalizedName} service error:', error);
return Result.error('${capitalizedName} service error');
}
}
async customMethod(data: any): Promise<Result> {
try {
// Add your custom business logic here
logger.info('${capitalizedName} custom method called with:', data);
return Result.success({ message: 'Custom method executed' });
} catch (error) {
logger.error('${capitalizedName} custom method error:', error);
return Result.error('Custom method failed');
}
}
// Add more service methods as needed
}`;
}
}
generateFeatureTypes(featureName, capitalizedName) {
return `// TypeScript types for ${featureName} feature
export interface ${capitalizedName}Config {
enabled: boolean;
// Add your configuration interface properties here
}
export interface ${capitalizedName}Data {
// Add your data interface properties here
}
export interface ${capitalizedName}Options {
// Add your options interface properties here
}
// Add more type definitions as needed
`;
}
generateFeatureSchemas(featureName, capitalizedName) {
return `// Validation schemas for ${featureName} feature
import Joi from 'joi';
export const ${featureName}ValidationSchema = Joi.object({
// Add your validation schema here
// Example:
// name: Joi.string().required(),
// email: Joi.string().email().required(),
});
export const ${featureName}UpdateSchema = Joi.object({
// Add your update validation schema here
});
// Add more validation schemas as needed
`;
}
async generateFeatureCore(featureName) {
const capitalizedName = this.capitalizeFirst(featureName);
switch (featureName) {
case 'auth':
return `import { Router } from 'express';
import { AuthController } from './controllers';
import { AuthService } from './services';
import { AuthConfig } from './config';
export class ${capitalizedName}Feature {
private router: Router;
private controller: AuthController;
private service: AuthService;
private config: AuthConfig;
constructor() {
this.router = Router();
this.config = new AuthConfig();
this.service = new AuthService();
this.controller = new AuthController(this.service);
this.setupRoutes();
}
private setupRoutes(): void {
// Authentication routes
this.router.post('/login', this.controller.login.bind(this.controller));
this.router.post('/register', this.controller.register.bind(this.controller));
this.router.post('/logout', this.controller.logout.bind(this.controller));
this.router.post('/refresh', this.controller.refreshToken.bind(this.controller));
this.router.post('/forgot-password', this.controller.forgotPassword.bind(this.controller));
this.router.post('/reset-password', this.controller.resetPassword.bind(this.controller));
this.router.post('/verify-email', this.controller.verifyEmail.bind(this.controller));
}
getRouter(): Router {
return this.router;
}
getService(): AuthService {
return this.service;
}
getConfig(): AuthConfig {
return this.config;
}
}`;
case 'email':
return `import { Router } from 'express';
import { EmailController } from './controllers';
import { EmailService } from './services';
import { EmailConfig } from './config';
export class ${capitalizedName}Feature {
private router: Router;
private controller: EmailController;
private service: EmailService;
private config: EmailConfig;
constructor() {
this.router = Router();
this.config = new EmailConfig();
this.service = new EmailService(this.config);
this.controller = new EmailController(this.service);
this.setupRoutes();
}
private setupRoutes(): void {
// Email routes
this.router.post('/send', this.controller.sendEmail.bind(this.controller));
this.router.post('/send-template', this.controller.sendTemplateEmail.bind(this.controller));
this.router.get('/templates', this.controller.getTemplates.bind(this.controller));
this.router.post('/templates', this.controller.createTemplate.bind(this.controller));
}
getRouter(): Router {
return this.router;
}
getService(): EmailService {
return this.service;
}
getConfig(): EmailConfig {
return this.config;
}
}`;
case 'socket':
return `import { Server as SocketIOServer } from 'socket.io';
import { SocketController } from './controllers';
import { SocketService } from './services';
import { SocketConfig } from './config';
export class ${capitalizedName}Feature {
private io: SocketIOServer;
private controller: SocketController;
private service: SocketService;
private config: SocketConfig;
constructor(io: SocketIOServer) {
this.io = io;
this.config = new SocketConfig();
this.service = new SocketService(this.io, this.config);
this.controller = new SocketController(this.service);
this.setupEvents();
}
private setupEvents(): void {
this.io.on('connection', (socket) => {
this.controller.handleConnection(socket);
// Register event handlers
socket.on('join-room', this.controller.handleJoinRoom.bind(this.controller, socket));
socket.on('leave-room', this.controller.handleLeaveRoom.bind(this.controller, socket));
socket.on('send-message', this.controller.handleSendMessage.bind(this.controller, socket));
socket.on('disconnect', this.controller.handleDisconnect.bind(this.controller, socket));
});
}
getService(): SocketService {
return this.service;
}
getConfig(): SocketConfig {
return this.config;
}
}`;
case 'cron':
return `import { CronController } from './controllers';
import { CronService } from './services';
import { CronConfig } from './config';
export class ${capitalizedName}Feature {
private controller: CronController;
private service: CronService;
private config: CronConfig;
constructor() {
this.config = new CronConfig();
this.service = new CronService(this.config);
this.controller = new CronController(this.service);
this.initializeJobs();
}
private initializeJobs(): void {
// Initialize scheduled jobs
this.service.initializeJobs();
}
getService(): CronService {
return this.service;
}
getConfig(): CronConfig {
return this.config;
}
// Method to manually trigger jobs
async triggerJob(jobName: string): Promise<void> {
await this.service.triggerJob(jobName);
}
}`;
case 'audit':
return `import { Router } from 'express';
import { AuditController } from './controllers';
import { AuditService } from './services';
import { AuditConfig } from './config';
export class ${capitalizedName}Feature {
private router: Router;
private controller: AuditController;
private service: AuditService;
private config: AuditConfig;
constructor() {
this.router = Router();
this.config = new AuditConfig();
this.service = new AuditService(this.config);
this.controller = new AuditController(this.service);
this.setupRoutes();
}
private setupRoutes(): void {
// Audit routes
this.router.get('/logs', this.controller.getLogs.bind(this.controller));
this.router.post('/logs', this.controller.createLog.bind(this.controller));
this.router.get('/logs/:id', this.controller.getLogById.bind(this.controller));
this.router.delete('/logs/:id', this.controller.deleteLog.bind(this.controller));
}
getRouter(): Router {
return this.router;
}
getService(): AuditService {
return this.service;
}
getConfig(): AuditConfig {
return this.config;
}
// Method to log audit events
async logEvent(event: any): Promise<void> {
await this.service.logEvent(event);
}
}`;
default:
return `import { Router } from 'express';
import { ${capitalizedName}Controller } from './controllers';
import { ${capitalizedName}Service } from './services';
import { ${capitalizedName}Config } from './config';
export class ${capitalizedName}Feature {
private router: Router;
private controller: ${capitalizedName}Controller;
private service: ${capitalizedName}Service;
private config: ${capitalizedName}Config;
constructor() {
this.router = Router();
this.config = new ${capitalizedName}Config();
this.service = new ${capitalizedName}Service(this.config);
this.controller = new ${capitalizedName}Controller(this.service);
this.setupRoutes();
}
private setupRoutes(): void {
// Add your feature routes here
this.router.get('/', this.controller.index.bind(this.controller));
}
getRouter(): Router {
return this.router;
}
getService(): ${capitalizedName}Service {
return this.service;
}
getConfig(): ${capitalizedName}Config {
return this.config;
}
}`;
}
}
generateFeatureConfig(featureName) {
const capitalizedName = this.capitalizeFirst(featureName);
return `export class ${capitalizedName}Config {
constructor() {
// Initialize configuration from environment variables
this.loadFromEnv();
}
private loadFromEnv(): void {
// Load configuration from environment variables
// Override this method to add specific configuration loading
}
// Add getter methods for configuration values
get enabled(): boolean {
return process.env.${featureName.toUpperCase()}_ENABLED === 'true';
}
}`;
}
async trackPathRefinements(projectPath, featureName, type) {
const refinementsPath = path.join(projectPath, '.diagramers-refinements.json');
let refinements = {};
if (await fs.pathExists(refinementsPath)) {
refinements = JSON.parse(await fs.readFile(refinementsPath, 'utf8'));
}
if (!refinements.paths) {
refinements.paths = [];
}
refinements.paths.push({
type: type,
name: featureName,
path: `src/features/${featureName}`,
addedAt: new Date().toISOString(),
version: '1.0.0'
});
await fs.writeFile(refinementsPath, JSON.stringify(refinements, null, 2));
}
async createModuleStructure(projectPath, moduleName, options) {
const modulePath = path.join(projectPath, 'src', 'modules', moduleName);
// Convert module name to valid TypeScript identifiers
const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
const moduleNameCapitalized = moduleNameCamelCase.charAt(0).toUpperCase() + moduleNameCamelCase.slice(1);
// Create module directory structure
const structure = {
'entities': 'Data models and interfaces',
'schemas': 'Database schemas and validation',
'services': 'Business logic and data access',
'controllers': 'HTTP request handlers',
'routes': 'API route definitions'
};
for (const [dir, description] of Object.entries(structure)) {
const dirPath = path.join(modulePath, dir);
await fs.ensureDir(dirPath);
// Create index file with correct file extensions - use camelCase for file names
let fileName = '';
switch (dir) {
case 'entities':
fileName = `${moduleNameCamelCase}.entity`;
break;
case 'schemas':
fileName = `${moduleNameCamelCase}.schema`;
break;
case 'services':
fileName = `${moduleNameCamelCase}.service`;
break;
case 'controllers':
fileName = `${moduleNameCamelCase}.controller`;
break;
case 'routes':
fileName = `${moduleNameCamelCase}.routes`;
break;
default:
fileName = `${moduleNameCamelCase}.${dir.slice(0, -1)}`;
}
const indexContent = `// ${description} for ${moduleName} module
export * from './${fileName}';
`;
await fs.writeFile(path.join(dirPath, 'index.ts'), indexContent);
}
// Create main entity
await this.createEntity(modulePath, moduleNameCamelCase, options);
// Create schema
await this.createSchema(modulePath, moduleNameCamelCase, options);
// Create service
await this.createService(modulePath, moduleNameCamelCase, options);
// Create controller
await this.createController(modulePath, moduleNameCamelCase, options);
// Create routes
await this.createRoutes(modulePath, moduleNameCamelCase, options);
}
async createEntity(modulePath, moduleNameCamelCase, options) {
// Convert module name to valid TypeScript identifiers
const moduleNameCapitalized = moduleNameCamelCase.charAt(0).toUpperCase() + moduleNameCamelCase.slice(1);
const entityContent = `import { BaseEntity } from '../../../shared/types/base-entity';
export interface ${moduleNameCapitalized} extends BaseEntity {
_id?: string;
name: string;
description?: string;
}
export interface Create${moduleNameCapitalized}Data {
name: string;
description?: string;
}
export interface Update${moduleNameCapitalized}Data {
name?: string;
description?: string;
}`;
await fs.writeFile(path.join(modulePath, 'entities', `${moduleNameCamelCase}.entity.ts`), entityContent);
}
async createSchema(modulePath, moduleNameCamelCase, options) {
// Convert module name to valid TypeScript identifiers
const moduleNameCapitalized = moduleNameCamelCase.charAt(0).toUpperCase() + moduleNameCamelCase.slice(1);
const schemaContent = `import mongoose, { Document, Schema } from 'mongoose';
import { ${moduleNameCapitalized} } from '../entities/${moduleNameCamelCase}.entity';
export interface I${moduleNameCapitalized}Document extends Omit<${moduleNameCapitalized}, '_id'>, Document {}
const ${moduleNameCapitalized}Schema = new Schema<I${moduleNameCapitalized}Document>({
name: { type: String, required: true },
description: { type: String },
status: { type: Number, default: 1 },
createdAt: { type: Date,