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
package/package.json
CHANGED
package/template/.env.example
CHANGED
|
@@ -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
|
-
|
|
13
|
+
# JWT Configuration
|
|
14
|
+
JWT_SECRET=your_super_secret_key_here
|
|
9
15
|
JWT_EXPIRES_IN=1d
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
# Application
|
|
13
18
|
NODE_ENV=development
|
|
19
|
+
APP_PORT=3000
|
|
20
|
+
|
|
21
|
+
# Logging
|
|
22
|
+
LOG_LEVEL=info
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
# Maintenance Mode
|
|
25
|
+
MAINTENANCE_SECRET=secret_key_for_bypass
|
package/template/package.json
CHANGED
|
@@ -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
|
-
"
|
|
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(
|
|
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
|
-
|
|
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
|
};
|