lapeh 1.0.3 ā 1.0.5
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/index.js +4 -0
- package/package.json +31 -31
- package/prisma/base.prisma +1 -1
- package/prisma/schema.prisma +3 -1
- package/scripts/check-update.js +89 -0
- package/scripts/init-project.js +166 -71
package/bin/index.js
CHANGED
|
@@ -65,6 +65,10 @@ const packageJsonPath = path.join(projectDir, 'package.json');
|
|
|
65
65
|
const packageJson = require(packageJsonPath);
|
|
66
66
|
|
|
67
67
|
packageJson.name = projectName;
|
|
68
|
+
// Add lapeh framework version to dependencies to track it like react-router
|
|
69
|
+
packageJson.dependencies = packageJson.dependencies || {};
|
|
70
|
+
packageJson.dependencies["lapeh"] = packageJson.version;
|
|
71
|
+
|
|
68
72
|
packageJson.version = '1.0.0';
|
|
69
73
|
packageJson.description = 'Generated by lapeh';
|
|
70
74
|
delete packageJson.bin; // Remove the bin entry from the generated project
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lapeh",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Framework API Express yang siap pakai (Standardized)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"lapeh": "bin/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"dev": "nodemon src/index.ts",
|
|
10
|
+
"dev": "node scripts/check-update.js && nodemon src/index.ts",
|
|
11
11
|
"first": "node scripts/init-project.js",
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"start": "node dist/src/index.js",
|
|
@@ -38,36 +38,36 @@
|
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"type": "commonjs",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@prisma/adapter-mariadb": "
|
|
42
|
-
"@prisma/adapter-pg": "
|
|
43
|
-
"@prisma/client": "
|
|
44
|
-
"bcryptjs": "
|
|
45
|
-
"cors": "
|
|
46
|
-
"dotenv": "
|
|
47
|
-
"express": "
|
|
48
|
-
"express-rate-limit": "
|
|
49
|
-
"helmet": "
|
|
50
|
-
"ioredis": "
|
|
51
|
-
"jsonwebtoken": "
|
|
52
|
-
"multer": "
|
|
53
|
-
"pg": "
|
|
54
|
-
"slugify": "
|
|
55
|
-
"socket.io": "
|
|
56
|
-
"uuid": "
|
|
57
|
-
"zod": "
|
|
41
|
+
"@prisma/adapter-mariadb": "7.2.0",
|
|
42
|
+
"@prisma/adapter-pg": "7.2.0",
|
|
43
|
+
"@prisma/client": "7.2.0",
|
|
44
|
+
"bcryptjs": "3.0.3",
|
|
45
|
+
"cors": "2.8.5",
|
|
46
|
+
"dotenv": "17.2.3",
|
|
47
|
+
"express": "5.2.1",
|
|
48
|
+
"express-rate-limit": "8.2.1",
|
|
49
|
+
"helmet": "8.1.0",
|
|
50
|
+
"ioredis": "5.8.2",
|
|
51
|
+
"jsonwebtoken": "9.0.3",
|
|
52
|
+
"multer": "2.0.2",
|
|
53
|
+
"pg": "8.16.3",
|
|
54
|
+
"slugify": "1.6.6",
|
|
55
|
+
"socket.io": "4.8.3",
|
|
56
|
+
"uuid": "13.0.0",
|
|
57
|
+
"zod": "3.23.8"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@types/bcryptjs": "
|
|
61
|
-
"@types/cors": "
|
|
62
|
-
"@types/express": "
|
|
63
|
-
"@types/jsonwebtoken": "
|
|
64
|
-
"@types/node": "
|
|
65
|
-
"@types/pg": "
|
|
66
|
-
"@types/uuid": "
|
|
67
|
-
"nodemon": "
|
|
68
|
-
"prisma": "
|
|
69
|
-
"ts-node": "
|
|
70
|
-
"ts-node-dev": "
|
|
71
|
-
"typescript": "
|
|
60
|
+
"@types/bcryptjs": "2.4.6",
|
|
61
|
+
"@types/cors": "2.8.19",
|
|
62
|
+
"@types/express": "5.0.6",
|
|
63
|
+
"@types/jsonwebtoken": "9.0.10",
|
|
64
|
+
"@types/node": "25.0.3",
|
|
65
|
+
"@types/pg": "8.16.0",
|
|
66
|
+
"@types/uuid": "10.0.0",
|
|
67
|
+
"nodemon": "3.1.11",
|
|
68
|
+
"prisma": "7.2.0",
|
|
69
|
+
"ts-node": "10.9.2",
|
|
70
|
+
"ts-node-dev": "2.0.0",
|
|
71
|
+
"typescript": "5.9.3"
|
|
72
72
|
}
|
|
73
73
|
}
|
package/prisma/base.prisma
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
// --- KONFIGURASI ---
|
|
6
|
+
// Ganti URL ini dengan URL raw package.json dari repository GitHub/GitLab Anda
|
|
7
|
+
// Contoh: 'https://raw.githubusercontent.com/username/project-name/main/package.json'
|
|
8
|
+
// Jika package dipublish ke NPM, Anda bisa menggunakan registry NPM.
|
|
9
|
+
const REPO_VERSION_URL = 'https://registry.npmjs.org/lapeh/latest';
|
|
10
|
+
const TIMEOUT = 2000; // Timeout 2 detik agar tidak terlalu lama menunggu
|
|
11
|
+
|
|
12
|
+
const packageJson = require('../package.json');
|
|
13
|
+
// Cek apakah ada key "lapeh" di dependencies (project user)
|
|
14
|
+
// Jika tidak ada, fallback ke version package.json (mungkin ini repo framework itu sendiri)
|
|
15
|
+
const currentVersion = packageJson.dependencies?.['lapeh'] || packageJson.version;
|
|
16
|
+
|
|
17
|
+
function checkForUpdates() {
|
|
18
|
+
if (!REPO_VERSION_URL) return;
|
|
19
|
+
|
|
20
|
+
const req = https.get(REPO_VERSION_URL, {
|
|
21
|
+
headers: { 'User-Agent': 'NodeJS Update Checker' },
|
|
22
|
+
timeout: TIMEOUT
|
|
23
|
+
}, (res) => {
|
|
24
|
+
let data = '';
|
|
25
|
+
|
|
26
|
+
if (res.statusCode !== 200) {
|
|
27
|
+
// Silent fail jika URL tidak bisa diakses
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
res.on('data', (chunk) => {
|
|
32
|
+
data += chunk;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
res.on('end', () => {
|
|
36
|
+
try {
|
|
37
|
+
const remoteJson = JSON.parse(data);
|
|
38
|
+
// Jika cek ke NPM, version ada di root object atau 'version'
|
|
39
|
+
// Jika cek ke raw github, structure sama dengan package.json
|
|
40
|
+
const latestVersion = remoteJson.version || remoteJson['dist-tags']?.latest;
|
|
41
|
+
|
|
42
|
+
if (latestVersion && isNewer(latestVersion, currentVersion)) {
|
|
43
|
+
showUpdateMessage(latestVersion, currentVersion);
|
|
44
|
+
}
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// Ignore parsing errors
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
req.on('error', (e) => {
|
|
52
|
+
// Ignore network errors
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
req.end();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isNewer(latest, current) {
|
|
59
|
+
const lParts = latest.split('.').map(Number);
|
|
60
|
+
const cParts = current.split('.').map(Number);
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < 3; i++) {
|
|
63
|
+
if (lParts[i] > cParts[i]) return true;
|
|
64
|
+
if (lParts[i] < cParts[i]) return false;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function showUpdateMessage(latest, current) {
|
|
70
|
+
const reset = "\x1b[0m";
|
|
71
|
+
const bright = "\x1b[1m";
|
|
72
|
+
const fgYellow = "\x1b[33m";
|
|
73
|
+
const fgCyan = "\x1b[36m";
|
|
74
|
+
|
|
75
|
+
console.log('\n');
|
|
76
|
+
console.log(`${fgYellow}āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā${reset}`);
|
|
77
|
+
console.log(`${fgYellow}ā ā${reset}`);
|
|
78
|
+
console.log(`${fgYellow}ā ${bright}UPDATE FRAMEWORK TERSEDIA!${reset}${fgYellow} ā${reset}`);
|
|
79
|
+
console.log(`${fgYellow}ā ā${reset}`);
|
|
80
|
+
console.log(`${fgYellow}ā Versi Lokal : ${fgCyan}${current}${reset}${fgYellow} ā${reset}`);
|
|
81
|
+
console.log(`${fgYellow}ā Versi Terbaru : ${fgCyan}${latest}${reset}${fgYellow} ā${reset}`);
|
|
82
|
+
console.log(`${fgYellow}ā ā${reset}`);
|
|
83
|
+
console.log(`${fgYellow}ā Silakan cek repository untuk melihat perubahan terbaru. ā${reset}`);
|
|
84
|
+
console.log(`${fgYellow}ā ā${reset}`);
|
|
85
|
+
console.log(`${fgYellow}āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā${reset}`);
|
|
86
|
+
console.log('\n');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
checkForUpdates();
|
package/scripts/init-project.js
CHANGED
|
@@ -1,71 +1,166 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const { execSync } = require("child_process");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
console.log(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { execSync } = require("child_process");
|
|
4
|
+
const readline = require("readline");
|
|
5
|
+
|
|
6
|
+
const rootDir = path.join(__dirname, "..");
|
|
7
|
+
const envExample = path.join(rootDir, ".env.example");
|
|
8
|
+
const envFile = path.join(rootDir, ".env");
|
|
9
|
+
const prismaBaseFile = path.join(rootDir, "prisma", "base.prisma");
|
|
10
|
+
|
|
11
|
+
const rl = readline.createInterface({
|
|
12
|
+
input: process.stdin,
|
|
13
|
+
output: process.stdout,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const ask = (query, defaultVal) => {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
rl.question(`${query} ${defaultVal ? `[${defaultVal}]` : ""}: `, (answer) => {
|
|
19
|
+
resolve(answer.trim() || defaultVal);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const selectOption = async (query, options) => {
|
|
25
|
+
console.log(query);
|
|
26
|
+
options.forEach((opt, idx) => {
|
|
27
|
+
console.log(` [${opt.key}] ${opt.label}`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
while (true) {
|
|
31
|
+
const answer = await ask(">", options[0].key); // Default to first option
|
|
32
|
+
const selected = options.find(o => o.key.toLowerCase() === answer.toLowerCase());
|
|
33
|
+
if (selected) return selected;
|
|
34
|
+
|
|
35
|
+
// Check if user entered the full name or label
|
|
36
|
+
const byLabel = options.find(o => o.label.toLowerCase().includes(answer.toLowerCase()));
|
|
37
|
+
if (byLabel) return byLabel;
|
|
38
|
+
|
|
39
|
+
console.log("Pilihan tidak valid. Silakan coba lagi.");
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
(async () => {
|
|
44
|
+
console.log("š Starting project initialization...");
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// --- DATABASE SELECTION ---
|
|
48
|
+
console.log("\n--- Database Configuration ---");
|
|
49
|
+
const dbType = await selectOption("Database apa yang akan digunakan?", [
|
|
50
|
+
{ key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
|
|
51
|
+
{ key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
|
|
52
|
+
{ key: "mariadb", label: "MariaDB", provider: "mysql", defaultPort: "3306" },
|
|
53
|
+
{ key: "sqlite", label: "SQLite", provider: "sqlite", defaultPort: "" },
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
let dbUrl = "";
|
|
57
|
+
let dbProvider = dbType.provider;
|
|
58
|
+
|
|
59
|
+
if (dbType.key === "sqlite") {
|
|
60
|
+
dbUrl = "file:./dev.db";
|
|
61
|
+
} else {
|
|
62
|
+
const host = await ask("Database Host", "localhost");
|
|
63
|
+
const port = await ask("Database Port", dbType.defaultPort);
|
|
64
|
+
const user = await ask("Database User", "root");
|
|
65
|
+
const password = await ask("Database Password", "");
|
|
66
|
+
const dbName = await ask("Database Name", "lapeh");
|
|
67
|
+
|
|
68
|
+
if (dbType.key === "pgsql") {
|
|
69
|
+
dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
|
|
70
|
+
} else {
|
|
71
|
+
dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Close readline as we are done with input
|
|
76
|
+
rl.close();
|
|
77
|
+
|
|
78
|
+
// 1. Setup .env
|
|
79
|
+
console.log("\nš Setting up .env...");
|
|
80
|
+
let envContent = "";
|
|
81
|
+
if (fs.existsSync(envExample)) {
|
|
82
|
+
envContent = fs.readFileSync(envExample, "utf8");
|
|
83
|
+
} else {
|
|
84
|
+
// Fallback minimal env if example missing
|
|
85
|
+
envContent = `PORT=4000\nDATABASE_PROVIDER="postgresql"\nDATABASE_URL=""\nJWT_SECRET="replace_this"\n`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Replace DATABASE_URL and DATABASE_PROVIDER
|
|
89
|
+
// Regex to replace existing values or append if missing (simplified)
|
|
90
|
+
if (envContent.includes("DATABASE_URL=")) {
|
|
91
|
+
envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL="${dbUrl}"`);
|
|
92
|
+
envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL="${dbUrl}"`); // Handle unquoted
|
|
93
|
+
} else {
|
|
94
|
+
envContent += `\nDATABASE_URL="${dbUrl}"`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (envContent.includes("DATABASE_PROVIDER=")) {
|
|
98
|
+
envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
99
|
+
envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
100
|
+
} else {
|
|
101
|
+
envContent += `\nDATABASE_PROVIDER="${dbProvider}"`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fs.writeFileSync(envFile, envContent);
|
|
105
|
+
console.log("ā
.env updated with database configuration.");
|
|
106
|
+
|
|
107
|
+
// 2. Update prisma/base.prisma
|
|
108
|
+
console.log("š Updating prisma/base.prisma...");
|
|
109
|
+
if (fs.existsSync(prismaBaseFile)) {
|
|
110
|
+
let baseContent = fs.readFileSync(prismaBaseFile, "utf8");
|
|
111
|
+
// Replace provider
|
|
112
|
+
baseContent = baseContent.replace(/provider\s*=\s*".*"/, `provider = "${dbProvider}"`);
|
|
113
|
+
fs.writeFileSync(prismaBaseFile, baseContent);
|
|
114
|
+
} else {
|
|
115
|
+
console.warn("ā ļø prisma/base.prisma not found. Skipping.");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 3. Install dependencies
|
|
119
|
+
console.log("\nš¦ Installing dependencies...");
|
|
120
|
+
execSync("npm install", { stdio: "inherit", cwd: rootDir });
|
|
121
|
+
|
|
122
|
+
// 4. Generate JWT Secret
|
|
123
|
+
console.log("\nš Generating JWT Secret...");
|
|
124
|
+
try {
|
|
125
|
+
execSync("node scripts/generate-jwt-secret.js", {
|
|
126
|
+
stdio: "inherit",
|
|
127
|
+
cwd: rootDir,
|
|
128
|
+
});
|
|
129
|
+
} catch (e) {
|
|
130
|
+
console.warn("ā ļø Failed to generate JWT secret automatically.");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 5. Setup Database (Migrate)
|
|
134
|
+
console.log("\nšļø Setting up database...");
|
|
135
|
+
try {
|
|
136
|
+
execSync("node scripts/compile-schema.js", { stdio: "inherit", cwd: rootDir });
|
|
137
|
+
console.log("āļø Generating Prisma Client...");
|
|
138
|
+
execSync("npx prisma generate", { stdio: "inherit", cwd: rootDir });
|
|
139
|
+
|
|
140
|
+
console.log("š Running Migration...");
|
|
141
|
+
execSync("npx prisma migrate dev --name init_setup", { stdio: "inherit", cwd: rootDir });
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.warn(
|
|
144
|
+
'ā ļø Database migration had an issue. Please check your database connection in .env and run "npm run prisma:migrate" manually.'
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 6. Seed Database
|
|
149
|
+
console.log("\nš± Seeding database...");
|
|
150
|
+
try {
|
|
151
|
+
execSync("npm run db:seed", { stdio: "inherit", cwd: rootDir });
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.warn(
|
|
154
|
+
'ā ļø Database seeding had an issue. You might need to run "npm run db:seed" manually.'
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log("\nā
Setup complete! You can now run:");
|
|
159
|
+
console.log(" npm run dev");
|
|
160
|
+
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error("\nā Setup failed:", error.message);
|
|
163
|
+
rl.close();
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
})();
|