node-project-gen 0.0.4

⚡ Blazing fast, architecture-aware Node.js backend generator. Scaffold production-ready projects in under 1 second.
use crate::cli::*;
use crate::generator::ProjectConfig;
use std::path::PathBuf;

pub fn generate(
    project_path: &PathBuf,
    config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    generate_env_config(project_path, config)?;
    generate_env_file(project_path, config)?;
    generate_swagger_config(project_path, config)?;
    Ok(())
}

fn generate_env_config(
    project_path: &PathBuf,
    config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    let db_env = if let Some(db) = config.db {
        match db {
            Database::Mongodb => r#"
  databaseUrl: process.env.DATABASE_URL || 'mongodb://localhost:27017/mydb',"#,
            Database::Postgres => r#"
  databaseUrl: process.env.DATABASE_URL || 'postgresql://user:password@localhost:5432/mydb',"#,
            Database::Mysql => r#"
  databaseUrl: process.env.DATABASE_URL || 'mysql://user:password@localhost:3306/mydb',"#,
            Database::Sqlite => r#"
  databaseUrl: process.env.DATABASE_URL || 'file:./dev.db',"#,
        }
    } else {
        ""
    };

    let jwt_env = if matches!(config.auth, Some(AuthStrategy::Jwt) | Some(AuthStrategy::Oauth)) {
        r#"
  jwtSecret: process.env.JWT_SECRET || 'your-super-secret-key-change-in-production',
  jwtExpiresIn: process.env.JWT_EXPIRES_IN || '1h',
  jwtRefreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',"#
    } else {
        ""
    };

    let content = format!(
        r#"import dotenv from 'dotenv';

dotenv.config();

export const config = {{
  // Server
  port: parseInt(process.env.PORT || '3000', 10),
  nodeEnv: process.env.NODE_ENV || 'development',
  corsOrigin: process.env.CORS_ORIGIN || '*',

  // API
  apiPrefix: process.env.API_PREFIX || '/api/v1',
  apiVersion: process.env.API_VERSION || '1.0.0',{db_env}{jwt_env}

  // Logging
  logLevel: process.env.LOG_LEVEL || 'info',

  // Rate Limiting
  rateLimitMax: parseInt(process.env.RATE_LIMIT_MAX || '100', 10),
  rateLimitWindowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000', 10),
}};
"#,
        db_env = db_env,
        jwt_env = jwt_env,
    );

    std::fs::write(
        project_path.join(format!("src/config/env.config.{}", config.get_ext())),
        content,
    )?;

    Ok(())
}

fn generate_env_file(
    project_path: &PathBuf,
    config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut env_content = String::from(
        r#"# Server Configuration
NODE_ENV=development
PORT=3000
CORS_ORIGIN=*

# API Configuration
API_PREFIX=/api/v1

# Logging
LOG_LEVEL=info

# Rate Limiting
RATE_LIMIT_MAX=100
RATE_LIMIT_WINDOW_MS=900000
"#,
    );

    if let Some(db) = config.db {
        env_content.push_str("\n# Database\n");
        match db {
            Database::Mongodb => {
                env_content.push_str("DATABASE_URL=mongodb://localhost:27017/mydb\n");
            }
            Database::Postgres => {
                env_content.push_str(
                    "DATABASE_URL=postgresql://user:password@localhost:5432/mydb\n",
                );
            }
            Database::Mysql => {
                env_content.push_str("DATABASE_URL=mysql://user:password@localhost:3306/mydb\n");
            }
            Database::Sqlite => {
                env_content.push_str("DATABASE_URL=file:./dev.db\n");
            }
        }
    }

    if matches!(config.auth, Some(AuthStrategy::Jwt) | Some(AuthStrategy::Oauth)) {
        env_content.push_str(
            r#"
# Authentication
JWT_SECRET=your-super-secret-key-change-in-production
JWT_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=7d
"#,
        );
    }

    if matches!(config.auth, Some(AuthStrategy::Session)) {
        env_content.push_str(
            r#"
# Session
SESSION_SECRET=your-session-secret-change-in-production
SESSION_MAX_AGE=86400000
REDIS_URL=redis://localhost:6379
"#,
        );
    }

    if matches!(config.auth, Some(AuthStrategy::Firebase)) {
        env_content.push_str(
            r#"
# Firebase
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_PRIVATE_KEY=your-private-key
FIREBASE_CLIENT_EMAIL=your-client-email
"#,
        );
    }

    if matches!(config.auth, Some(AuthStrategy::Oauth)) {
        env_content.push_str(
            r#"
# OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_CALLBACK_URL=http://localhost:3000/api/v1/auth/google/callback
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GITHUB_CALLBACK_URL=http://localhost:3000/api/v1/auth/github/callback
"#,
        );
    }

    std::fs::write(project_path.join(".env.example"), &env_content)?;
    std::fs::write(project_path.join(".env"), &env_content)?;

    Ok(())
}

fn generate_swagger_config(
    project_path: &PathBuf,
    config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    if config.framework == Framework::Express {
        let content = format!(
            r#"import swaggerJSDoc from 'swagger-jsdoc';

const swaggerDefinition = {{
  openapi: '3.0.0',
  info: {{
    title: '{name} API',
    version: '1.0.0',
    description: 'API documentation for {name}',
    contact: {{
      name: 'API Support',
    }},
  }},
  servers: [
    {{
      url: 'http://localhost:3000/api/v1',
      description: 'Development server',
    }},
  ],
  components: {{
    securitySchemes: {{
      bearerAuth: {{
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
      }},
    }},
  }},
}};

const options: swaggerJSDoc.Options = {{
  swaggerDefinition,
  apis: ['./src/**/*.ts'],
}};

export const swaggerSpec = swaggerJSDoc(options);
"#,
            name = config.name,
        );

        std::fs::write(
            project_path.join(format!("src/config/swagger.config.{}", config.get_ext())),
            content,
        )?;
    }

    Ok(())
}