create-lumina-project 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import fs from 'fs-extra';
4
4
  import path from 'path';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-lumina-project",
3
- "version": "1.0.0",
4
- "author": "Ghost",
3
+ "version": "1.0.1",
4
+ "author": "Glenson Ansin",
5
5
  "description": "Scaffold a modern Node.js API with Lumina architecture.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -1,15 +1,25 @@
1
+ # Database Configuration
2
+ # Supported dialects: mysql, postgres, mariadb, sqlite
1
3
  DB_DIALECT=mysql
2
4
  DB_HOST=127.0.0.1
3
5
  DB_PORT=3306
4
6
  DB_DATABASE=lumina
7
+ DB_DATABASE_TEST=lumina_test
5
8
  DB_USERNAME=root
6
- DB_PASSWORD=
9
+ DB_PASSWORD=your_password
10
+ #true or false
11
+ DB_SSL=
7
12
 
8
- JWT_SECRET=secret_key
13
+ # JWT Configuration
14
+ JWT_SECRET=your_super_secret_key_here
9
15
  JWT_EXPIRES_IN=1d
10
16
 
11
- LOG_LEVEL=
12
-
17
+ # Application
13
18
  NODE_ENV=development
19
+ APP_PORT=3000
20
+
21
+ # Logging
22
+ LOG_LEVEL=info
14
23
 
15
- MAINTENANCE_SECRET=secret_key
24
+ # Maintenance Mode
25
+ MAINTENANCE_SECRET=secret_key_for_bypass
@@ -17,10 +17,10 @@
17
17
  "create:model": "tsx scripts/create-model.ts",
18
18
  "create:migration": "tsx scripts/create-migration.ts",
19
19
  "create:controller": "tsx scripts/create-controller.ts",
20
- "create:factory": "tsx scripts/create-factory.ts"
20
+ "create:factory": "tsx scripts/create-factory.ts",
21
+ "key:generate": "tsx scripts/generate-keys.ts"
21
22
  },
22
- "author": "Ghost",
23
- "license": "ISC",
23
+ "private": true,
24
24
  "type": "module",
25
25
  "dependencies": {
26
26
  "@faker-js/faker": "^10.2.0",
@@ -35,6 +35,8 @@
35
35
  "multer": "^2.0.2",
36
36
  "mysql2": "^3.16.2",
37
37
  "nodemon": "^3.1.11",
38
+ "pg": "^8.18.0",
39
+ "pg-hstore": "^2.3.4",
38
40
  "sequelize": "^6.37.7",
39
41
  "sequelize-cli": "^6.6.5",
40
42
  "umzug": "^3.8.2",
@@ -0,0 +1,78 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import crypto from 'crypto';
4
+ import Logger from '../src/utils/Logger.js';
5
+
6
+ class KeyGenerator {
7
+ private envPath: string;
8
+ private examplePath: string;
9
+
10
+ constructor() {
11
+ this.envPath = path.join(process.cwd(), '.env');
12
+ this.examplePath = path.join(process.cwd(), '.env.example');
13
+ }
14
+
15
+ public run(): void {
16
+ try {
17
+ this.ensureEnvExists();
18
+ this.updateKeys();
19
+ Logger.info('Application keys generated and saved to .env successfully.');
20
+ } catch (error) {
21
+ Logger.error('Failed to generate keys:', error);
22
+ process.exit(1);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Generates a random 64-character hex string.
28
+ */
29
+ private generateSecret(): string {
30
+ return crypto.randomBytes(32).toString('hex');
31
+ }
32
+
33
+ /**
34
+ * Checks if .env exists. If not, copies .env.example.
35
+ */
36
+ private ensureEnvExists(): void {
37
+ if (!fs.existsSync(this.envPath)) {
38
+ if (fs.existsSync(this.examplePath)) {
39
+ fs.copyFileSync(this.examplePath, this.envPath);
40
+ Logger.info('Created .env file from .env.example');
41
+ } else {
42
+ throw new Error('.env.example file not found. Cannot generate .env.');
43
+ }
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Reads .env, replaces specific keys, and writes it back.
49
+ */
50
+ private updateKeys(): void {
51
+ let envContent = fs.readFileSync(this.envPath, 'utf-8');
52
+
53
+ // 1. Update JWT_SECRET
54
+ envContent = this.replaceOrAppend(envContent, 'JWT_SECRET', this.generateSecret());
55
+
56
+ // 2. Update MAINTENANCE_SECRET
57
+ envContent = this.replaceOrAppend(envContent, 'MAINTENANCE_SECRET', this.generateSecret());
58
+
59
+ fs.writeFileSync(this.envPath, envContent);
60
+ }
61
+
62
+ /**
63
+ * Helper to replace an existing key's value or append if missing.
64
+ */
65
+ private replaceOrAppend(content: string, key: string, newValue: string): string {
66
+ const regex = new RegExp(`^${key}=.*`, 'm');
67
+
68
+ if (regex.test(content)) {
69
+ // Key exists, replace it
70
+ return content.replace(regex, `${key}=${newValue}`);
71
+ } else {
72
+ // Key missing, append it
73
+ return content + `\n${key}=${newValue}`;
74
+ }
75
+ }
76
+ }
77
+
78
+ new KeyGenerator().run();
@@ -43,6 +43,19 @@ class MigrationRunner {
43
43
  }
44
44
  }
45
45
 
46
+ /**
47
+ * Helper to get SSL config for the temp connection
48
+ */
49
+ private getSSLConfig() {
50
+ const useSSL = process.env.DB_SSL === 'true';
51
+ return useSSL ? {
52
+ ssl: {
53
+ require: true,
54
+ rejectUnauthorized: false
55
+ }
56
+ } : {};
57
+ }
58
+
46
59
  /**
47
60
  * Check if the database exists and create it if not.
48
61
  */
@@ -50,19 +63,36 @@ class MigrationRunner {
50
63
  const env = process.env.NODE_ENV || 'development';
51
64
  const config = (configList as any)[env];
52
65
  const dbName = config.database;
66
+ const isPostgres = config.dialect === 'postgres';
67
+
68
+ const connectionDb = isPostgres ? 'postgres' : '';
53
69
 
54
70
  // Connect to the server, not the specific DB
55
- const tempSequelize = new Sequelize('', config.username, config.password, {
71
+ const tempSequelize = new Sequelize(connectionDb, config.username, config.password, {
56
72
  host: config.host,
73
+ port: config.port,
57
74
  dialect: config.dialect,
58
75
  logging: false,
76
+ dialectOptions: this.getSSLConfig(),
59
77
  });
60
78
 
61
79
  try {
62
- await tempSequelize.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\`;`);
80
+ if (isPostgres) {
81
+ const result = await tempSequelize.query(
82
+ `SELECT 1 FROM pg_database WHERE datname = '${dbName}'`
83
+ );
84
+
85
+ if ((result[0] as any[]).length === 0) {
86
+ Logger.info(`Database "${dbName}" not found. Creating...`);
87
+ await tempSequelize.query(`CREATE DATABASE "${dbName}"`);
88
+ Logger.info(`Database "${dbName}" created.`);
89
+ }
90
+ } else {
91
+ // MySQL fallback
92
+ await tempSequelize.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\`;`);
93
+ }
63
94
  } catch (error) {
64
95
  Logger.error('Failed to create database:', error);
65
- throw error;
66
96
  } finally {
67
97
  await tempSequelize.close();
68
98
  }
@@ -22,6 +22,7 @@ class DatabaseConfig {
22
22
  private getEnvironmentConfig(env: 'development' | 'test' | 'production' = 'development'): Options {
23
23
  const isTest = env === 'test';
24
24
  const isProd = env === 'production';
25
+ const useSSL = process.env.DB_SSL === 'true';
25
26
 
26
27
  return {
27
28
  username: process.env.DB_USERNAME,
@@ -30,6 +31,12 @@ class DatabaseConfig {
30
31
  host: process.env.DB_HOST,
31
32
  port: Number(process.env.DB_PORT) || 3306,
32
33
  dialect: (process.env.DB_DIALECT as any) || 'mysql',
34
+ dialectOptions: useSSL ? {
35
+ ssl: {
36
+ require: true,
37
+ rejectUnauthorized: false
38
+ }
39
+ } : {},
33
40
  // Disable logging in test/production to keep logs clean
34
41
  logging: isProd || isTest ? false : (msg) => Logger.info(`[SQL] ${msg}`),
35
42
  };