lapeh 1.0.12 → 2.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 +5 -6
- package/.vscode/settings.json +5 -0
- package/docker-compose.yml +8 -1
- package/package.json +4 -3
- package/prisma/migrations/20251226000329_create_pets_table/migration.sql +11 -0
- package/prisma/migrations/20251226001249_create_pets_table/migration.sql +82 -0
- package/prisma/migrations/20251226001717_restore_core_models/migration.sql +236 -0
- package/prisma/migrations/migration_lock.toml +3 -3
- package/prisma/schema.prisma +170 -155
- package/prisma/seed.ts +59 -0
- package/readme.md +38 -0
- package/scripts/compile-schema.js +2 -2
- package/scripts/init-project.js +16 -1
- package/scripts/make-controller.js +205 -0
- package/src/controllers/authController.ts +9 -9
- package/src/controllers/petController.ts +132 -0
- package/src/models/core.model +163 -0
- package/src/models/pets.model +9 -0
- package/src/prisma.ts +1 -1
- package/src/redis.ts +14 -6
- package/src/routes/pets.ts +12 -0
- package/src/schema/pet-schema.ts +14 -0
- package/src/server.ts +2 -0
- package/src/models/schema.prisma +0 -159
package/prisma/schema.prisma
CHANGED
|
@@ -8,162 +8,177 @@ datasource db {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
model cache {
|
|
12
|
+
key String @id @db.VarChar(255)
|
|
13
|
+
value String @db.Text
|
|
14
|
+
expiration Int
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
model cache_locks {
|
|
18
|
+
key String @id @db.VarChar(255)
|
|
19
|
+
owner String @db.VarChar(255)
|
|
20
|
+
expiration Int
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
model failed_jobs {
|
|
24
|
+
id BigInt @id @default(autoincrement())
|
|
25
|
+
uuid String @unique @db.VarChar(255)
|
|
26
|
+
connection String @db.Text
|
|
27
|
+
queue String @db.Text
|
|
28
|
+
payload String @db.Text
|
|
29
|
+
exception String @db.Text
|
|
30
|
+
failed_at DateTime @default(now()) @db.Timestamp(0)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
model job_batches {
|
|
34
|
+
id String @id @db.VarChar(255)
|
|
35
|
+
name String @db.VarChar(255)
|
|
36
|
+
total_jobs Int
|
|
37
|
+
pending_jobs Int
|
|
38
|
+
failed_jobs Int
|
|
39
|
+
failed_job_ids String @db.Text
|
|
40
|
+
options String? @db.Text
|
|
41
|
+
cancelled_at Int?
|
|
42
|
+
created_at Int
|
|
43
|
+
finished_at Int?
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
model jobs {
|
|
47
|
+
id BigInt @id @default(autoincrement())
|
|
48
|
+
queue String @db.VarChar(255)
|
|
49
|
+
payload String @db.Text
|
|
50
|
+
attempts Int @db.SmallInt
|
|
51
|
+
reserved_at Int?
|
|
52
|
+
available_at Int
|
|
53
|
+
created_at Int
|
|
54
|
+
|
|
55
|
+
@@index([queue])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
model migrations {
|
|
59
|
+
id Int @id @default(autoincrement())
|
|
60
|
+
migration String @db.VarChar(255)
|
|
61
|
+
batch Int
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
model password_reset_tokens {
|
|
65
|
+
email String @id @db.VarChar(255)
|
|
66
|
+
token String @db.VarChar(255)
|
|
67
|
+
created_at DateTime? @db.Timestamp(0)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
model personal_access_tokens {
|
|
71
|
+
id BigInt @id @default(autoincrement())
|
|
72
|
+
tokenable_type String @db.VarChar(255)
|
|
73
|
+
tokenable_id BigInt
|
|
74
|
+
name String @db.Text
|
|
75
|
+
token String @unique @db.VarChar(64)
|
|
76
|
+
abilities String? @db.Text
|
|
77
|
+
last_used_at DateTime? @db.Timestamp(0)
|
|
78
|
+
expires_at DateTime? @db.Timestamp(0)
|
|
79
|
+
created_at DateTime? @db.Timestamp(0)
|
|
80
|
+
updated_at DateTime? @db.Timestamp(0)
|
|
81
|
+
|
|
82
|
+
@@index([expires_at])
|
|
83
|
+
@@index([tokenable_type, tokenable_id])
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
model sessions {
|
|
87
|
+
id String @id @db.VarChar(255)
|
|
88
|
+
user_id BigInt?
|
|
89
|
+
ip_address String? @db.VarChar(45)
|
|
90
|
+
user_agent String? @db.Text
|
|
91
|
+
payload String @db.Text
|
|
92
|
+
last_activity Int
|
|
93
|
+
|
|
94
|
+
@@index([last_activity])
|
|
95
|
+
@@index([user_id])
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
model users {
|
|
99
|
+
id BigInt @id @default(autoincrement())
|
|
100
|
+
uuid String @unique @db.Uuid
|
|
101
|
+
name String @db.VarChar(255)
|
|
102
|
+
email String @unique @db.VarChar(255)
|
|
103
|
+
avatar String? @db.VarChar(255)
|
|
104
|
+
avatar_url String? @db.VarChar(255)
|
|
105
|
+
email_verified_at DateTime? @db.Timestamp(0)
|
|
106
|
+
password String @db.VarChar(255)
|
|
107
|
+
remember_token String? @db.VarChar(100)
|
|
108
|
+
created_at DateTime? @db.Timestamp(0)
|
|
109
|
+
updated_at DateTime? @db.Timestamp(0)
|
|
110
|
+
|
|
111
|
+
user_roles user_roles[]
|
|
112
|
+
user_permissions user_permissions[]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
model roles {
|
|
116
|
+
id BigInt @id @default(autoincrement())
|
|
117
|
+
name String @db.VarChar(255)
|
|
118
|
+
slug String @unique @db.VarChar(255)
|
|
119
|
+
description String? @db.Text
|
|
120
|
+
created_at DateTime? @db.Timestamp(0)
|
|
121
|
+
updated_at DateTime? @db.Timestamp(0)
|
|
122
|
+
|
|
123
|
+
user_roles user_roles[]
|
|
124
|
+
role_permissions role_permissions[]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
model permissions {
|
|
128
|
+
id BigInt @id @default(autoincrement())
|
|
129
|
+
name String @db.VarChar(255)
|
|
130
|
+
slug String @unique @db.VarChar(255)
|
|
131
|
+
description String? @db.Text
|
|
132
|
+
created_at DateTime? @db.Timestamp(0)
|
|
133
|
+
updated_at DateTime? @db.Timestamp(0)
|
|
134
|
+
|
|
135
|
+
role_permissions role_permissions[]
|
|
136
|
+
user_permissions user_permissions[]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
model user_roles {
|
|
140
|
+
id BigInt @id @default(autoincrement())
|
|
141
|
+
user_id BigInt
|
|
142
|
+
role_id BigInt
|
|
143
|
+
created_at DateTime? @db.Timestamp(0)
|
|
144
|
+
|
|
145
|
+
user users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
146
|
+
role roles @relation(fields: [role_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
147
|
+
|
|
148
|
+
@@unique([user_id, role_id])
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
model role_permissions {
|
|
152
|
+
id BigInt @id @default(autoincrement())
|
|
153
|
+
role_id BigInt
|
|
154
|
+
permission_id BigInt
|
|
155
|
+
created_at DateTime? @db.Timestamp(0)
|
|
156
|
+
|
|
157
|
+
role roles @relation(fields: [role_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
158
|
+
permission permissions @relation(fields: [permission_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
159
|
+
|
|
160
|
+
@@unique([role_id, permission_id])
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
model user_permissions {
|
|
164
|
+
id BigInt @id @default(autoincrement())
|
|
165
|
+
user_id BigInt
|
|
166
|
+
permission_id BigInt
|
|
167
|
+
created_at DateTime? @db.Timestamp(0)
|
|
168
|
+
|
|
169
|
+
user users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
170
|
+
permission permissions @relation(fields: [permission_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
171
|
+
|
|
172
|
+
@@unique([user_id, permission_id])
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
11
176
|
|
|
12
|
-
model
|
|
13
|
-
key String @id @db.VarChar(255)
|
|
14
|
-
value String
|
|
15
|
-
expiration Int
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
model cache_locks {
|
|
19
|
-
key String @id @db.VarChar(255)
|
|
20
|
-
owner String @db.VarChar(255)
|
|
21
|
-
expiration Int
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
model failed_jobs {
|
|
25
|
-
id BigInt @id @default(autoincrement())
|
|
26
|
-
uuid String @unique(map: "failed_jobs_uuid_unique") @db.VarChar(255)
|
|
27
|
-
connection String
|
|
28
|
-
queue String
|
|
29
|
-
payload String
|
|
30
|
-
exception String
|
|
31
|
-
failed_at DateTime @default(now()) @db.Timestamp(0)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
model job_batches {
|
|
35
|
-
id String @id @db.VarChar(255)
|
|
36
|
-
name String @db.VarChar(255)
|
|
37
|
-
total_jobs Int
|
|
38
|
-
pending_jobs Int
|
|
39
|
-
failed_jobs Int
|
|
40
|
-
failed_job_ids String
|
|
41
|
-
options String?
|
|
42
|
-
cancelled_at Int?
|
|
43
|
-
created_at Int
|
|
44
|
-
finished_at Int?
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
model jobs {
|
|
48
|
-
id BigInt @id @default(autoincrement())
|
|
49
|
-
queue String @db.VarChar(255)
|
|
50
|
-
payload String
|
|
51
|
-
attempts Int @db.SmallInt
|
|
52
|
-
reserved_at Int?
|
|
53
|
-
available_at Int
|
|
54
|
-
created_at Int
|
|
55
|
-
|
|
56
|
-
@@index([queue], map: "jobs_queue_index")
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
model migrations {
|
|
60
|
-
id Int @id @default(autoincrement())
|
|
61
|
-
migration String @db.VarChar(255)
|
|
62
|
-
batch Int
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
model password_reset_tokens {
|
|
66
|
-
email String @id @db.VarChar(255)
|
|
67
|
-
token String @db.VarChar(255)
|
|
68
|
-
created_at DateTime? @db.Timestamp(0)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
model personal_access_tokens {
|
|
72
|
-
id BigInt @id @default(autoincrement())
|
|
73
|
-
tokenable_type String @db.VarChar(255)
|
|
74
|
-
tokenable_id BigInt
|
|
75
|
-
name String
|
|
76
|
-
token String @unique(map: "personal_access_tokens_token_unique") @db.VarChar(64)
|
|
77
|
-
abilities String?
|
|
78
|
-
last_used_at DateTime? @db.Timestamp(0)
|
|
79
|
-
expires_at DateTime? @db.Timestamp(0)
|
|
80
|
-
created_at DateTime? @db.Timestamp(0)
|
|
81
|
-
updated_at DateTime? @db.Timestamp(0)
|
|
82
|
-
|
|
83
|
-
@@index([expires_at], map: "personal_access_tokens_expires_at_index")
|
|
84
|
-
@@index([tokenable_type, tokenable_id], map: "personal_access_tokens_tokenable_type_tokenable_id_index")
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
model sessions {
|
|
88
|
-
id String @id @db.VarChar(255)
|
|
89
|
-
user_id BigInt?
|
|
90
|
-
ip_address String? @db.VarChar(45)
|
|
91
|
-
user_agent String?
|
|
92
|
-
payload String
|
|
93
|
-
last_activity Int
|
|
94
|
-
|
|
95
|
-
@@index([last_activity], map: "sessions_last_activity_index")
|
|
96
|
-
@@index([user_id], map: "sessions_user_id_index")
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
|
|
100
|
-
model users {
|
|
101
|
-
id BigInt @id @default(autoincrement())
|
|
102
|
-
uuid String @unique(map: "users_uuid_unique") @db.Uuid
|
|
103
|
-
name String @db.VarChar(255)
|
|
104
|
-
email String @unique(map: "users_email_unique") @db.VarChar(255)
|
|
105
|
-
avatar String? @db.VarChar(255)
|
|
106
|
-
avatar_url String? @db.VarChar(255)
|
|
107
|
-
email_verified_at DateTime? @db.Timestamp(0)
|
|
108
|
-
password String @db.VarChar(255)
|
|
109
|
-
remember_token String? @db.VarChar(100)
|
|
110
|
-
created_at DateTime? @db.Timestamp(0)
|
|
111
|
-
updated_at DateTime? @db.Timestamp(0)
|
|
112
|
-
user_roles user_roles[]
|
|
113
|
-
user_permissions user_permissions[]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
model roles {
|
|
117
|
-
id BigInt @id @default(autoincrement())
|
|
118
|
-
name String @db.VarChar(255)
|
|
119
|
-
slug String @unique @db.VarChar(255)
|
|
120
|
-
description String?
|
|
121
|
-
created_at DateTime? @db.Timestamp(0)
|
|
122
|
-
updated_at DateTime? @db.Timestamp(0)
|
|
123
|
-
user_roles user_roles[]
|
|
124
|
-
role_permissions role_permissions[]
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
model permissions {
|
|
128
|
-
id BigInt @id @default(autoincrement())
|
|
129
|
-
name String @db.VarChar(255)
|
|
130
|
-
slug String @unique @db.VarChar(255)
|
|
131
|
-
description String?
|
|
132
|
-
created_at DateTime? @db.Timestamp(0)
|
|
133
|
-
updated_at DateTime? @db.Timestamp(0)
|
|
134
|
-
user_permissions user_permissions[]
|
|
135
|
-
role_permissions role_permissions[]
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
model user_roles {
|
|
177
|
+
model pets {
|
|
139
178
|
id BigInt @id @default(autoincrement())
|
|
140
|
-
|
|
141
|
-
|
|
179
|
+
name String @db.VarChar(255)
|
|
180
|
+
species String @db.VarChar(100)
|
|
181
|
+
age Int @db.SmallInt
|
|
142
182
|
created_at DateTime? @db.Timestamp(0)
|
|
143
|
-
|
|
144
|
-
roles roles @relation(fields: [role_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
145
|
-
|
|
146
|
-
@@unique([user_id, role_id])
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
model role_permissions {
|
|
150
|
-
id BigInt @id @default(autoincrement())
|
|
151
|
-
role_id BigInt
|
|
152
|
-
permission_id BigInt
|
|
153
|
-
created_at DateTime? @db.Timestamp(0)
|
|
154
|
-
roles roles @relation(fields: [role_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
155
|
-
permissions permissions @relation(fields: [permission_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
156
|
-
|
|
157
|
-
@@unique([role_id, permission_id])
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
model user_permissions {
|
|
161
|
-
id BigInt @id @default(autoincrement())
|
|
162
|
-
user_id BigInt
|
|
163
|
-
permission_id BigInt
|
|
164
|
-
created_at DateTime? @db.Timestamp(0)
|
|
165
|
-
users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
166
|
-
permissions permissions @relation(fields: [permission_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
167
|
-
|
|
168
|
-
@@unique([user_id, permission_id])
|
|
183
|
+
updated_at DateTime? @db.Timestamp(0)
|
|
169
184
|
}
|
package/prisma/seed.ts
CHANGED
|
@@ -338,6 +338,65 @@ async function main() {
|
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
console.log("Assigned roles to users");
|
|
341
|
+
|
|
342
|
+
// 6. Seed Pets (Massive Seeding)
|
|
343
|
+
console.log("Starting massive pet seeding (50,000 records)...");
|
|
344
|
+
const petData = [];
|
|
345
|
+
const speciesList = [
|
|
346
|
+
"Dog",
|
|
347
|
+
"Cat",
|
|
348
|
+
"Bird",
|
|
349
|
+
"Fish",
|
|
350
|
+
"Rabbit",
|
|
351
|
+
"Hamster",
|
|
352
|
+
"Turtle",
|
|
353
|
+
"Parrot",
|
|
354
|
+
];
|
|
355
|
+
const names = [
|
|
356
|
+
"Bella",
|
|
357
|
+
"Max",
|
|
358
|
+
"Charlie",
|
|
359
|
+
"Luna",
|
|
360
|
+
"Lucy",
|
|
361
|
+
"Cooper",
|
|
362
|
+
"Bailey",
|
|
363
|
+
"Daisy",
|
|
364
|
+
"Sadie",
|
|
365
|
+
"Molly",
|
|
366
|
+
];
|
|
367
|
+
|
|
368
|
+
for (let i = 0; i < 50000; i++) {
|
|
369
|
+
const species = speciesList[Math.floor(Math.random() * speciesList.length)];
|
|
370
|
+
const name = names[Math.floor(Math.random() * names.length)] + " " + i;
|
|
371
|
+
const age = Math.floor(Math.random() * 15) + 1;
|
|
372
|
+
|
|
373
|
+
petData.push({
|
|
374
|
+
name,
|
|
375
|
+
species,
|
|
376
|
+
age,
|
|
377
|
+
created_at: new Date(),
|
|
378
|
+
updated_at: new Date(),
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Batch insert every 5000 records to prevent memory issues
|
|
382
|
+
if (petData.length === 5000) {
|
|
383
|
+
await prisma.pets.createMany({
|
|
384
|
+
data: petData,
|
|
385
|
+
});
|
|
386
|
+
petData.length = 0; // Clear array
|
|
387
|
+
console.log(`Seeded ${i + 1} pets...`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Insert remaining pets
|
|
392
|
+
if (petData.length > 0) {
|
|
393
|
+
await prisma.pets.createMany({
|
|
394
|
+
data: petData,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
console.log("Finished seeding 50,000 pets.");
|
|
398
|
+
|
|
399
|
+
console.log("Seeding finished.");
|
|
341
400
|
}
|
|
342
401
|
|
|
343
402
|
main()
|
package/readme.md
CHANGED
|
@@ -75,6 +75,44 @@ Jika Anda melakukan setup dengan flag `--full`, database akan terisi dengan akun
|
|
|
75
75
|
|
|
76
76
|
---
|
|
77
77
|
|
|
78
|
+
## 🧠 Zero-Config Redis
|
|
79
|
+
|
|
80
|
+
Lapeh otomatis mendeteksi ketersediaan Redis.
|
|
81
|
+
|
|
82
|
+
1. **Auto-Discovery**: Mencoba terhubung ke Redis URL di `.env` (`REDIS_URL`).
|
|
83
|
+
2. **Smart Fallback**: Jika Redis tidak tersedia atau koneksi gagal, otomatis beralih ke **In-Memory Mock**.
|
|
84
|
+
- Tidak perlu install Redis di local development.
|
|
85
|
+
- Fitur rate-limiting dan caching tetap berjalan (namun data hilang saat restart).
|
|
86
|
+
3. **Production Safety**: Memberikan peringatan log jika berjalan di Production menggunakan Mock.
|
|
87
|
+
|
|
88
|
+
**Force Mock Mode:**
|
|
89
|
+
Anda bisa memaksa menggunakan mock (misal untuk testing) dengan menambahkan env variable:
|
|
90
|
+
|
|
91
|
+
```env
|
|
92
|
+
NO_REDIS=true
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Optional: Menggunakan Real Redis dengan Docker
|
|
96
|
+
|
|
97
|
+
Jika Anda ingin menggunakan Redis yang sebenarnya di local environment, kami telah menyertakan konfigurasi `docker-compose.yml` yang aman (menggunakan ACL).
|
|
98
|
+
|
|
99
|
+
1. Jalankan Redis container:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
docker-compose up -d
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
2. Uncomment konfigurasi Redis di file `.env` Anda:
|
|
106
|
+
|
|
107
|
+
```env
|
|
108
|
+
REDIS_URL="redis://lapeh:12341234@localhost:6379"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> **Credential Default:**
|
|
112
|
+
>
|
|
113
|
+
> - User: `lapeh`
|
|
114
|
+
> - Password: `12341234`
|
|
115
|
+
|
|
78
116
|
## 🛠 Development Tools
|
|
79
117
|
|
|
80
118
|
API Lapeh menyediakan tools untuk mempercepat development, mirip dengan `artisan` di Laravel.
|
|
@@ -14,8 +14,8 @@ if (!fs.existsSync(modelsDir)) {
|
|
|
14
14
|
// Read base schema (datasource & generator)
|
|
15
15
|
let schemaContent = fs.readFileSync(baseFile, 'utf8');
|
|
16
16
|
|
|
17
|
-
// Read all .
|
|
18
|
-
const modelFiles = fs.readdirSync(modelsDir).filter(file => file.endsWith('.
|
|
17
|
+
// Read all .model files in src/models
|
|
18
|
+
const modelFiles = fs.readdirSync(modelsDir).filter(file => file.endsWith('.model'));
|
|
19
19
|
|
|
20
20
|
modelFiles.forEach(file => {
|
|
21
21
|
const content = fs.readFileSync(path.join(modelsDir, file), 'utf8');
|
package/scripts/init-project.js
CHANGED
|
@@ -122,7 +122,22 @@ const selectOption = async (query, options) => {
|
|
|
122
122
|
console.log("\n📦 Installing dependencies...");
|
|
123
123
|
execSync("npm install", { stdio: "inherit", cwd: rootDir });
|
|
124
124
|
|
|
125
|
-
// 4.
|
|
125
|
+
// 4. Create .vscode/settings.json
|
|
126
|
+
console.log("\n🛠️ Configuring VS Code...");
|
|
127
|
+
const vscodeDir = path.join(rootDir, ".vscode");
|
|
128
|
+
if (!fs.existsSync(vscodeDir)) {
|
|
129
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
const settingsFile = path.join(vscodeDir, "settings.json");
|
|
132
|
+
const settingsContent = {
|
|
133
|
+
"files.associations": {
|
|
134
|
+
"*.model": "prisma"
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
fs.writeFileSync(settingsFile, JSON.stringify(settingsContent, null, 2));
|
|
138
|
+
console.log("✅ VS Code configured (.model support added).");
|
|
139
|
+
|
|
140
|
+
// 5. Generate JWT Secret
|
|
126
141
|
console.log("\n🔑 Generating JWT Secret...");
|
|
127
142
|
try {
|
|
128
143
|
execSync("node scripts/generate-jwt-secret.js", {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const controllerName = args[0];
|
|
6
|
+
const isResource = args.includes('-r') || args.includes('--resource');
|
|
7
|
+
|
|
8
|
+
if (!controllerName || controllerName.startsWith('-')) {
|
|
9
|
+
console.error('❌ Please specify the controller name.');
|
|
10
|
+
console.error(' Usage: npm run make:controller <ControllerName> [-r]');
|
|
11
|
+
console.error(' Example: npm run make:controller TestController -r');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const controllersDir = path.join(__dirname, '..', 'src', 'controllers');
|
|
16
|
+
|
|
17
|
+
// Ensure controllers directory exists
|
|
18
|
+
if (!fs.existsSync(controllersDir)) {
|
|
19
|
+
fs.mkdirSync(controllersDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let fileName = controllerName;
|
|
23
|
+
if (!fileName.endsWith('.ts')) {
|
|
24
|
+
fileName += '.ts';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Capitalize first letter if convention is needed, but usually users provide PascalCase
|
|
28
|
+
// We will just use what user provided but ensure .ts extension
|
|
29
|
+
const filePath = path.join(controllersDir, fileName);
|
|
30
|
+
|
|
31
|
+
if (fs.existsSync(filePath)) {
|
|
32
|
+
console.error(`❌ Controller ${fileName} already exists at ${filePath}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let content = '';
|
|
37
|
+
|
|
38
|
+
if (isResource) {
|
|
39
|
+
content = `import { Request, Response } from "express";
|
|
40
|
+
import { prisma } from "../prisma";
|
|
41
|
+
import { sendSuccess, sendError } from "../utils/response";
|
|
42
|
+
import { getPagination, buildPaginationMeta } from "../utils/pagination";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Display a listing of the resource.
|
|
46
|
+
*/
|
|
47
|
+
export async function index(req: Request, res: Response) {
|
|
48
|
+
const { page, perPage, skip, take } = getPagination(req.query);
|
|
49
|
+
|
|
50
|
+
// TODO: Add search logic
|
|
51
|
+
const where: any = {};
|
|
52
|
+
|
|
53
|
+
// TODO: Replace 'model' with your actual model name
|
|
54
|
+
/*
|
|
55
|
+
const [data, total] = await Promise.all([
|
|
56
|
+
prisma.model.findMany({
|
|
57
|
+
where,
|
|
58
|
+
skip,
|
|
59
|
+
take,
|
|
60
|
+
orderBy: { created_at: "desc" },
|
|
61
|
+
}),
|
|
62
|
+
prisma.model.count({ where }),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
const serialized = data.map((item: any) => ({
|
|
66
|
+
...item,
|
|
67
|
+
id: item.id.toString(),
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
const meta = buildPaginationMeta(page, perPage, total);
|
|
71
|
+
|
|
72
|
+
sendSuccess(res, 200, "Data retrieved successfully", {
|
|
73
|
+
data: serialized,
|
|
74
|
+
meta,
|
|
75
|
+
});
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
sendSuccess(res, 200, "Index method", { message: "Implement me" });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Display the specified resource.
|
|
83
|
+
*/
|
|
84
|
+
export async function show(req: Request, res: Response) {
|
|
85
|
+
const { id } = req.params;
|
|
86
|
+
|
|
87
|
+
// TODO: Replace 'model' with your actual model name
|
|
88
|
+
/*
|
|
89
|
+
const item = await prisma.model.findUnique({
|
|
90
|
+
where: { id: BigInt(id) },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!item) {
|
|
94
|
+
sendError(res, 404, "Data not found");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
sendSuccess(res, 200, "Data retrieved successfully", {
|
|
99
|
+
...item,
|
|
100
|
+
id: item.id.toString(),
|
|
101
|
+
});
|
|
102
|
+
*/
|
|
103
|
+
sendSuccess(res, 200, "Show method", { id, message: "Implement me" });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Store a newly created resource in storage.
|
|
108
|
+
*/
|
|
109
|
+
export async function store(req: Request, res: Response) {
|
|
110
|
+
// TODO: Add validation
|
|
111
|
+
// const parsed = createSchema.safeParse(req.body);
|
|
112
|
+
// if (!parsed.success) { ... }
|
|
113
|
+
|
|
114
|
+
// TODO: Replace 'model' with your actual model name
|
|
115
|
+
/*
|
|
116
|
+
const item = await prisma.model.create({
|
|
117
|
+
data: {
|
|
118
|
+
...req.body,
|
|
119
|
+
created_at: new Date(),
|
|
120
|
+
updated_at: new Date(),
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
sendSuccess(res, 201, "Data created successfully", {
|
|
125
|
+
...item,
|
|
126
|
+
id: item.id.toString(),
|
|
127
|
+
});
|
|
128
|
+
*/
|
|
129
|
+
sendSuccess(res, 201, "Store method", { message: "Implement me" });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Update the specified resource in storage.
|
|
134
|
+
*/
|
|
135
|
+
export async function update(req: Request, res: Response) {
|
|
136
|
+
const { id } = req.params;
|
|
137
|
+
|
|
138
|
+
// TODO: Add validation
|
|
139
|
+
// const parsed = updateSchema.safeParse(req.body);
|
|
140
|
+
|
|
141
|
+
// TODO: Replace 'model' with your actual model name
|
|
142
|
+
/*
|
|
143
|
+
const existing = await prisma.model.findUnique({
|
|
144
|
+
where: { id: BigInt(id) },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!existing) {
|
|
148
|
+
sendError(res, 404, "Data not found");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const updated = await prisma.model.update({
|
|
153
|
+
where: { id: BigInt(id) },
|
|
154
|
+
data: {
|
|
155
|
+
...req.body,
|
|
156
|
+
updated_at: new Date(),
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
sendSuccess(res, 200, "Data updated successfully", {
|
|
161
|
+
...updated,
|
|
162
|
+
id: updated.id.toString(),
|
|
163
|
+
});
|
|
164
|
+
*/
|
|
165
|
+
sendSuccess(res, 200, "Update method", { id, message: "Implement me" });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Remove the specified resource from storage.
|
|
170
|
+
*/
|
|
171
|
+
export async function destroy(req: Request, res: Response) {
|
|
172
|
+
const { id } = req.params;
|
|
173
|
+
|
|
174
|
+
// TODO: Replace 'model' with your actual model name
|
|
175
|
+
/*
|
|
176
|
+
const existing = await prisma.model.findUnique({
|
|
177
|
+
where: { id: BigInt(id) },
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (!existing) {
|
|
181
|
+
sendError(res, 404, "Data not found");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
await prisma.model.delete({
|
|
186
|
+
where: { id: BigInt(id) },
|
|
187
|
+
});
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
sendSuccess(res, 200, "Data deleted successfully", null);
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
193
|
+
} else {
|
|
194
|
+
content = `import { Request, Response } from "express";
|
|
195
|
+
import { sendSuccess, sendError } from "../utils/response";
|
|
196
|
+
|
|
197
|
+
export async function index(req: Request, res: Response) {
|
|
198
|
+
sendSuccess(res, 200, "Hello from ${controllerName}", null);
|
|
199
|
+
}
|
|
200
|
+
`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fs.writeFileSync(filePath, content);
|
|
204
|
+
|
|
205
|
+
console.log(`✅ Controller created: src/controllers/${fileName}`);
|