lapeh 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/.env.example +16 -0
- package/bin/index.js +105 -0
- package/docker-compose.yml +17 -0
- package/package.json +70 -0
- package/prisma/base.prisma +8 -0
- package/prisma/migrations/20251225163737_init/migration.sql +236 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +174 -0
- package/prisma/seed.ts +350 -0
- package/prisma.config.ts +15 -0
- package/readme.md +120 -0
- package/scripts/compile-schema.js +29 -0
- package/scripts/generate-jwt-secret.js +38 -0
- package/scripts/make-model.js +42 -0
- package/scripts/make-module.js +158 -0
- package/src/controllers/authController.ts +347 -0
- package/src/controllers/rbacController.ts +353 -0
- package/src/index.ts +29 -0
- package/src/middleware/auth.ts +56 -0
- package/src/middleware/error.ts +8 -0
- package/src/middleware/visitor.ts +180 -0
- package/src/models/schema.prisma +159 -0
- package/src/prisma.ts +32 -0
- package/src/realtime.ts +34 -0
- package/src/redis.ts +69 -0
- package/src/routes/auth.ts +74 -0
- package/src/routes/rbac.ts +55 -0
- package/src/schema/auth-schema.ts +62 -0
- package/src/schema/user-schema.ts +57 -0
- package/src/server.ts +32 -0
- package/src/utils/pagination.ts +56 -0
- package/src/utils/response.ts +59 -0
- package/tsconfig.json +12 -0
package/prisma/seed.ts
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { prisma } from "../src/prisma";
|
|
2
|
+
import bcrypt from "bcryptjs";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import slugify from "slugify";
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
console.log("Start seeding...");
|
|
8
|
+
|
|
9
|
+
// 1. Create Users (Super Admin, Admin & User)
|
|
10
|
+
const passwordHash = await bcrypt.hash("string", 10);
|
|
11
|
+
|
|
12
|
+
const superAdmin = await prisma.users.upsert({
|
|
13
|
+
where: { email: "sa@sa.com" },
|
|
14
|
+
update: {},
|
|
15
|
+
create: {
|
|
16
|
+
uuid: uuidv4(),
|
|
17
|
+
name: "Super Admin",
|
|
18
|
+
email: "sa@sa.com",
|
|
19
|
+
password: passwordHash,
|
|
20
|
+
created_at: new Date(),
|
|
21
|
+
updated_at: new Date(),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
console.log(`Created super admin: ${superAdmin.name}`);
|
|
25
|
+
|
|
26
|
+
const admin = await prisma.users.upsert({
|
|
27
|
+
where: { email: "a@a.com" },
|
|
28
|
+
update: {},
|
|
29
|
+
create: {
|
|
30
|
+
uuid: uuidv4(),
|
|
31
|
+
name: "Admin User",
|
|
32
|
+
email: "a@a.com",
|
|
33
|
+
password: passwordHash,
|
|
34
|
+
created_at: new Date(),
|
|
35
|
+
updated_at: new Date(),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
console.log(`Created admin: ${admin.name}`);
|
|
39
|
+
|
|
40
|
+
const user = await prisma.users.upsert({
|
|
41
|
+
where: { email: "u@u.com" },
|
|
42
|
+
update: {},
|
|
43
|
+
create: {
|
|
44
|
+
uuid: uuidv4(),
|
|
45
|
+
name: "Regular User",
|
|
46
|
+
email: "u@u.com",
|
|
47
|
+
password: passwordHash,
|
|
48
|
+
created_at: new Date(),
|
|
49
|
+
updated_at: new Date(),
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
console.log(`Created user: ${user.name}`);
|
|
53
|
+
|
|
54
|
+
// 2. Seed Roles
|
|
55
|
+
const superAdminRole = await prisma.roles.upsert({
|
|
56
|
+
where: { slug: "super_admin" },
|
|
57
|
+
update: {},
|
|
58
|
+
create: {
|
|
59
|
+
name: "Super Admin",
|
|
60
|
+
slug: "super_admin",
|
|
61
|
+
description: "Full access to all resources",
|
|
62
|
+
created_at: new Date(),
|
|
63
|
+
updated_at: new Date(),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const adminRole = await prisma.roles.upsert({
|
|
68
|
+
where: { slug: "admin" },
|
|
69
|
+
update: {},
|
|
70
|
+
create: {
|
|
71
|
+
name: "Admin",
|
|
72
|
+
slug: "admin",
|
|
73
|
+
description: "Administrator",
|
|
74
|
+
created_at: new Date(),
|
|
75
|
+
updated_at: new Date(),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const userRole = await prisma.roles.upsert({
|
|
80
|
+
where: { slug: "user" },
|
|
81
|
+
update: {},
|
|
82
|
+
create: {
|
|
83
|
+
name: "User",
|
|
84
|
+
slug: "user",
|
|
85
|
+
description: "Regular user",
|
|
86
|
+
created_at: new Date(),
|
|
87
|
+
updated_at: new Date(),
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
console.log("Seeded roles");
|
|
92
|
+
|
|
93
|
+
// 3. Seed Permissions
|
|
94
|
+
const manageUsers = await prisma.permissions.upsert({
|
|
95
|
+
where: { slug: "users.manage" },
|
|
96
|
+
update: {},
|
|
97
|
+
create: {
|
|
98
|
+
name: "Manage Users",
|
|
99
|
+
slug: "users.manage",
|
|
100
|
+
description: "Create, update, and delete users",
|
|
101
|
+
created_at: new Date(),
|
|
102
|
+
updated_at: new Date(),
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const manageUsersCreate = await prisma.permissions.upsert({
|
|
106
|
+
where: { slug: "users.create" },
|
|
107
|
+
update: {},
|
|
108
|
+
create: {
|
|
109
|
+
name: "Create Users",
|
|
110
|
+
slug: "users.create",
|
|
111
|
+
description: "Create users",
|
|
112
|
+
created_at: new Date(),
|
|
113
|
+
updated_at: new Date(),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
const manageUsersView = await prisma.permissions.upsert({
|
|
117
|
+
where: { slug: "users.view" },
|
|
118
|
+
update: {},
|
|
119
|
+
create: {
|
|
120
|
+
name: "View Users",
|
|
121
|
+
slug: "users.view",
|
|
122
|
+
description: "View users",
|
|
123
|
+
created_at: new Date(),
|
|
124
|
+
updated_at: new Date(),
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const manageUsersUpdate = await prisma.permissions.upsert({
|
|
128
|
+
where: { slug: "users.update" },
|
|
129
|
+
update: {},
|
|
130
|
+
create: {
|
|
131
|
+
name: "Update Users",
|
|
132
|
+
slug: "users.update",
|
|
133
|
+
description: "Update users",
|
|
134
|
+
created_at: new Date(),
|
|
135
|
+
updated_at: new Date(),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
const manageUsersDelete = await prisma.permissions.upsert({
|
|
139
|
+
where: { slug: "users.delete" },
|
|
140
|
+
update: {},
|
|
141
|
+
create: {
|
|
142
|
+
name: "Delete Users",
|
|
143
|
+
slug: "users.delete",
|
|
144
|
+
description: "Delete users",
|
|
145
|
+
created_at: new Date(),
|
|
146
|
+
updated_at: new Date(),
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const manageRoles = await prisma.permissions.upsert({
|
|
151
|
+
where: { slug: "roles.manage" },
|
|
152
|
+
update: {},
|
|
153
|
+
create: {
|
|
154
|
+
name: "Manage Roles",
|
|
155
|
+
slug: "roles.manage",
|
|
156
|
+
description: "Create, update, and delete roles",
|
|
157
|
+
created_at: new Date(),
|
|
158
|
+
updated_at: new Date(),
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const managePermissions = await prisma.permissions.upsert({
|
|
163
|
+
where: { slug: "permissions.manage" },
|
|
164
|
+
update: {},
|
|
165
|
+
create: {
|
|
166
|
+
name: "Manage Permissions",
|
|
167
|
+
slug: "permissions.manage",
|
|
168
|
+
description: "Create, update, and delete permissions",
|
|
169
|
+
created_at: new Date(),
|
|
170
|
+
updated_at: new Date(),
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const manageProfiles = await prisma.permissions.upsert({
|
|
175
|
+
where: { slug: "profiles.manage" },
|
|
176
|
+
update: {},
|
|
177
|
+
create: {
|
|
178
|
+
name: "Manage Profiles",
|
|
179
|
+
slug: "profiles.manage",
|
|
180
|
+
description: "Manage user profiles",
|
|
181
|
+
created_at: new Date(),
|
|
182
|
+
updated_at: new Date(),
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
console.log("Seeded permissions");
|
|
187
|
+
|
|
188
|
+
// 4. Assign permissions to roles
|
|
189
|
+
const postsCreate = await prisma.permissions.upsert({
|
|
190
|
+
where: { slug: "posts.create" },
|
|
191
|
+
update: {},
|
|
192
|
+
create: {
|
|
193
|
+
name: "Create Posts",
|
|
194
|
+
slug: "posts.create",
|
|
195
|
+
description: "Create posts",
|
|
196
|
+
created_at: new Date(),
|
|
197
|
+
updated_at: new Date(),
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const postsUpdate = await prisma.permissions.upsert({
|
|
202
|
+
where: { slug: "posts.update" },
|
|
203
|
+
update: {},
|
|
204
|
+
create: {
|
|
205
|
+
name: "Update Posts",
|
|
206
|
+
slug: "posts.update",
|
|
207
|
+
description: "Update posts",
|
|
208
|
+
created_at: new Date(),
|
|
209
|
+
updated_at: new Date(),
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const postsDelete = await prisma.permissions.upsert({
|
|
214
|
+
where: { slug: "posts.delete" },
|
|
215
|
+
update: {},
|
|
216
|
+
create: {
|
|
217
|
+
name: "Delete Posts",
|
|
218
|
+
slug: "posts.delete",
|
|
219
|
+
description: "Delete posts",
|
|
220
|
+
created_at: new Date(),
|
|
221
|
+
updated_at: new Date(),
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const postsView = await prisma.permissions.upsert({
|
|
226
|
+
where: { slug: "posts.view" },
|
|
227
|
+
update: {},
|
|
228
|
+
create: {
|
|
229
|
+
name: "View Posts",
|
|
230
|
+
slug: "posts.view",
|
|
231
|
+
description: "View posts",
|
|
232
|
+
created_at: new Date(),
|
|
233
|
+
updated_at: new Date(),
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const categoriesManage = await prisma.permissions.upsert({
|
|
238
|
+
where: { slug: "categories.manage" },
|
|
239
|
+
update: {},
|
|
240
|
+
create: {
|
|
241
|
+
name: "Manage Categories",
|
|
242
|
+
slug: "categories.manage",
|
|
243
|
+
description: "Manage categories",
|
|
244
|
+
created_at: new Date(),
|
|
245
|
+
updated_at: new Date(),
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const commentsManage = await prisma.permissions.upsert({
|
|
250
|
+
where: { slug: "comments.manage" },
|
|
251
|
+
update: {},
|
|
252
|
+
create: {
|
|
253
|
+
name: "Manage Comments",
|
|
254
|
+
slug: "comments.manage",
|
|
255
|
+
description: "Manage comments",
|
|
256
|
+
created_at: new Date(),
|
|
257
|
+
updated_at: new Date(),
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
console.log("Seeded resource permissions");
|
|
262
|
+
|
|
263
|
+
// 4. Assign permissions to roles
|
|
264
|
+
const rolePermPairs: { roleId: bigint; permId: bigint }[] = [
|
|
265
|
+
// super_admin gets all users permissions + other management permissions
|
|
266
|
+
{ roleId: superAdminRole.id, permId: manageUsers.id },
|
|
267
|
+
{ roleId: superAdminRole.id, permId: manageUsersCreate.id },
|
|
268
|
+
{ roleId: superAdminRole.id, permId: manageUsersView.id },
|
|
269
|
+
{ roleId: superAdminRole.id, permId: manageUsersUpdate.id },
|
|
270
|
+
{ roleId: superAdminRole.id, permId: manageUsersDelete.id },
|
|
271
|
+
{ roleId: superAdminRole.id, permId: manageRoles.id },
|
|
272
|
+
{ roleId: superAdminRole.id, permId: managePermissions.id },
|
|
273
|
+
{ roleId: superAdminRole.id, permId: manageProfiles.id },
|
|
274
|
+
{ roleId: superAdminRole.id, permId: postsCreate.id },
|
|
275
|
+
{ roleId: superAdminRole.id, permId: postsUpdate.id },
|
|
276
|
+
{ roleId: superAdminRole.id, permId: postsDelete.id },
|
|
277
|
+
{ roleId: superAdminRole.id, permId: postsView.id },
|
|
278
|
+
{ roleId: superAdminRole.id, permId: categoriesManage.id },
|
|
279
|
+
{ roleId: superAdminRole.id, permId: commentsManage.id },
|
|
280
|
+
// admin gets users permissions except delete
|
|
281
|
+
{ roleId: adminRole.id, permId: manageUsersCreate.id },
|
|
282
|
+
{ roleId: adminRole.id, permId: manageUsersView.id },
|
|
283
|
+
{ roleId: adminRole.id, permId: manageUsersUpdate.id },
|
|
284
|
+
// admin gets other management permissions
|
|
285
|
+
{ roleId: adminRole.id, permId: manageRoles.id },
|
|
286
|
+
{ roleId: adminRole.id, permId: managePermissions.id },
|
|
287
|
+
{ roleId: adminRole.id, permId: postsCreate.id },
|
|
288
|
+
{ roleId: adminRole.id, permId: postsUpdate.id },
|
|
289
|
+
{ roleId: adminRole.id, permId: postsDelete.id },
|
|
290
|
+
{ roleId: adminRole.id, permId: postsView.id },
|
|
291
|
+
{ roleId: adminRole.id, permId: categoriesManage.id },
|
|
292
|
+
{ roleId: adminRole.id, permId: commentsManage.id },
|
|
293
|
+
// user gets read-only posts
|
|
294
|
+
{ roleId: userRole.id, permId: postsView.id },
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
for (const pair of rolePermPairs) {
|
|
298
|
+
await prisma.role_permissions.upsert({
|
|
299
|
+
where: {
|
|
300
|
+
role_id_permission_id: {
|
|
301
|
+
role_id: pair.roleId,
|
|
302
|
+
permission_id: pair.permId,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
create: {
|
|
306
|
+
role_id: pair.roleId,
|
|
307
|
+
permission_id: pair.permId,
|
|
308
|
+
created_at: new Date(),
|
|
309
|
+
},
|
|
310
|
+
update: {},
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.log("Assigned permissions to roles");
|
|
315
|
+
|
|
316
|
+
// 5. Assign roles to users
|
|
317
|
+
const userRolePairs: { userId: bigint; roleId: bigint }[] = [
|
|
318
|
+
{ userId: superAdmin.id, roleId: superAdminRole.id },
|
|
319
|
+
{ userId: admin.id, roleId: adminRole.id },
|
|
320
|
+
{ userId: user.id, roleId: userRole.id },
|
|
321
|
+
];
|
|
322
|
+
|
|
323
|
+
for (const pair of userRolePairs) {
|
|
324
|
+
await prisma.user_roles.upsert({
|
|
325
|
+
where: {
|
|
326
|
+
user_id_role_id: {
|
|
327
|
+
user_id: pair.userId,
|
|
328
|
+
role_id: pair.roleId,
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
create: {
|
|
332
|
+
user_id: pair.userId,
|
|
333
|
+
role_id: pair.roleId,
|
|
334
|
+
created_at: new Date(),
|
|
335
|
+
},
|
|
336
|
+
update: {},
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
console.log("Assigned roles to users");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
main()
|
|
344
|
+
.catch((e) => {
|
|
345
|
+
console.error(e);
|
|
346
|
+
process.exit(1);
|
|
347
|
+
})
|
|
348
|
+
.finally(async () => {
|
|
349
|
+
await prisma.$disconnect();
|
|
350
|
+
});
|
package/prisma.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This file was generated by Prisma, and assumes you have installed the following:
|
|
2
|
+
// npm install --save-dev prisma dotenv
|
|
3
|
+
import "dotenv/config";
|
|
4
|
+
import { defineConfig, env } from "prisma/config";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
schema: "prisma/schema.prisma",
|
|
8
|
+
migrations: {
|
|
9
|
+
path: "prisma/migrations",
|
|
10
|
+
seed: "ts-node prisma/seed.ts",
|
|
11
|
+
},
|
|
12
|
+
datasource: {
|
|
13
|
+
url: env("DATABASE_URL"),
|
|
14
|
+
},
|
|
15
|
+
});
|
package/readme.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# API Lapeh Framework
|
|
2
|
+
|
|
3
|
+
**API Lapeh** adalah framework berbasis Express.js yang terstandarisasi, dirancang untuk mempercepat pengembangan REST API dengan struktur yang solid, aman, dan scalable. Terinspirasi oleh struktur Laravel dan NestJS, namun tetap menjaga kesederhanaan Express.
|
|
4
|
+
|
|
5
|
+
## 🚀 Fitur Utama
|
|
6
|
+
|
|
7
|
+
- **Struktur Modular**: Terorganisir rapi dengan Controllers, Services, Routes, dan Middleware.
|
|
8
|
+
- **TypeScript Ready**: Full TypeScript support untuk type-safety.
|
|
9
|
+
- **Prisma ORM**: Integrasi database yang modern dan type-safe.
|
|
10
|
+
- **Schema Terpisah**: Mendukung pemisahan schema Prisma per model (mirip Eloquent).
|
|
11
|
+
- **Generator Tools**: CLI commands untuk generate Module dan Model dengan cepat.
|
|
12
|
+
- **Security Best Practices**: Dilengkapi dengan Helmet, Rate Limiting, CORS, dan JWT Authentication.
|
|
13
|
+
- **Validasi Data**: Menggunakan Zod untuk validasi request yang kuat.
|
|
14
|
+
|
|
15
|
+
## 📦 Instalasi & Penggunaan
|
|
16
|
+
|
|
17
|
+
Buat project baru cukup dengan satu perintah:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx lapeh-cli nama-project-anda
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Apa yang terjadi otomatis?
|
|
24
|
+
|
|
25
|
+
1. Struktur project dibuat.
|
|
26
|
+
2. Dependencies diinstall.
|
|
27
|
+
3. Environment variable (`.env`) disiapkan.
|
|
28
|
+
4. **JWT Secret** di-generate otomatis.
|
|
29
|
+
|
|
30
|
+
Masuk ke folder project dan jalankan:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd nama-project-anda
|
|
34
|
+
npm run dev
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Server akan berjalan di `http://localhost:4000`.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🛠 Development Tools
|
|
42
|
+
|
|
43
|
+
API Lapeh menyediakan tools untuk mempercepat development, mirip dengan `artisan` di Laravel.
|
|
44
|
+
|
|
45
|
+
### 1. Membuat Module (Resource)
|
|
46
|
+
|
|
47
|
+
Membuat Controller, Service, dan Route sekaligus.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm run make:module NamaResource
|
|
51
|
+
# Contoh: npm run make:module Product
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Command ini akan membuat:
|
|
55
|
+
|
|
56
|
+
- `src/controllers/product.controller.ts`
|
|
57
|
+
- `src/services/product.service.ts`
|
|
58
|
+
- `src/routes/product.route.ts` (dan otomatis didaftarkan di `src/routes/index.ts` jika memungkinkan)
|
|
59
|
+
|
|
60
|
+
### 2. Membuat Model Database
|
|
61
|
+
|
|
62
|
+
Membuat file model Prisma baru di dalam folder `src/models/` (atau `prisma/models` tergantung konfigurasi).
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run make:model NamaModel
|
|
66
|
+
# Contoh: npm run make:model User
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Ini akan membuat file `src/models/User.prisma`.
|
|
70
|
+
|
|
71
|
+
### 3. Workflow Database (Prisma)
|
|
72
|
+
|
|
73
|
+
Karena framework ini menggunakan **Schema Terpisah** (split schema), Anda **TIDAK BOLEH** mengedit `prisma/schema.prisma` secara manual.
|
|
74
|
+
|
|
75
|
+
- **Edit Models**: Lakukan perubahan di `src/models/*.prisma`.
|
|
76
|
+
- **Apply Changes**: Jalankan perintah migrasi standar, sistem akan otomatis menggabungkan (compile) schema Anda.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Generate Prisma Client (setiap ada perubahan model)
|
|
80
|
+
npm run prisma:generate
|
|
81
|
+
|
|
82
|
+
# Migrasi Database (Development)
|
|
83
|
+
npm run prisma:migrate
|
|
84
|
+
|
|
85
|
+
# Deploy ke Production
|
|
86
|
+
npm run prisma:deploy
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
> **Catatan:** Script `compile-schema.js` akan otomatis berjalan sebelum perintah prisma di atas dieksekusi.
|
|
90
|
+
|
|
91
|
+
### 4. Generate JWT Secret
|
|
92
|
+
|
|
93
|
+
Jika Anda perlu me-refresh secret key JWT:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm run generate:jwt
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 📂 Struktur Folder
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
src/
|
|
105
|
+
├── controllers/ # Logika Request & Response
|
|
106
|
+
├── services/ # Business Logic
|
|
107
|
+
├── routes/ # Definisi Route API
|
|
108
|
+
├── models/ # Definisi Schema Prisma per Model
|
|
109
|
+
├── middleware/ # Auth, Validation, Error Handling
|
|
110
|
+
├── schema/ # Zod Validation Schemas
|
|
111
|
+
├── utils/ # Helper Functions
|
|
112
|
+
└── index.ts # App Entry Point
|
|
113
|
+
prisma/
|
|
114
|
+
├── schema.prisma # [GENERATED] Jangan edit file ini
|
|
115
|
+
└── base.prisma # Konfigurasi Datasource & Generator
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 📝 Lisensi
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const prismaDir = path.join(__dirname, '..', 'prisma');
|
|
5
|
+
const modelsDir = path.join(__dirname, '..', 'src', 'models');
|
|
6
|
+
const schemaFile = path.join(prismaDir, 'schema.prisma');
|
|
7
|
+
const baseFile = path.join(prismaDir, 'base.prisma');
|
|
8
|
+
|
|
9
|
+
// Ensure models directory exists
|
|
10
|
+
if (!fs.existsSync(modelsDir)) {
|
|
11
|
+
fs.mkdirSync(modelsDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Read base schema (datasource & generator)
|
|
15
|
+
let schemaContent = fs.readFileSync(baseFile, 'utf8');
|
|
16
|
+
|
|
17
|
+
// Read all .prisma files in src/models
|
|
18
|
+
const modelFiles = fs.readdirSync(modelsDir).filter(file => file.endsWith('.prisma'));
|
|
19
|
+
|
|
20
|
+
modelFiles.forEach(file => {
|
|
21
|
+
const content = fs.readFileSync(path.join(modelsDir, file), 'utf8');
|
|
22
|
+
schemaContent += '\n\n' + content;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Write concatenated content to prisma/schema.prisma
|
|
26
|
+
fs.writeFileSync(schemaFile, schemaContent);
|
|
27
|
+
|
|
28
|
+
console.log('✅ Prisma schema compiled successfully!');
|
|
29
|
+
console.log(` Merged ${modelFiles.length} model files from src/models/ into prisma/schema.prisma`);
|
|
@@ -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,42 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const modelName = process.argv[2];
|
|
5
|
+
|
|
6
|
+
if (!modelName) {
|
|
7
|
+
console.error('❌ Please specify the model name.');
|
|
8
|
+
console.error(' Usage: npm run make:model <ModelName>');
|
|
9
|
+
console.error(' Example: npm run make:model Product');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const PascalCaseName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
|
|
14
|
+
const tableName = modelName.toLowerCase() + 's'; // simple pluralization
|
|
15
|
+
|
|
16
|
+
const modelsDir = path.join(__dirname, '..', 'src', 'models');
|
|
17
|
+
const modelPath = path.join(modelsDir, `${PascalCaseName}.prisma`);
|
|
18
|
+
|
|
19
|
+
if (fs.existsSync(modelPath)) {
|
|
20
|
+
console.error(`❌ Model ${PascalCaseName} already exists at ${modelPath}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Ensure models directory exists
|
|
25
|
+
if (!fs.existsSync(modelsDir)) {
|
|
26
|
+
fs.mkdirSync(modelsDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const content = `model ${tableName} {
|
|
30
|
+
id BigInt @id @default(autoincrement())
|
|
31
|
+
name String @db.VarChar(255)
|
|
32
|
+
createdAt DateTime? @default(now()) @db.Timestamp(0)
|
|
33
|
+
updatedAt DateTime? @updatedAt @db.Timestamp(0)
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
fs.writeFileSync(modelPath, content);
|
|
38
|
+
|
|
39
|
+
console.log(`✅ Model created: src/models/${PascalCaseName}.prisma`);
|
|
40
|
+
console.log(`\nNext steps:`);
|
|
41
|
+
console.log(`1. Edit the model file.`);
|
|
42
|
+
console.log(`2. Run 'npm run prisma:migrate' to update the database.`);
|