lapeeh 1.0.0
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/.env.example +14 -0
- package/LICENSE +21 -0
- package/bin/index.js +934 -0
- package/doc/en/ARCHITECTURE_GUIDE.md +79 -0
- package/doc/en/CHANGELOG.md +203 -0
- package/doc/en/CHEATSHEET.md +90 -0
- package/doc/en/CLI.md +111 -0
- package/doc/en/CONTRIBUTING.md +119 -0
- package/doc/en/DEPLOYMENT.md +171 -0
- package/doc/en/FAQ.md +69 -0
- package/doc/en/FEATURES.md +99 -0
- package/doc/en/GETTING_STARTED.md +84 -0
- package/doc/en/INTRODUCTION.md +62 -0
- package/doc/en/PACKAGES.md +63 -0
- package/doc/en/PERFORMANCE.md +98 -0
- package/doc/en/ROADMAP.md +104 -0
- package/doc/en/SECURITY.md +95 -0
- package/doc/en/STRUCTURE.md +79 -0
- package/doc/en/TUTORIAL.md +145 -0
- package/doc/id/ARCHITECTURE_GUIDE.md +76 -0
- package/doc/id/CHANGELOG.md +203 -0
- package/doc/id/CHEATSHEET.md +90 -0
- package/doc/id/CLI.md +139 -0
- package/doc/id/CONTRIBUTING.md +119 -0
- package/doc/id/DEPLOYMENT.md +171 -0
- package/doc/id/FAQ.md +69 -0
- package/doc/id/FEATURES.md +169 -0
- package/doc/id/GETTING_STARTED.md +91 -0
- package/doc/id/INTRODUCTION.md +62 -0
- package/doc/id/PACKAGES.md +63 -0
- package/doc/id/PERFORMANCE.md +100 -0
- package/doc/id/ROADMAP.md +107 -0
- package/doc/id/SECURITY.md +94 -0
- package/doc/id/STRUCTURE.md +79 -0
- package/doc/id/TUTORIAL.md +145 -0
- package/docker-compose.yml +24 -0
- package/ecosystem.config.js +17 -0
- package/eslint.config.mjs +26 -0
- package/gitignore.template +30 -0
- package/lib/bootstrap.ts +210 -0
- package/lib/core/realtime.ts +34 -0
- package/lib/core/redis.ts +139 -0
- package/lib/core/serializer.ts +63 -0
- package/lib/core/server.ts +70 -0
- package/lib/core/store.ts +116 -0
- package/lib/middleware/auth.ts +63 -0
- package/lib/middleware/error.ts +50 -0
- package/lib/middleware/multipart.ts +13 -0
- package/lib/middleware/rateLimit.ts +14 -0
- package/lib/middleware/requestLogger.ts +27 -0
- package/lib/middleware/visitor.ts +178 -0
- package/lib/utils/logger.ts +100 -0
- package/lib/utils/pagination.ts +56 -0
- package/lib/utils/response.ts +88 -0
- package/lib/utils/validator.ts +394 -0
- package/nodemon.json +6 -0
- package/package.json +126 -0
- package/readme.md +357 -0
- package/scripts/check-update.js +92 -0
- package/scripts/config-clear.js +45 -0
- package/scripts/generate-jwt-secret.js +38 -0
- package/scripts/init-project.js +84 -0
- package/scripts/make-module.js +89 -0
- package/scripts/release.js +494 -0
- package/scripts/seed-json.js +158 -0
- package/scripts/verify-rbac-functional.js +187 -0
- package/src/config/app.ts +9 -0
- package/src/config/cors.ts +5 -0
- package/src/modules/Auth/auth.controller.ts +519 -0
- package/src/modules/Rbac/rbac.controller.ts +533 -0
- package/src/routes/auth.ts +74 -0
- package/src/routes/index.ts +7 -0
- package/src/routes/rbac.ts +42 -0
- package/storage/logs/.gitkeep +0 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +30 -0
package/readme.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# lapeeh Framework - Modern Node.js & TypeScript API Framework
|
|
2
|
+
|
|
3
|
+
**lapeeh** adalah framework **Node.js** berbasis **Express** dan **TypeScript** yang dirancang untuk kecepatan dan skalabilitas. Menggabungkan fleksibilitas Express dengan struktur solid ala **Laravel** dan **NestJS**, lapeeh memberikan pengalaman development **REST API** yang cepat, terstandarisasi, dan siap produksi.
|
|
4
|
+
|
|
5
|
+
Cocok untuk developer yang mencari **Express boilerplate** dengan fitur lengkap: Authentication, dan Zero-Config Redis.
|
|
6
|
+
|
|
7
|
+
## š Fitur Utama
|
|
8
|
+
|
|
9
|
+
- **Production Ready**: Struktur folder modular (MVC) yang mudah dikembangkan.
|
|
10
|
+
- **TypeScript First**: Full type-safety untuk mengurangi runtime error.
|
|
11
|
+
- **Database Agnostic**: Bebas pilih database dan ORM pilihan Anda.
|
|
12
|
+
- **Standardized Structure**: Controller, Service, dan Route yang terpisah rapi.
|
|
13
|
+
- **Auto CLI Generator**: Buat modul dan controller dengan satu perintah.
|
|
14
|
+
- **Smart Caching**: Otomatis menggunakan Redis jika tersedia, fallback ke in-memory jika tidak.
|
|
15
|
+
- **Secure by Default**: Dilengkapi Helmet, Rate Limiting, CORS, dan JWT Auth.
|
|
16
|
+
- **Robust Validation**: Validasi request otomatis menggunakan Zod.
|
|
17
|
+
- **High Performance**: Mendukung Fast-Serialization (Fastify-style) untuk response JSON super cepat.
|
|
18
|
+
- **Scalable**: Siap untuk deployment Cluster/Load Balancer dengan Redis Store.
|
|
19
|
+
|
|
20
|
+
## š® Roadmap (Rencana Masa Depan)
|
|
21
|
+
|
|
22
|
+
lapeeh Framework akan terus berkembang menjadi solusi Enterprise yang lengkap. Kami memiliki rencana besar untuk fitur-fitur seperti **Job Queues**, **Storage Abstraction (S3)**, **Mailer**, dan **OpenAPI Generator**.
|
|
23
|
+
|
|
24
|
+
Lihat detail rencana pengembangan di **[ROADMAP.md](doc/ROADMAP.md)**.
|
|
25
|
+
|
|
26
|
+
## š¤ Berkontribusi (Open Source)
|
|
27
|
+
|
|
28
|
+
lapeeh adalah proyek Open Source dan kami sangat terbuka untuk kontribusi dari komunitas! Baik itu perbaikan bug, penambahan fitur, atau perbaikan dokumentasi.
|
|
29
|
+
|
|
30
|
+
Ingin ikut berkontribusi? Silakan baca **[Panduan Kontribusi (CONTRIBUTING.md)](doc/CONTRIBUTING.md)** untuk memulai.
|
|
31
|
+
|
|
32
|
+
## š Dokumentasi Lengkap
|
|
33
|
+
|
|
34
|
+
Kami menyusun "Learning Path" agar Anda bisa memahami framework ini dari nol hingga mahir.
|
|
35
|
+
|
|
36
|
+
### š£ Level 1: Pemula (Wajib Baca)
|
|
37
|
+
|
|
38
|
+
- **[Pengenalan Framework](doc/INTRODUCTION.md)**: Mengapa framework ini ada? Apa bedanya dengan yang lain?
|
|
39
|
+
- **[Getting Started](doc/GETTING_STARTED.md)**: Instalasi dan setup awal.
|
|
40
|
+
- **[Bedah Struktur Folder](doc/STRUCTURE.md)**: Pahami fungsi setiap file dan direktori.
|
|
41
|
+
- **[Referensi Package](doc/PACKAGES.md)**: Penjelasan kegunaan setiap library yang terinstall.
|
|
42
|
+
- **[Cheatsheet (Contekan)](doc/CHEATSHEET.md)**: Daftar perintah & kode cepat.
|
|
43
|
+
|
|
44
|
+
### šØ Level 2: Membangun Aplikasi
|
|
45
|
+
|
|
46
|
+
- **[CLI Tools](doc/CLI.md)**: Percepat kerja dengan generator kode (`make:module`, dll).
|
|
47
|
+
- **[Tutorial Studi Kasus](doc/TUTORIAL.md)**: Bikin API "Perpustakaan" dari nol sampai jadi.
|
|
48
|
+
- **[Fitur & Konsep Inti](doc/FEATURES.md)**: Validasi, Auth, RBAC, dan Serializer.
|
|
49
|
+
|
|
50
|
+
### š Level 3: Mahir & Production
|
|
51
|
+
|
|
52
|
+
- **[Performance Guide](doc/PERFORMANCE.md)**: Tips optimasi high-scale app.
|
|
53
|
+
- **[Security Best Practices](doc/SECURITY.md)**: Panduan mengamankan aplikasi.
|
|
54
|
+
- **[Deployment Guide](doc/DEPLOYMENT.md)**: Cara deploy ke VPS, Docker, atau Cloud.
|
|
55
|
+
- **[FAQ & Troubleshooting](doc/FAQ.md)**: Solusi masalah umum.
|
|
56
|
+
- **[Changelog](doc/CHANGELOG.md)**: Riwayat versi.
|
|
57
|
+
|
|
58
|
+
## š¦ Instalasi & Penggunaan
|
|
59
|
+
|
|
60
|
+
Anda dapat menginstall framework ini menggunakan versi terbaru:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx lapeeh@latest nama-project-anda
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Perintah di atas akan membuat proyek **bersih** (clean slate):
|
|
67
|
+
|
|
68
|
+
- Struktur folder dibuat.
|
|
69
|
+
- Dependensi diinstall.
|
|
70
|
+
- Folder `bin` dan `lib` framework tersembunyi di `node_modules` agar root proyek Anda tetap rapi.
|
|
71
|
+
|
|
72
|
+
### Apa yang terjadi otomatis?
|
|
73
|
+
|
|
74
|
+
1. Struktur project dibuat (Core framework tersembunyi sebagai dependency).
|
|
75
|
+
2. Dependencies diinstall.
|
|
76
|
+
3. **JWT Secret** di-generate otomatis.
|
|
77
|
+
|
|
78
|
+
Masuk ke folder project dan jalankan:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd nama-project-anda
|
|
82
|
+
npm run dev
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
> **Catatan**: Perintah `npm run dev` sekarang menggunakan CLI internal framework (`lapeeh dev`), memberikan pengalaman development yang lebih stabil dan terstandarisasi. Core framework (`bin` dan `lib`) tidak lagi memenuhi root folder Anda, tetapi tersimpan aman sebagai dependency.
|
|
86
|
+
|
|
87
|
+
Server akan berjalan di `http://localhost:8000`.
|
|
88
|
+
|
|
89
|
+
### š”ļø Keamanan & Pembaruan
|
|
90
|
+
|
|
91
|
+
Framework ini didesain dengan memprioritaskan keamanan:
|
|
92
|
+
|
|
93
|
+
- **Zero-Vulnerability Policy**: Kami secara rutin melakukan audit dependensi (`npm audit`) untuk memastikan tidak ada celah keamanan.
|
|
94
|
+
- **Framework-as-Dependency**: Dengan menyembunyikan core logic di `node_modules`, pembaruan framework menjadi lebih mudah (cukup update versi `@lapeeh/lapeeh` di `package.json`) tanpa merusak kode aplikasi Anda.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## š Upgrade Project
|
|
99
|
+
|
|
100
|
+
Jika Anda memiliki project lama yang dibuat dengan versi lapeeh sebelumnya dan ingin memperbarui struktur, scripts, dan konfigurasi ke standar terbaru (termasuk keamanan Redis baru), Anda tidak perlu membuat project ulang.
|
|
101
|
+
|
|
102
|
+
Cukup jalankan perintah ini di dalam folder project Anda:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx lapeeh@latest upgrade
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Perintah ini akan secara otomatis:
|
|
109
|
+
|
|
110
|
+
1. Mengupdate `scripts/` (termasuk generator controller baru).
|
|
111
|
+
2. Mengupdate `docker-compose.yml` (keamanan Redis).
|
|
112
|
+
3. Mengupdate dependencies di `package.json`.
|
|
113
|
+
4. Menambahkan konfigurasi `.vscode` dan `tsconfig` terbaru.
|
|
114
|
+
|
|
115
|
+
> **Catatan:** File `.env` Anda **tidak akan ditimpa**, namun kami akan mengupdate `.env.example` sebagai referensi konfigurasi terbaru.
|
|
116
|
+
|
|
117
|
+
## š§ Zero-Config Redis
|
|
118
|
+
|
|
119
|
+
lapeeh otomatis mendeteksi ketersediaan Redis.
|
|
120
|
+
|
|
121
|
+
1. **Auto-Discovery**: Mencoba terhubung ke Redis URL di `.env` (`REDIS_URL`).
|
|
122
|
+
2. **Smart Fallback**: Jika Redis tidak tersedia atau koneksi gagal, otomatis beralih ke **In-Memory Mock**.
|
|
123
|
+
- Tidak perlu install Redis di local development.
|
|
124
|
+
- Fitur rate-limiting dan caching tetap berjalan (namun data hilang saat restart).
|
|
125
|
+
3. **Production Safety**: Memberikan peringatan log jika berjalan di Production menggunakan Mock.
|
|
126
|
+
|
|
127
|
+
**Force Mock Mode:**
|
|
128
|
+
Anda bisa memaksa menggunakan mock (misal untuk testing) dengan menambahkan env variable:
|
|
129
|
+
|
|
130
|
+
```env
|
|
131
|
+
NO_REDIS=true
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Optional: Menggunakan Real Redis dengan Docker
|
|
135
|
+
|
|
136
|
+
Jika Anda ingin menggunakan Redis yang sebenarnya di local environment, kami telah menyertakan konfigurasi `docker-compose.yml` yang aman (menggunakan ACL).
|
|
137
|
+
|
|
138
|
+
1. Jalankan Redis container:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
docker-compose up -d
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
2. Uncomment konfigurasi Redis di file `.env` Anda:
|
|
145
|
+
|
|
146
|
+
```env
|
|
147
|
+
REDIS_URL="redis://lapeeh:12341234@localhost:6379"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> **Credential Default:**
|
|
151
|
+
>
|
|
152
|
+
> - User: `lapeeh`
|
|
153
|
+
> - Password: `12341234`
|
|
154
|
+
|
|
155
|
+
## š Development Tools
|
|
156
|
+
|
|
157
|
+
API lapeeh menyediakan tools untuk mempercepat development, mirip dengan `artisan` di Laravel.
|
|
158
|
+
|
|
159
|
+
### 1. Membuat Module (Resource)
|
|
160
|
+
|
|
161
|
+
Membuat Controller, Service, dan Route sekaligus.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm run make:module NamaResource
|
|
165
|
+
# Contoh: npm run make:module Product
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Command ini akan membuat:
|
|
169
|
+
|
|
170
|
+
- `src/controllers/product.controller.ts`
|
|
171
|
+
- `src/services/product.service.ts`
|
|
172
|
+
- `src/routes/product.route.ts` (dan otomatis didaftarkan di `src/routes/index.ts` jika memungkinkan)
|
|
173
|
+
|
|
174
|
+
### 2. Membuat Controller
|
|
175
|
+
|
|
176
|
+
Membuat file Controller baru. Gunakan flag `-r` untuk membuat controller lengkap dengan method CRUD (index, show, store, update, destroy).
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npm run make:controller NamaController
|
|
180
|
+
# Contoh Basic: npm run make:controller PaymentController
|
|
181
|
+
|
|
182
|
+
# Contoh Resource (CRUD Lengkap):
|
|
183
|
+
npm run make:controller PaymentController -r
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 3. Database (No-ORM)
|
|
187
|
+
|
|
188
|
+
Since v3.0.0, lapeeh Framework **does not include a default ORM** (like Prisma). We believe in giving you full control over your database stack.
|
|
189
|
+
|
|
190
|
+
You can freely choose to use:
|
|
191
|
+
|
|
192
|
+
- **Prisma** (Manual installation)
|
|
193
|
+
- **TypeORM**
|
|
194
|
+
- **Drizzle ORM**
|
|
195
|
+
- **Mongoose**
|
|
196
|
+
- **Raw SQL** (pg, mysql2)
|
|
197
|
+
|
|
198
|
+
The framework provides a `Validator` class for request validation and a `Serializer` for response formatting, but data persistence is up to you.
|
|
199
|
+
|
|
200
|
+
### 4. Generate JWT Secret
|
|
201
|
+
|
|
202
|
+
Jika Anda perlu me-refresh secret key JWT:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm run generate:jwt
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 5. Maintenance (Clear Config)
|
|
209
|
+
|
|
210
|
+
Membersihkan cache framework, NPM, build artifacts, dan temporary files (sangat berguna jika mengalami isu cache aneh atau ingin reset environment development).
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npm run config:clear
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
- Menghapus `node_modules/.cache`
|
|
217
|
+
- Menghapus `dist/`
|
|
218
|
+
- Menghapus `dump.rdb` (Redis Persistence)
|
|
219
|
+
- Membersihkan `npm cache`
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## š Struktur Folder
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
src/
|
|
227
|
+
āāā controllers/ # Logika Request & Response
|
|
228
|
+
āāā services/ # Business Logic
|
|
229
|
+
āāā routes/ # Definisi Route API
|
|
230
|
+
āāā middleware/ # Auth, Validation, Error Handling
|
|
231
|
+
āāā schema/ # Zod Validation Schemas
|
|
232
|
+
āāā utils/ # Helper Functions
|
|
233
|
+
āāā index.ts # App Entry Point
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## š Lisensi
|
|
237
|
+
|
|
238
|
+
MIT
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## š Deployment Guide
|
|
243
|
+
|
|
244
|
+
### 1) Build
|
|
245
|
+
|
|
246
|
+
- Build: `npm run build`
|
|
247
|
+
- Start (dev): `npm run start`
|
|
248
|
+
- Start (prod): `npm run start:prod`
|
|
249
|
+
|
|
250
|
+
### 2) Production Environment
|
|
251
|
+
|
|
252
|
+
- Pastikan `.env` berisi kredensial production:
|
|
253
|
+
- `JWT_SECRET` (gunakan `npm run generate:jwt` untuk mengganti)
|
|
254
|
+
- Database credentials (sesuai pilihan ORM/DB Anda)
|
|
255
|
+
|
|
256
|
+
### 3) Menjalankan dengan PM2
|
|
257
|
+
|
|
258
|
+
- Install PM2:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
npm i -g pm2
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- Jalankan aplikasi:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
pm2 start dist/src/index.js --name lapeeh-api --time
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- Simpan proses agar auto-start saat reboot:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
pm2 save
|
|
274
|
+
pm2 startup
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- Monitoring:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
pm2 status
|
|
281
|
+
pm2 logs lapeeh-api
|
|
282
|
+
pm2 restart lapeeh-api
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 4) Nginx Reverse Proxy (Recommended)
|
|
286
|
+
|
|
287
|
+
- Buat server block `/etc/nginx/sites-available/lapeeh`:
|
|
288
|
+
|
|
289
|
+
```nginx
|
|
290
|
+
server {
|
|
291
|
+
listen 80;
|
|
292
|
+
server_name example.com;
|
|
293
|
+
|
|
294
|
+
location / {
|
|
295
|
+
proxy_set_header Host $host;
|
|
296
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
297
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
298
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
299
|
+
proxy_pass http://127.0.0.1:8000;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
- Aktifkan:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
sudo ln -s /etc/nginx/sites-available/lapeeh /etc/nginx/sites-enabled/lapeeh
|
|
308
|
+
sudo nginx -t
|
|
309
|
+
sudo systemctl reload nginx
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
- SSL (opsional, Certbot):
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
sudo apt install certbot python3-certbot-nginx -y
|
|
316
|
+
sudo certbot --nginx -d example.com
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 5) Apache 2 Reverse Proxy (Alternatif)
|
|
320
|
+
|
|
321
|
+
- Enable modul proxy:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
sudo a2enmod proxy proxy_http headers
|
|
325
|
+
sudo systemctl reload apache2
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
- Buat vhost `/etc/apache2/sites-available/lapeeh.conf`:
|
|
329
|
+
|
|
330
|
+
```apache
|
|
331
|
+
<VirtualHost *:80>
|
|
332
|
+
ServerName example.com
|
|
333
|
+
ProxyPreserveHost On
|
|
334
|
+
ProxyRequests Off
|
|
335
|
+
<Proxy *>
|
|
336
|
+
Require all granted
|
|
337
|
+
</Proxy>
|
|
338
|
+
ProxyPass / http://127.0.0.1:8000/
|
|
339
|
+
ProxyPassReverse / http://127.0.0.1:8000/
|
|
340
|
+
ErrorLog ${APACHE_LOG_DIR}/lapeeh-error.log
|
|
341
|
+
CustomLog ${APACHE_LOG_DIR}/lapeeh-access.log combined
|
|
342
|
+
</VirtualHost>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
- Aktifkan:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
sudo a2ensite lapeeh.conf
|
|
349
|
+
sudo apachectl configtest
|
|
350
|
+
sudo systemctl reload apache2
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 6) Checklist Produksi
|
|
354
|
+
|
|
355
|
+
- `pm2 status` menunjukkan proses hidup
|
|
356
|
+
- Proxy (Nginx/Apache) menuju port aplikasi (default 8000)
|
|
357
|
+
- `.env` aman dan tidak di-commit ke repository
|
|
@@ -0,0 +1,92 @@
|
|
|
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/lapeeh/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 "lapeeh" di dependencies (project user)
|
|
14
|
+
// Jika tidak ada, fallback ke version package.json (mungkin ini repo framework itu sendiri)
|
|
15
|
+
const currentVersion = packageJson.dependencies?.['lapeeh'] || 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}ā Untuk upgrade jalankan: ā${reset}`);
|
|
86
|
+
console.log(`${fgYellow}ā ${fgCyan}npm install lapeeh@latest${reset}${fgYellow} ā${reset}`);
|
|
87
|
+
console.log(`${fgYellow}ā ā${reset}`);
|
|
88
|
+
console.log(`${fgYellow}āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā${reset}`);
|
|
89
|
+
console.log('\n');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
checkForUpdates();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
const rootDir = path.join(__dirname, '..');
|
|
6
|
+
|
|
7
|
+
console.log('š§¹ Starting cleanup process...');
|
|
8
|
+
|
|
9
|
+
// 1. Remove dist folder (Build artifacts)
|
|
10
|
+
const distDir = path.join(rootDir, 'dist');
|
|
11
|
+
if (fs.existsSync(distDir)) {
|
|
12
|
+
console.log('šļø Removing dist/ folder...');
|
|
13
|
+
fs.rmSync(distDir, { recursive: true, force: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 2. Remove node_modules/.cache (Framework caches: ts-node, eslint, etc)
|
|
17
|
+
const nmCacheDir = path.join(rootDir, 'node_modules', '.cache');
|
|
18
|
+
if (fs.existsSync(nmCacheDir)) {
|
|
19
|
+
console.log('šļø Removing node_modules/.cache...');
|
|
20
|
+
fs.rmSync(nmCacheDir, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 3. Remove Redis local persistence file (dump.rdb)
|
|
24
|
+
const dumpRdb = path.join(rootDir, 'dump.rdb');
|
|
25
|
+
if (fs.existsSync(dumpRdb)) {
|
|
26
|
+
console.log('šļø Removing dump.rdb (Redis persistence)...');
|
|
27
|
+
fs.unlinkSync(dumpRdb);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 4. Remove Coverage folder (if exists)
|
|
31
|
+
const coverageDir = path.join(rootDir, 'coverage');
|
|
32
|
+
if (fs.existsSync(coverageDir)) {
|
|
33
|
+
console.log('šļø Removing coverage/ folder...');
|
|
34
|
+
fs.rmSync(coverageDir, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 5. Clear NPM Cache
|
|
38
|
+
try {
|
|
39
|
+
console.log('š¦ Clearing NPM cache...');
|
|
40
|
+
execSync('npm cache clean --force', { stdio: 'inherit' });
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.warn('ā ļø Warning: Could not clear NPM cache. You might need admin privileges.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log('⨠Cleanup complete! Project is fresh.');
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
const envPath = path.join(__dirname, '..', '.env');
|
|
6
|
+
|
|
7
|
+
function generateSecret() {
|
|
8
|
+
return crypto.randomBytes(64).toString('hex');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const secret = generateSecret();
|
|
13
|
+
let envContent = '';
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(envPath)) {
|
|
16
|
+
envContent = fs.readFileSync(envPath, 'utf8');
|
|
17
|
+
} else {
|
|
18
|
+
console.log('.env file not found, creating one...');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if JWT_SECRET exists
|
|
22
|
+
if (envContent.match(/^JWT_SECRET=/m)) {
|
|
23
|
+
envContent = envContent.replace(/^JWT_SECRET=.*/m, `JWT_SECRET="${secret}"`);
|
|
24
|
+
} else {
|
|
25
|
+
// Ensure there is a newline before appending if the file is not empty
|
|
26
|
+
if (envContent && !envContent.endsWith('\n')) {
|
|
27
|
+
envContent += '\n';
|
|
28
|
+
}
|
|
29
|
+
envContent += `JWT_SECRET="${secret}"\n`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fs.writeFileSync(envPath, envContent);
|
|
33
|
+
console.log('ā
JWT Secret generated and updated in .env file.');
|
|
34
|
+
console.log('š New Secret has been set.');
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('ā Error updating .env file:', error);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
|
|
10
|
+
const rl = readline.createInterface({
|
|
11
|
+
input: process.stdin,
|
|
12
|
+
output: process.stdout,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const ask = (query, defaultVal) => {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
rl.question(`${query} ${defaultVal ? `[${defaultVal}]` : ""}: `, (answer) => {
|
|
18
|
+
resolve(answer.trim() || defaultVal);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const selectOption = async (query, options) => {
|
|
24
|
+
console.log(query);
|
|
25
|
+
options.forEach((opt, idx) => {
|
|
26
|
+
console.log(` [${opt.key}] ${opt.label}`);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
while (true) {
|
|
30
|
+
const answer = await ask(">", options[0].key); // Default to first option
|
|
31
|
+
const selected = options.find(o => o.key.toLowerCase() === answer.toLowerCase());
|
|
32
|
+
if (selected) return selected;
|
|
33
|
+
|
|
34
|
+
// Check if user entered the full name or label
|
|
35
|
+
const byLabel = options.find(o => o.label.toLowerCase().includes(answer.toLowerCase()));
|
|
36
|
+
if (byLabel) return byLabel;
|
|
37
|
+
|
|
38
|
+
console.log("Pilihan tidak valid. Silakan coba lagi.");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
(async () => {
|
|
43
|
+
console.log("š Starting project initialization...");
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// Close readline as we are done with input
|
|
47
|
+
rl.close();
|
|
48
|
+
|
|
49
|
+
// 1. Setup .env
|
|
50
|
+
console.log("\nš Setting up .env...");
|
|
51
|
+
let envContent = "";
|
|
52
|
+
if (fs.existsSync(envExample)) {
|
|
53
|
+
envContent = fs.readFileSync(envExample, "utf8");
|
|
54
|
+
} else {
|
|
55
|
+
// Fallback minimal env if example missing
|
|
56
|
+
envContent = `PORT=8000\nJWT_SECRET="replace_this"\n`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fs.writeFileSync(envFile, envContent);
|
|
60
|
+
console.log("ā
.env created.");
|
|
61
|
+
|
|
62
|
+
// 3. Install dependencies
|
|
63
|
+
console.log("\nš¦ Installing dependencies...");
|
|
64
|
+
execSync("npm install", { stdio: "inherit", cwd: rootDir });
|
|
65
|
+
|
|
66
|
+
// 5. Generate JWT Secret
|
|
67
|
+
console.log("\nš Generating JWT Secret...");
|
|
68
|
+
try {
|
|
69
|
+
execSync("node scripts/generate-jwt-secret.js", {
|
|
70
|
+
stdio: "inherit",
|
|
71
|
+
cwd: rootDir,
|
|
72
|
+
});
|
|
73
|
+
} catch (e) {
|
|
74
|
+
console.warn("ā ļø Failed to generate JWT secret automatically.");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log("\nā
Setup complete! You can now run:");
|
|
78
|
+
console.log(" npm run dev");
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error("\nā Setup failed:", error.message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
})();
|