lapeh 3.0.8 ā 3.0.10
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/README.md +5 -5
- package/dist/lib/bootstrap.js +1 -1
- package/doc/en/CHANGELOG.md +8 -0
- package/doc/en/DEPLOYMENT.md +4 -4
- package/doc/en/FAQ.md +15 -4
- package/doc/en/GETTING_STARTED.md +2 -2
- package/doc/en/TUTORIAL.md +22 -11
- package/doc/id/CHANGELOG.md +8 -0
- package/doc/id/DEPLOYMENT.md +4 -4
- package/doc/id/FAQ.md +15 -4
- package/doc/id/GETTING_STARTED.md +2 -2
- package/doc/id/TUTORIAL.md +22 -11
- package/lib/bootstrap.ts +1 -1
- package/package.json +1 -1
- package/scripts/init-project.js +1 -1
- package/scripts/release.js +137 -61
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ npm run dev
|
|
|
84
84
|
|
|
85
85
|
> **Catatan**: Perintah `npm run dev` sekarang menggunakan CLI internal framework (`lapeh 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
86
|
|
|
87
|
-
Server akan berjalan di `http://localhost:
|
|
87
|
+
Server akan berjalan di `http://localhost:8000`.
|
|
88
88
|
|
|
89
89
|
### š”ļø Keamanan & Pembaruan
|
|
90
90
|
|
|
@@ -296,7 +296,7 @@ server {
|
|
|
296
296
|
proxy_set_header X-Real-IP $remote_addr;
|
|
297
297
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
298
298
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
299
|
-
proxy_pass http://127.0.0.1:
|
|
299
|
+
proxy_pass http://127.0.0.1:8000;
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
```
|
|
@@ -335,8 +335,8 @@ sudo systemctl reload apache2
|
|
|
335
335
|
<Proxy *>
|
|
336
336
|
Require all granted
|
|
337
337
|
</Proxy>
|
|
338
|
-
ProxyPass / http://127.0.0.1:
|
|
339
|
-
ProxyPassReverse / http://127.0.0.1:
|
|
338
|
+
ProxyPass / http://127.0.0.1:8000/
|
|
339
|
+
ProxyPassReverse / http://127.0.0.1:8000/
|
|
340
340
|
ErrorLog ${APACHE_LOG_DIR}/lapeh-error.log
|
|
341
341
|
CustomLog ${APACHE_LOG_DIR}/lapeh-access.log combined
|
|
342
342
|
</VirtualHost>
|
|
@@ -353,5 +353,5 @@ sudo systemctl reload apache2
|
|
|
353
353
|
### 6) Checklist Produksi
|
|
354
354
|
|
|
355
355
|
- `pm2 status` menunjukkan proses hidup
|
|
356
|
-
- Proxy (Nginx/Apache) menuju port aplikasi (default
|
|
356
|
+
- Proxy (Nginx/Apache) menuju port aplikasi (default 8000)
|
|
357
357
|
- `.env` aman dan tidak di-commit ke repository
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -137,7 +137,7 @@ async function bootstrap() {
|
|
|
137
137
|
process.exit(1);
|
|
138
138
|
}
|
|
139
139
|
const app = await createApp();
|
|
140
|
-
const port = process.env.PORT ? Number(process.env.PORT) :
|
|
140
|
+
const port = process.env.PORT ? Number(process.env.PORT) : 8000;
|
|
141
141
|
const server = http_1.default.createServer(app);
|
|
142
142
|
(0, realtime_1.initRealtime)(server);
|
|
143
143
|
try {
|
package/doc/en/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
File ini mencatat semua perubahan, pembaruan, dan perbaikan yang dilakukan pada framework Lapeh, diurutkan berdasarkan tanggal.
|
|
4
4
|
|
|
5
|
+
## [2025-12-30] - Tuesday, December 30, 2025 - Smart Blog Automation (v3.0.8)
|
|
6
|
+
|
|
7
|
+
### š¤ Release Script Improvements
|
|
8
|
+
|
|
9
|
+
- **Smart Blog Generation**: The `release.js` script now intelligently reads content from `CHANGELOG.md` (both ID and EN) to generate release blog posts. No more generic "Routine maintenance" content!
|
|
10
|
+
- **Flexible Version Detection**: Improved Regex to detect versions in various changelog header formats.
|
|
11
|
+
- **Bilingual Support**: Ensures release notes are accurately available in both Indonesian and English.
|
|
12
|
+
|
|
5
13
|
## [2025-12-30] - Tuesday, December 30, 2025 - Documentation & CLI (v3.0.7)
|
|
6
14
|
|
|
7
15
|
### š Documentation
|
package/doc/en/DEPLOYMENT.md
CHANGED
|
@@ -107,14 +107,14 @@ You can combine multiple Node.js apps in one `ecosystem.config.js`.
|
|
|
107
107
|
|
|
108
108
|
### 8. Reverse Proxy (Nginx)
|
|
109
109
|
|
|
110
|
-
Do not expose port
|
|
110
|
+
Do not expose port 8000 directly. Use Nginx in front of it.
|
|
111
111
|
Nginx block config:
|
|
112
112
|
|
|
113
113
|
```nginx
|
|
114
114
|
server {
|
|
115
115
|
server_name api.your-domain.com;
|
|
116
116
|
location / {
|
|
117
|
-
proxy_pass http://localhost:
|
|
117
|
+
proxy_pass http://localhost:8000;
|
|
118
118
|
proxy_http_version 1.1;
|
|
119
119
|
proxy_set_header Upgrade $http_upgrade;
|
|
120
120
|
proxy_set_header Connection 'upgrade';
|
|
@@ -141,7 +141,7 @@ RUN npm install
|
|
|
141
141
|
COPY . .
|
|
142
142
|
RUN npm run build
|
|
143
143
|
|
|
144
|
-
EXPOSE
|
|
144
|
+
EXPOSE 8000
|
|
145
145
|
CMD ["npm", "run", "start:prod"]
|
|
146
146
|
```
|
|
147
147
|
|
|
@@ -149,7 +149,7 @@ CMD ["npm", "run", "start:prod"]
|
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
151
|
docker build -t my-lapeh-app .
|
|
152
|
-
docker run -p
|
|
152
|
+
docker run -p 8000:8000 --env-file .env my-lapeh-app
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
## Strategy 3: PaaS (Railway / Render / Vercel)
|
package/doc/en/FAQ.md
CHANGED
|
@@ -5,19 +5,23 @@ Kumpulan pertanyaan umum dan solusi untuk masalah yang sering dihadapi.
|
|
|
5
5
|
## Database
|
|
6
6
|
|
|
7
7
|
### Q: Database apa yang didukung?
|
|
8
|
+
|
|
8
9
|
**A:**
|
|
9
10
|
Lapeh bersifat **Database Agnostic**. Anda bisa menggunakan PostgreSQL, MySQL, MongoDB, atau database apapun. Framework tidak membatasi pilihan database Anda. Kami menyediakan wrapper `db` generik yang bisa Anda konfigurasi sesuai kebutuhan.
|
|
10
11
|
|
|
11
12
|
## Redis & Caching
|
|
12
13
|
|
|
13
14
|
### Q: Saya tidak ingin pakai Redis di local, ribet installnya.
|
|
15
|
+
|
|
14
16
|
**A:**
|
|
15
17
|
Tenang, Lapeh otomatis mendeteksi jika Redis tidak berjalan dan akan menggunakan **In-Memory Mock** (simulasi Redis di RAM). Aplikasi tetap jalan normal tanpa error. Anda tidak perlu config apa-apa.
|
|
16
18
|
|
|
17
19
|
### Q: Bagaimana cara clear cache Redis?
|
|
20
|
+
|
|
18
21
|
**A:**
|
|
19
22
|
Jika punya akses CLI Redis: `redis-cli FLUSHALL`.
|
|
20
23
|
Atau via kode:
|
|
24
|
+
|
|
21
25
|
```typescript
|
|
22
26
|
import { redis } from "@/core/redis";
|
|
23
27
|
await redis.flushall();
|
|
@@ -26,33 +30,40 @@ await redis.flushall();
|
|
|
26
30
|
## Fitur & Kustomisasi
|
|
27
31
|
|
|
28
32
|
### Q: Bagaimana cara handle File Upload?
|
|
33
|
+
|
|
29
34
|
**A:**
|
|
30
35
|
Gunakan `multer`. Framework sudah menyiapkannya di `src/routes/auth.ts` sebagai contoh (upload avatar).
|
|
31
36
|
Anda bisa copy logic konfigurasi `multer` tersebut ke route lain.
|
|
32
37
|
|
|
33
38
|
### Q: Bagaimana cara menambah middleware global baru?
|
|
39
|
+
|
|
34
40
|
**A:**
|
|
35
41
|
Buka `src/core/server.ts`. Di sana ada bagian `// Global Middlewares`. Tambahkan middleware Express Anda di situ:
|
|
42
|
+
|
|
36
43
|
```typescript
|
|
37
44
|
app.use(myCustomMiddleware);
|
|
38
45
|
```
|
|
39
46
|
|
|
40
47
|
### Q: Saya ingin mengubah port server.
|
|
48
|
+
|
|
41
49
|
**A:**
|
|
42
|
-
Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `
|
|
50
|
+
Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `8000`.
|
|
43
51
|
|
|
44
52
|
## Troubleshooting
|
|
45
53
|
|
|
46
|
-
### Q: Error `EADDRINUSE: address already in use :::
|
|
54
|
+
### Q: Error `EADDRINUSE: address already in use :::8000`
|
|
55
|
+
|
|
47
56
|
**A:**
|
|
48
|
-
Artinya port
|
|
57
|
+
Artinya port 8000 sedang dipakai program lain (atau instance Lapeh sebelumnya yang nyangkut).
|
|
49
58
|
**Solusi:**
|
|
59
|
+
|
|
50
60
|
1. Matikan terminal.
|
|
51
61
|
2. Jalankan perintah kill (framework biasanya memberi saran commandnya saat error muncul).
|
|
52
62
|
- Windows: `taskkill /F /IM node.exe` (Hati-hati ini mematikan semua node process).
|
|
53
|
-
- Atau cari PID nya: `netstat -ano | findstr :
|
|
63
|
+
- Atau cari PID nya: `netstat -ano | findstr :8000`.
|
|
54
64
|
|
|
55
65
|
### Q: Error `Unique constraint failed` saat seeding
|
|
66
|
+
|
|
56
67
|
**A:**
|
|
57
68
|
Data yang mau dimasukkan sudah ada (misal email `sa@sa.com`).
|
|
58
69
|
Jalankan `npm run db:reset` untuk menghapus semua data dan mulai dari nol.
|
|
@@ -43,7 +43,7 @@ This script will automatically perform the following:
|
|
|
43
43
|
npm run dev
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
The server will run at `http://localhost:
|
|
46
|
+
The server will run at `http://localhost:8000` (or the port specified in `.env`).
|
|
47
47
|
|
|
48
48
|
## Directory Structure
|
|
49
49
|
|
|
@@ -72,7 +72,7 @@ The `.env` file stores important configurations. Here are the key variables:
|
|
|
72
72
|
|
|
73
73
|
```ini
|
|
74
74
|
# Server
|
|
75
|
-
PORT=
|
|
75
|
+
PORT=8000
|
|
76
76
|
NODE_ENV=development
|
|
77
77
|
|
|
78
78
|
# Security
|
package/doc/en/TUTORIAL.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Tutorial: Building a Library API
|
|
2
2
|
|
|
3
3
|
In this tutorial, we will build a simple "Book Management" feature using Lapeh Framework's core features:
|
|
4
|
+
|
|
4
5
|
1. **CLI** for code generation.
|
|
5
6
|
2. **Validator** for input validation.
|
|
6
7
|
3. **Fast Serialization** for high-performance responses.
|
|
@@ -16,6 +17,7 @@ npm run make:module Book
|
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
The framework will generate:
|
|
20
|
+
|
|
19
21
|
- `src/controllers/bookController.ts`
|
|
20
22
|
- `src/routes/book.ts`
|
|
21
23
|
|
|
@@ -49,16 +51,22 @@ const bookSchema = {
|
|
|
49
51
|
title: { type: "string" },
|
|
50
52
|
author: { type: "string" },
|
|
51
53
|
isbn: { type: "string" },
|
|
52
|
-
stock: { type: "number" }
|
|
53
|
-
}
|
|
54
|
+
stock: { type: "number" },
|
|
55
|
+
},
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
// 2. Create Serializer
|
|
57
|
-
const bookDetailSerializer = getSerializer(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const bookDetailSerializer = getSerializer(
|
|
60
|
+
"book-detail",
|
|
61
|
+
createResponseSchema(bookSchema)
|
|
62
|
+
);
|
|
63
|
+
const bookListSerializer = getSerializer(
|
|
64
|
+
"book-list",
|
|
65
|
+
createResponseSchema({
|
|
66
|
+
type: "array",
|
|
67
|
+
items: bookSchema,
|
|
68
|
+
})
|
|
69
|
+
);
|
|
62
70
|
```
|
|
63
71
|
|
|
64
72
|
### Implement Create (with Validation)
|
|
@@ -70,7 +78,7 @@ export async function createBook(req: Request, res: Response) {
|
|
|
70
78
|
title: "required|string|min:3",
|
|
71
79
|
author: "required|string",
|
|
72
80
|
isbn: "required|string",
|
|
73
|
-
stock: "required|number|min:1"
|
|
81
|
+
stock: "required|number|min:1",
|
|
74
82
|
});
|
|
75
83
|
|
|
76
84
|
if (validator.fails()) {
|
|
@@ -80,7 +88,7 @@ export async function createBook(req: Request, res: Response) {
|
|
|
80
88
|
// 2. Save Data (In-Memory)
|
|
81
89
|
const newBook: Book = {
|
|
82
90
|
id: Date.now().toString(),
|
|
83
|
-
...validator.validated()
|
|
91
|
+
...validator.validated(),
|
|
84
92
|
};
|
|
85
93
|
books.push(newBook);
|
|
86
94
|
|
|
@@ -113,6 +121,7 @@ export default router;
|
|
|
113
121
|
## Step 4: Test Your API
|
|
114
122
|
|
|
115
123
|
Run the server:
|
|
124
|
+
|
|
116
125
|
```bash
|
|
117
126
|
npm run dev
|
|
118
127
|
```
|
|
@@ -120,15 +129,17 @@ npm run dev
|
|
|
120
129
|
Test with curl or Postman:
|
|
121
130
|
|
|
122
131
|
**Create Book:**
|
|
132
|
+
|
|
123
133
|
```bash
|
|
124
|
-
curl -X POST http://localhost:
|
|
134
|
+
curl -X POST http://localhost:8000/api/book \
|
|
125
135
|
-H "Content-Type: application/json" \
|
|
126
136
|
-d '{"title":"Lapeh Guide", "author":"Team", "isbn":"12345", "stock":10}'
|
|
127
137
|
```
|
|
128
138
|
|
|
129
139
|
**List Books:**
|
|
140
|
+
|
|
130
141
|
```bash
|
|
131
|
-
curl http://localhost:
|
|
142
|
+
curl http://localhost:8000/api/book
|
|
132
143
|
```
|
|
133
144
|
|
|
134
145
|
Congratulations! You have built a fast, validated API without getting bogged down in database configuration.
|
package/doc/id/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
File ini mencatat semua perubahan, pembaruan, dan perbaikan yang dilakukan pada framework Lapeh, diurutkan berdasarkan tanggal.
|
|
4
4
|
|
|
5
|
+
## [2025-12-30] - Selasa, 30 Desember 2025 - Otomatisasi Blog Cerdas (v3.0.8)
|
|
6
|
+
|
|
7
|
+
### š¤ Peningkatan Script Rilis
|
|
8
|
+
|
|
9
|
+
- **Smart Blog Generation**: Script `release.js` kini secara cerdas membaca konten dari `CHANGELOG.md` (baik ID maupun EN) untuk membuat postingan blog rilis. Tidak ada lagi konten generik "Routine maintenance"!
|
|
10
|
+
- **Deteksi Versi Fleksibel**: Peningkatan Regex untuk mendeteksi versi dalam format header changelog yang bervariasi.
|
|
11
|
+
- **Dukungan Dwibahasa**: Memastikan rilis notes tersedia dalam Bahasa Indonesia dan Inggris secara akurat.
|
|
12
|
+
|
|
5
13
|
## [2025-12-30] - Selasa, 30 Desember 2025 - Dokumentasi & CLI (v3.0.7)
|
|
6
14
|
|
|
7
15
|
### š Dokumentasi
|
package/doc/id/DEPLOYMENT.md
CHANGED
|
@@ -107,14 +107,14 @@ Anda bisa menggabungkannya dalam satu file `ecosystem.config.js`.
|
|
|
107
107
|
|
|
108
108
|
### 8. Reverse Proxy (Nginx)
|
|
109
109
|
|
|
110
|
-
Jangan expose port
|
|
110
|
+
Jangan expose port 8000 langsung. Gunakan Nginx di depannya.
|
|
111
111
|
Config Nginx block:
|
|
112
112
|
|
|
113
113
|
```nginx
|
|
114
114
|
server {
|
|
115
115
|
server_name api.domain-anda.com;
|
|
116
116
|
location / {
|
|
117
|
-
proxy_pass http://localhost:
|
|
117
|
+
proxy_pass http://localhost:8000;
|
|
118
118
|
proxy_http_version 1.1;
|
|
119
119
|
proxy_set_header Upgrade $http_upgrade;
|
|
120
120
|
proxy_set_header Connection 'upgrade';
|
|
@@ -141,7 +141,7 @@ RUN npm install
|
|
|
141
141
|
COPY . .
|
|
142
142
|
RUN npm run build
|
|
143
143
|
|
|
144
|
-
EXPOSE
|
|
144
|
+
EXPOSE 8000
|
|
145
145
|
CMD ["npm", "run", "start:prod"]
|
|
146
146
|
```
|
|
147
147
|
|
|
@@ -149,7 +149,7 @@ CMD ["npm", "run", "start:prod"]
|
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
151
|
docker build -t my-lapeh-app .
|
|
152
|
-
docker run -p
|
|
152
|
+
docker run -p 8000:8000 --env-file .env my-lapeh-app
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
## Strategi 3: PaaS (Railway / Render / Vercel)
|
package/doc/id/FAQ.md
CHANGED
|
@@ -5,19 +5,23 @@ Kumpulan pertanyaan umum dan solusi untuk masalah yang sering dihadapi.
|
|
|
5
5
|
## Database
|
|
6
6
|
|
|
7
7
|
### Q: Database apa yang didukung?
|
|
8
|
+
|
|
8
9
|
**A:**
|
|
9
10
|
Lapeh bersifat **Database Agnostic**. Anda bisa menggunakan PostgreSQL, MySQL, MongoDB, atau database apapun. Framework tidak membatasi pilihan database Anda. Kami menyediakan wrapper `db` generik yang bisa Anda konfigurasi sesuai kebutuhan.
|
|
10
11
|
|
|
11
12
|
## Redis & Caching
|
|
12
13
|
|
|
13
14
|
### Q: Saya tidak ingin pakai Redis di local, ribet installnya.
|
|
15
|
+
|
|
14
16
|
**A:**
|
|
15
17
|
Tenang, Lapeh otomatis mendeteksi jika Redis tidak berjalan dan akan menggunakan **In-Memory Mock** (simulasi Redis di RAM). Aplikasi tetap jalan normal tanpa error. Anda tidak perlu config apa-apa.
|
|
16
18
|
|
|
17
19
|
### Q: Bagaimana cara clear cache Redis?
|
|
20
|
+
|
|
18
21
|
**A:**
|
|
19
22
|
Jika punya akses CLI Redis: `redis-cli FLUSHALL`.
|
|
20
23
|
Atau via kode:
|
|
24
|
+
|
|
21
25
|
```typescript
|
|
22
26
|
import { redis } from "@/core/redis";
|
|
23
27
|
await redis.flushall();
|
|
@@ -26,33 +30,40 @@ await redis.flushall();
|
|
|
26
30
|
## Fitur & Kustomisasi
|
|
27
31
|
|
|
28
32
|
### Q: Bagaimana cara handle File Upload?
|
|
33
|
+
|
|
29
34
|
**A:**
|
|
30
35
|
Gunakan `multer`. Framework sudah menyiapkannya di `src/routes/auth.ts` sebagai contoh (upload avatar).
|
|
31
36
|
Anda bisa copy logic konfigurasi `multer` tersebut ke route lain.
|
|
32
37
|
|
|
33
38
|
### Q: Bagaimana cara menambah middleware global baru?
|
|
39
|
+
|
|
34
40
|
**A:**
|
|
35
41
|
Buka `src/core/server.ts`. Di sana ada bagian `// Global Middlewares`. Tambahkan middleware Express Anda di situ:
|
|
42
|
+
|
|
36
43
|
```typescript
|
|
37
44
|
app.use(myCustomMiddleware);
|
|
38
45
|
```
|
|
39
46
|
|
|
40
47
|
### Q: Saya ingin mengubah port server.
|
|
48
|
+
|
|
41
49
|
**A:**
|
|
42
|
-
Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `
|
|
50
|
+
Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `8000`.
|
|
43
51
|
|
|
44
52
|
## Troubleshooting
|
|
45
53
|
|
|
46
|
-
### Q: Error `EADDRINUSE: address already in use :::
|
|
54
|
+
### Q: Error `EADDRINUSE: address already in use :::8000`
|
|
55
|
+
|
|
47
56
|
**A:**
|
|
48
|
-
Artinya port
|
|
57
|
+
Artinya port 8000 sedang dipakai program lain (atau instance Lapeh sebelumnya yang nyangkut).
|
|
49
58
|
**Solusi:**
|
|
59
|
+
|
|
50
60
|
1. Matikan terminal.
|
|
51
61
|
2. Jalankan perintah kill (framework biasanya memberi saran commandnya saat error muncul).
|
|
52
62
|
- Windows: `taskkill /F /IM node.exe` (Hati-hati ini mematikan semua node process).
|
|
53
|
-
- Atau cari PID nya: `netstat -ano | findstr :
|
|
63
|
+
- Atau cari PID nya: `netstat -ano | findstr :8000`.
|
|
54
64
|
|
|
55
65
|
### Q: Error `Unique constraint failed` saat seeding
|
|
66
|
+
|
|
56
67
|
**A:**
|
|
57
68
|
Data yang mau dimasukkan sudah ada (misal email `sa@sa.com`).
|
|
58
69
|
Jalankan `npm run db:reset` untuk menghapus semua data dan mulai dari nol.
|
|
@@ -43,7 +43,7 @@ Script ini akan melakukan hal-hal berikut secara otomatis:
|
|
|
43
43
|
npm run dev
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Server akan berjalan di `http://localhost:
|
|
46
|
+
Server akan berjalan di `http://localhost:8000` (atau port yang Anda tentukan di `.env`).
|
|
47
47
|
|
|
48
48
|
## Struktur Direktori
|
|
49
49
|
|
|
@@ -72,7 +72,7 @@ File `.env` menyimpan konfigurasi penting. Berikut adalah variabel kunci:
|
|
|
72
72
|
|
|
73
73
|
```ini
|
|
74
74
|
# Server
|
|
75
|
-
PORT=
|
|
75
|
+
PORT=8000
|
|
76
76
|
NODE_ENV=development
|
|
77
77
|
|
|
78
78
|
# Security
|
package/doc/id/TUTORIAL.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Tutorial: Membangun API Perpustakaan
|
|
2
2
|
|
|
3
3
|
Dalam tutorial ini, kita akan membangun fitur "Manajemen Buku" sederhana menggunakan semua fitur unggulan Lapeh Framework:
|
|
4
|
+
|
|
4
5
|
1. **CLI** untuk generate kode.
|
|
5
6
|
2. **Validator** untuk validasi input.
|
|
6
7
|
3. **Fast Serialization** untuk respon cepat.
|
|
@@ -16,6 +17,7 @@ npm run make:module Book
|
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
Framework akan membuat:
|
|
20
|
+
|
|
19
21
|
- `src/controllers/bookController.ts`
|
|
20
22
|
- `src/routes/book.ts`
|
|
21
23
|
|
|
@@ -49,16 +51,22 @@ const bookSchema = {
|
|
|
49
51
|
title: { type: "string" },
|
|
50
52
|
author: { type: "string" },
|
|
51
53
|
isbn: { type: "string" },
|
|
52
|
-
stock: { type: "number" }
|
|
53
|
-
}
|
|
54
|
+
stock: { type: "number" },
|
|
55
|
+
},
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
// 2. Buat Serializer
|
|
57
|
-
const bookDetailSerializer = getSerializer(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const bookDetailSerializer = getSerializer(
|
|
60
|
+
"book-detail",
|
|
61
|
+
createResponseSchema(bookSchema)
|
|
62
|
+
);
|
|
63
|
+
const bookListSerializer = getSerializer(
|
|
64
|
+
"book-list",
|
|
65
|
+
createResponseSchema({
|
|
66
|
+
type: "array",
|
|
67
|
+
items: bookSchema,
|
|
68
|
+
})
|
|
69
|
+
);
|
|
62
70
|
```
|
|
63
71
|
|
|
64
72
|
### Implementasi Create (dengan Validasi)
|
|
@@ -70,7 +78,7 @@ export async function createBook(req: Request, res: Response) {
|
|
|
70
78
|
title: "required|string|min:3",
|
|
71
79
|
author: "required|string",
|
|
72
80
|
isbn: "required|string",
|
|
73
|
-
stock: "required|number|min:1"
|
|
81
|
+
stock: "required|number|min:1",
|
|
74
82
|
});
|
|
75
83
|
|
|
76
84
|
if (validator.fails()) {
|
|
@@ -80,7 +88,7 @@ export async function createBook(req: Request, res: Response) {
|
|
|
80
88
|
// 2. Simpan Data (In-Memory)
|
|
81
89
|
const newBook: Book = {
|
|
82
90
|
id: Date.now().toString(),
|
|
83
|
-
...validator.validated()
|
|
91
|
+
...validator.validated(),
|
|
84
92
|
};
|
|
85
93
|
books.push(newBook);
|
|
86
94
|
|
|
@@ -113,6 +121,7 @@ export default router;
|
|
|
113
121
|
## Langkah 4: Test API Anda
|
|
114
122
|
|
|
115
123
|
Jalankan server:
|
|
124
|
+
|
|
116
125
|
```bash
|
|
117
126
|
npm run dev
|
|
118
127
|
```
|
|
@@ -120,15 +129,17 @@ npm run dev
|
|
|
120
129
|
Test dengan curl atau Postman:
|
|
121
130
|
|
|
122
131
|
**Buat Buku Baru:**
|
|
132
|
+
|
|
123
133
|
```bash
|
|
124
|
-
curl -X POST http://localhost:
|
|
134
|
+
curl -X POST http://localhost:8000/api/book \
|
|
125
135
|
-H "Content-Type: application/json" \
|
|
126
136
|
-d '{"title":"Panduan Lapeh", "author":"Tim Lapeh", "isbn":"12345", "stock":10}'
|
|
127
137
|
```
|
|
128
138
|
|
|
129
139
|
**List Buku:**
|
|
140
|
+
|
|
130
141
|
```bash
|
|
131
|
-
curl http://localhost:
|
|
142
|
+
curl http://localhost:8000/api/book
|
|
132
143
|
```
|
|
133
144
|
|
|
134
145
|
Selamat! Anda telah membangun API yang cepat dan tervalidasi tanpa terjebak dalam konfigurasi database yang rumit.
|
package/lib/bootstrap.ts
CHANGED
|
@@ -159,7 +159,7 @@ export async function bootstrap() {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
const app = await createApp();
|
|
162
|
-
const port = process.env.PORT ? Number(process.env.PORT) :
|
|
162
|
+
const port = process.env.PORT ? Number(process.env.PORT) : 8000;
|
|
163
163
|
const server = http.createServer(app);
|
|
164
164
|
|
|
165
165
|
initRealtime(server);
|
package/package.json
CHANGED
package/scripts/init-project.js
CHANGED
|
@@ -53,7 +53,7 @@ const selectOption = async (query, options) => {
|
|
|
53
53
|
envContent = fs.readFileSync(envExample, "utf8");
|
|
54
54
|
} else {
|
|
55
55
|
// Fallback minimal env if example missing
|
|
56
|
-
envContent = `PORT=
|
|
56
|
+
envContent = `PORT=8000\nJWT_SECRET="replace_this"\n`;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
fs.writeFileSync(envFile, envContent);
|
package/scripts/release.js
CHANGED
|
@@ -103,21 +103,45 @@ function generateAutoCommitMessage() {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
// Helper to
|
|
107
|
-
function
|
|
106
|
+
// Helper to parse changelog entry with structure
|
|
107
|
+
function parseChangelogEntry(filePath, version) {
|
|
108
108
|
try {
|
|
109
109
|
if (!fs.existsSync(filePath)) return null;
|
|
110
110
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
|
|
112
|
+
// 1. Find the header line to extract Title
|
|
113
|
+
// Regex matches: ## [Date] - Day, Date - Title (vVersion)
|
|
114
|
+
const headerRegex = new RegExp(`## \\[.*?\\] - .*? - (.*?) \\(v${version}\\)`, 'i');
|
|
115
|
+
const headerMatch = content.match(headerRegex);
|
|
116
|
+
const title = headerMatch ? headerMatch[1].trim() : null;
|
|
117
|
+
|
|
118
|
+
// 2. Extract the body
|
|
119
|
+
const bodyRegex = new RegExp(`## \\[.*?\\] - .*?v${version}.*?([\\s\\S]*?)(?=\\n## \\[|$)`, 'i');
|
|
120
|
+
const bodyMatch = content.match(bodyRegex);
|
|
121
|
+
|
|
122
|
+
if (!bodyMatch) return null;
|
|
123
|
+
|
|
124
|
+
let rawBody = bodyMatch[1].trim();
|
|
125
|
+
|
|
126
|
+
// 3. Extract Intro (text before first ###)
|
|
127
|
+
let intro = '';
|
|
128
|
+
let features = rawBody;
|
|
129
|
+
|
|
130
|
+
const firstHeaderIndex = rawBody.indexOf('###');
|
|
131
|
+
if (firstHeaderIndex > 0) {
|
|
132
|
+
intro = rawBody.substring(0, firstHeaderIndex).trim();
|
|
133
|
+
features = rawBody.substring(firstHeaderIndex).trim();
|
|
134
|
+
} else if (firstHeaderIndex === -1 && !rawBody.startsWith('-') && !rawBody.startsWith('*')) {
|
|
135
|
+
// If no subheaders and doesn't start with list, treat as intro
|
|
136
|
+
intro = rawBody;
|
|
137
|
+
features = '';
|
|
119
138
|
}
|
|
120
|
-
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
title,
|
|
142
|
+
intro,
|
|
143
|
+
features
|
|
144
|
+
};
|
|
121
145
|
} catch (e) {
|
|
122
146
|
return null;
|
|
123
147
|
}
|
|
@@ -178,47 +202,79 @@ async function main() {
|
|
|
178
202
|
let blogTitleEN = '';
|
|
179
203
|
|
|
180
204
|
if (createBlog.toLowerCase() === 'y') {
|
|
181
|
-
console.log('\nš¤ Auto-detecting changes from Git & Changelog...');
|
|
182
|
-
const changes = getGitChanges();
|
|
183
205
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
const useAuto = await question('š¤ Auto-generate content from CHANGELOG/Git? (y/n): ');
|
|
207
|
+
|
|
208
|
+
let titleID, descriptionID, introID, featureListID;
|
|
209
|
+
let titleEN, descriptionEN, introEN, featureListEN;
|
|
210
|
+
|
|
211
|
+
if (useAuto.toLowerCase() === 'y') {
|
|
212
|
+
console.log('\nš¤ Auto-detecting changes from Git & Changelog...');
|
|
213
|
+
const changes = getGitChanges();
|
|
214
|
+
|
|
215
|
+
// Try to read from CHANGELOG.md first
|
|
216
|
+
const parsedID = parseChangelogEntry(path.join(rootDir, 'doc/id/CHANGELOG.md'), newVersion);
|
|
217
|
+
const parsedEN = parseChangelogEntry(path.join(rootDir, 'doc/en/CHANGELOG.md'), newVersion);
|
|
218
|
+
|
|
219
|
+
if (parsedID) {
|
|
220
|
+
console.log('ā
Found entry in doc/id/CHANGELOG.md');
|
|
221
|
+
titleID = parsedID.title || `Update Terbaru v${newVersion}`;
|
|
222
|
+
introID = parsedID.intro || `Kami dengan bangga mengumumkan rilis **Lapeh v${newVersion}**. Update ini menghadirkan **${parsedID.title || 'berbagai fitur baru'}** untuk meningkatkan pengalaman pengembangan Anda.`;
|
|
223
|
+
descriptionID = parsedID.intro ? parsedID.intro.split('\n')[0] : `Rilis versi ${newVersion} hadir dengan berbagai pembaruan dan perbaikan.`;
|
|
224
|
+
featureListID = parsedID.features;
|
|
225
|
+
} else {
|
|
226
|
+
console.log('ā ļø No entry in doc/id/CHANGELOG.md, using git logs...');
|
|
227
|
+
titleID = changes.length > 0 ? changes[0] : 'Maintenance Release';
|
|
228
|
+
descriptionID = changes.length > 0 ? `Includes: ${changes.slice(0, 2).join(', ')}` : 'Routine maintenance and updates.';
|
|
229
|
+
introID = `Kami dengan bangga mengumumkan rilis **Lapeh v${newVersion}**. Rilis ini mencakup pemeliharaan rutin dan perbaikan bug.`;
|
|
230
|
+
featureListID = changes.length > 0
|
|
231
|
+
? changes.map(f => `* **${f.trim()}**`).join('\n')
|
|
232
|
+
: '* **Routine maintenance**';
|
|
233
|
+
}
|
|
209
234
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
235
|
+
if (parsedEN) {
|
|
236
|
+
console.log('ā
Found entry in doc/en/CHANGELOG.md');
|
|
237
|
+
titleEN = parsedEN.title || `Latest Update v${newVersion}`;
|
|
238
|
+
introEN = parsedEN.intro || `We are proud to announce the release of **Lapeh v${newVersion}**. This update brings **${parsedEN.title || 'various new features'}** to enhance your development experience.`;
|
|
239
|
+
descriptionEN = parsedEN.intro ? parsedEN.intro.split('\n')[0] : `Release version ${newVersion} comes with various updates and improvements.`;
|
|
240
|
+
featureListEN = parsedEN.features;
|
|
241
|
+
} else {
|
|
242
|
+
console.log('ā ļø No entry in doc/en/CHANGELOG.md, using git logs...');
|
|
243
|
+
titleEN = changes.length > 0 ? changes[0] : 'Maintenance Release';
|
|
244
|
+
descriptionEN = changes.length > 0 ? `Includes: ${changes.slice(0, 2).join(', ')}` : 'Routine maintenance and updates.';
|
|
245
|
+
introEN = `We are proud to announce the release of **Lapeh v${newVersion}**. This release includes routine maintenance and bug fixes.`;
|
|
246
|
+
featureListEN = changes.length > 0
|
|
247
|
+
? changes.map(f => `* **${f.trim()}**`).join('\n')
|
|
248
|
+
: '* **Routine maintenance**';
|
|
249
|
+
}
|
|
215
250
|
} else {
|
|
216
|
-
console.log('
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
251
|
+
console.log('\nš Manual Blog Entry');
|
|
252
|
+
console.log('Silakan masukkan detail blog secara manual.');
|
|
253
|
+
|
|
254
|
+
// ID Inputs
|
|
255
|
+
titleID = await question('Judul Blog (ID): ');
|
|
256
|
+
descriptionID = await question('Deskripsi Singkat (ID): ');
|
|
257
|
+
const contentID = await question('Konten Utama/Fitur (ID) - Gunakan format Markdown jika perlu: ');
|
|
258
|
+
introID = `Rilis versi ${newVersion} telah hadir.`; // Fallback for manual
|
|
259
|
+
featureListID = contentID;
|
|
260
|
+
|
|
261
|
+
console.log('\n--- English Version ---');
|
|
262
|
+
|
|
263
|
+
// EN Inputs
|
|
264
|
+
titleEN = await question('Blog Title (EN): ');
|
|
265
|
+
descriptionEN = await question('Short Description (EN): ');
|
|
266
|
+
const contentEN = await question('Main Content/Features (EN): ');
|
|
267
|
+
introEN = `Release version ${newVersion} is here.`; // Fallback for manual
|
|
268
|
+
featureListEN = contentEN;
|
|
269
|
+
|
|
270
|
+
// Set defaults if empty
|
|
271
|
+
if (!titleID) titleID = `Update v${newVersion}`;
|
|
272
|
+
if (!descriptionID) descriptionID = `Pembaruan versi ${newVersion}`;
|
|
273
|
+
if (!featureListID) featureListID = '* Pembaruan rutin';
|
|
274
|
+
|
|
275
|
+
if (!titleEN) titleEN = `Update v${newVersion}`;
|
|
276
|
+
if (!descriptionEN) descriptionEN = `Update version ${newVersion}`;
|
|
277
|
+
if (!featureListEN) featureListEN = '* Routine updates';
|
|
222
278
|
}
|
|
223
279
|
|
|
224
280
|
blogTitleEN = titleEN; // Save for commit message
|
|
@@ -232,52 +288,72 @@ async function main() {
|
|
|
232
288
|
|
|
233
289
|
// Indonesian Blog Content
|
|
234
290
|
const idContent = `---
|
|
235
|
-
title: "Rilis v${newVersion}"
|
|
291
|
+
title: "Rilis Lapeh v${newVersion}: ${titleID}"
|
|
236
292
|
date: ${date}
|
|
237
293
|
author: Tim Lapeh
|
|
238
294
|
description: "${descriptionID.replace(/"/g, '\\"')}"
|
|
239
295
|
---
|
|
240
296
|
|
|
241
|
-
# Rilis v${newVersion}
|
|
297
|
+
# Rilis Lapeh v${newVersion}: ${titleID}
|
|
242
298
|
|
|
243
|
-
|
|
299
|
+
Ditulis pada **${dateString}** oleh **Tim Lapeh**
|
|
244
300
|
|
|
245
|
-
|
|
301
|
+
${introID}
|
|
302
|
+
|
|
303
|
+
## Apa yang Baru? š
|
|
246
304
|
|
|
247
305
|
${featureListID}
|
|
248
306
|
|
|
249
|
-
## Cara
|
|
307
|
+
## Cara Upgrade
|
|
308
|
+
|
|
309
|
+
Bagi pengguna baru, cukup jalankan:
|
|
310
|
+
|
|
311
|
+
\`\`\`bash
|
|
312
|
+
npx lapeh init my-project
|
|
313
|
+
\`\`\`
|
|
314
|
+
|
|
315
|
+
Bagi pengguna lama yang ingin update ke versi terbaru:
|
|
250
316
|
|
|
251
317
|
\`\`\`bash
|
|
252
318
|
npm install lapeh@latest
|
|
253
319
|
\`\`\`
|
|
254
320
|
|
|
255
|
-
Terima kasih telah
|
|
321
|
+
Terima kasih telah menjadi bagian dari perjalanan Lapeh Framework!
|
|
256
322
|
`;
|
|
257
323
|
|
|
258
324
|
// English Blog Content
|
|
259
325
|
const enContent = `---
|
|
260
|
-
title: "Release v${newVersion}"
|
|
326
|
+
title: "Release Lapeh v${newVersion}: ${titleEN}"
|
|
261
327
|
date: ${date}
|
|
262
328
|
author: Lapeh Team
|
|
263
329
|
description: "${descriptionEN.replace(/"/g, '\\"')}"
|
|
264
330
|
---
|
|
265
331
|
|
|
266
|
-
# Release v${newVersion}
|
|
332
|
+
# Release Lapeh v${newVersion}: ${titleEN}
|
|
333
|
+
|
|
334
|
+
Written on **${dateStringEn}** by **Lapeh Team**
|
|
267
335
|
|
|
268
|
-
${
|
|
336
|
+
${introEN}
|
|
269
337
|
|
|
270
|
-
##
|
|
338
|
+
## What's New? š
|
|
271
339
|
|
|
272
340
|
${featureListEN}
|
|
273
341
|
|
|
274
|
-
## How to
|
|
342
|
+
## How to Upgrade
|
|
343
|
+
|
|
344
|
+
For new users, simply run:
|
|
345
|
+
|
|
346
|
+
\`\`\`bash
|
|
347
|
+
npx lapeh init my-project
|
|
348
|
+
\`\`\`
|
|
349
|
+
|
|
350
|
+
For existing users who want to update to the latest version:
|
|
275
351
|
|
|
276
352
|
\`\`\`bash
|
|
277
353
|
npm install lapeh@latest
|
|
278
354
|
\`\`\`
|
|
279
355
|
|
|
280
|
-
Thank you for
|
|
356
|
+
Thank you for being part of the Lapeh Framework journey!
|
|
281
357
|
`;
|
|
282
358
|
|
|
283
359
|
fs.writeFileSync(path.join(websiteDir, 'blog', blogFileName), idContent);
|