create-craftjs 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.
Files changed (66) hide show
  1. package/README.md +137 -0
  2. package/bin/index.js +158 -0
  3. package/package.json +24 -0
  4. package/template/.dockerignore +4 -0
  5. package/template/Dockerfile +12 -0
  6. package/template/babel.config.json +3 -0
  7. package/template/craft/commands/build.js +15 -0
  8. package/template/craft/commands/db-fresh.js +22 -0
  9. package/template/craft/commands/db-generate.js +23 -0
  10. package/template/craft/commands/db-migrate.js +22 -0
  11. package/template/craft/commands/dev.js +16 -0
  12. package/template/craft/commands/key-generate.js +41 -0
  13. package/template/craft/commands/make-apidocs.js +121 -0
  14. package/template/craft/commands/make-command.js +38 -0
  15. package/template/craft/commands/make-dto.js +39 -0
  16. package/template/craft/commands/make-middleware.js +46 -0
  17. package/template/craft/commands/make-repository.js +36 -0
  18. package/template/craft/commands/make-route.js +88 -0
  19. package/template/craft/commands/make-service.js +39 -0
  20. package/template/craft/commands/make-test.js +48 -0
  21. package/template/craft/commands/make-utils.js +30 -0
  22. package/template/craft/commands/make-validation.js +42 -0
  23. package/template/craft/commands/make-view.js +42 -0
  24. package/template/craft/commands/start.js +29 -0
  25. package/template/craft/commands/test.js +20 -0
  26. package/template/craft.js +256 -0
  27. package/template/nodemon.json +6 -0
  28. package/template/package-lock.json +8777 -0
  29. package/template/package.json +79 -0
  30. package/template/prisma/migrations/20250518142257_create_table_users/migration.sql +13 -0
  31. package/template/prisma/migrations/migration_lock.toml +3 -0
  32. package/template/prisma/schema.prisma +22 -0
  33. package/template/prisma/seed.ts +29 -0
  34. package/template/public/assets/images/default-user.png +0 -0
  35. package/template/src/apidocs/auth-docs.ts +314 -0
  36. package/template/src/apidocs/users-docs.ts +240 -0
  37. package/template/src/config/database.ts +90 -0
  38. package/template/src/config/env.ts +29 -0
  39. package/template/src/config/logger.ts +116 -0
  40. package/template/src/config/web.ts +40 -0
  41. package/template/src/controllers/auth-controller.ts +88 -0
  42. package/template/src/controllers/user-controller.ts +79 -0
  43. package/template/src/dtos/list-dto.ts +12 -0
  44. package/template/src/dtos/user-dto.ts +57 -0
  45. package/template/src/main.ts +28 -0
  46. package/template/src/middleware/auth-middleware.ts +44 -0
  47. package/template/src/middleware/error-middleware.ts +27 -0
  48. package/template/src/middleware/http-logger-middleware.ts +31 -0
  49. package/template/src/repositories/user-repository.ts +75 -0
  50. package/template/src/routes/auth-route.ts +20 -0
  51. package/template/src/routes/main-route.ts +25 -0
  52. package/template/src/routes/user-route.ts +35 -0
  53. package/template/src/services/auth-service.ts +162 -0
  54. package/template/src/services/user-service.ts +102 -0
  55. package/template/src/types/type-request.ts +6 -0
  56. package/template/src/utils/async-handler.ts +9 -0
  57. package/template/src/utils/response-error.ts +10 -0
  58. package/template/src/utils/response.ts +60 -0
  59. package/template/src/utils/swagger.ts +135 -0
  60. package/template/src/utils/validation.ts +7 -0
  61. package/template/src/validations/user-validation.ts +127 -0
  62. package/template/src/views/index.ejs +6 -0
  63. package/template/src/views/layouts/main.ejs +14 -0
  64. package/template/src/views/partials/header.ejs +3 -0
  65. package/template/test/user.test.ts +16 -0
  66. package/template/tsconfig.json +13 -0
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "craftjs",
3
+ "description": "A starter kit backend framework powered by Express, TypeScript, EJS Engine, and Prisma — designed for rapid development, simplicity, and scalability.",
4
+ "version": "1.0.4",
5
+ "keywords": [
6
+ "express",
7
+ "typescript",
8
+ "prisma",
9
+ "api"
10
+ ],
11
+ "license": "UNLICENSED",
12
+ "main": "main.js",
13
+ "scripts": {
14
+ "craft": "node craft.js",
15
+ "start": "node craft start",
16
+ "dev": "node craft dev",
17
+ "build": "node craft build",
18
+ "test": "node craft test"
19
+ },
20
+ "jest": {
21
+ "transform": {
22
+ "^.+\\.[t|j]sx?$": "babel-jest"
23
+ }
24
+ },
25
+ "prisma": {
26
+ "seed": "ts-node prisma/seed.ts"
27
+ },
28
+ "dependencies": {
29
+ "@prisma/client": "^6.6.0",
30
+ "argon2": "^0.41.1",
31
+ "chalk": "^4.1.2",
32
+ "cookie-parser": "^1.4.7",
33
+ "cors": "^2.8.5",
34
+ "crypto": "^1.0.1",
35
+ "dotenv": "^16.5.0",
36
+ "ejs": "^3.1.10",
37
+ "express": "^5.1.0",
38
+ "express-ejs-layouts": "^2.5.1",
39
+ "inquirer": "^8.2.6",
40
+ "jsonwebtoken": "^9.0.2",
41
+ "luxon": "^3.6.1",
42
+ "swagger-jsdoc": "^6.2.8",
43
+ "swagger-themes": "^1.4.3",
44
+ "swagger-ui-express": "^5.0.1",
45
+ "winston": "^3.17.0",
46
+ "winston-daily-rotate-file": "^5.0.0",
47
+ "yargs": "^17.7.2",
48
+ "zod": "^3.24.3"
49
+ },
50
+ "devDependencies": {
51
+ "@babel/preset-env": "^7.27.2",
52
+ "@babel/preset-typescript": "^7.27.0",
53
+ "@jest/globals": "^29.7.0",
54
+ "@types/argon2": "^0.14.1",
55
+ "@types/cookie-parser": "^1.4.8",
56
+ "@types/cors": "^2.8.17",
57
+ "@types/dotenv": "^6.1.1",
58
+ "@types/ejs": "^3.1.5",
59
+ "@types/express": "^5.0.1",
60
+ "@types/express-ejs-layouts": "^2.5.4",
61
+ "@types/jest": "^29.5.14",
62
+ "@types/jsonwebtoken": "^9.0.9",
63
+ "@types/luxon": "^3.6.2",
64
+ "@types/node": "^22.15.18",
65
+ "@types/supertest": "^6.0.3",
66
+ "@types/swagger-jsdoc": "^6.0.4",
67
+ "@types/swagger-ui-express": "^4.1.8",
68
+ "babel-jest": "^29.7.0",
69
+ "jest": "^29.7.0",
70
+ "nodemon": "^3.1.9",
71
+ "prettier": "^3.5.3",
72
+ "prisma": "^6.6.0",
73
+ "supertest": "^7.1.0",
74
+ "ts-jest": "^29.3.4",
75
+ "ts-node": "^10.9.2",
76
+ "tsx": "^4.19.4",
77
+ "typescript": "^5.8.3"
78
+ }
79
+ }
@@ -0,0 +1,13 @@
1
+ -- CreateTable
2
+ CREATE TABLE `users` (
3
+ `id` VARCHAR(191) NOT NULL,
4
+ `fullName` VARCHAR(100) NOT NULL,
5
+ `email` VARCHAR(100) NOT NULL,
6
+ `password` VARCHAR(255) NOT NULL,
7
+ `created_at` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
8
+ `updated_at` TIMESTAMP(0) NOT NULL,
9
+ `deleted_at` TIMESTAMP(0) NULL,
10
+
11
+ UNIQUE INDEX `users_email_key`(`email`),
12
+ PRIMARY KEY (`id`)
13
+ ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,3 @@
1
+ # Please do not edit this file manually
2
+ # It should be added in your version-control system (e.g., Git)
3
+ provider = "mysql"
@@ -0,0 +1,22 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ binaryTargets = ["native", "debian-openssl-1.1.x"]
4
+ output = "../node_modules/.prisma/client"
5
+ }
6
+
7
+ datasource db {
8
+ provider = "mysql"
9
+ url = env("DATABASE_URL")
10
+ }
11
+
12
+ model User {
13
+ id String @id @default(uuid())
14
+ fullName String @db.VarChar(100)
15
+ email String @unique @db.VarChar(100)
16
+ password String @db.VarChar(255)
17
+ created_at DateTime @default(now()) @db.Timestamp(0)
18
+ updated_at DateTime @updatedAt @db.Timestamp(0)
19
+ deleted_at DateTime? @db.Timestamp(0)
20
+
21
+ @@map("users")
22
+ }
@@ -0,0 +1,29 @@
1
+ import { PrismaClient } from "@prisma/client";
2
+ import * as argon2 from "argon2";
3
+ import dotenv from "dotenv";
4
+
5
+ dotenv.config();
6
+
7
+ const prisma = new PrismaClient();
8
+
9
+ async function main() {
10
+ await prisma.user.upsert({
11
+ where: { email: "tes@gmail.com" },
12
+ update: {},
13
+ create: {
14
+ fullName: "Tes",
15
+ email: "tes@gmail.com",
16
+ password: await argon2.hash("12345678"),
17
+ },
18
+ });
19
+
20
+ console.log("Users seeded");
21
+ }
22
+ main()
23
+ .catch((e) => {
24
+ console.error(e);
25
+ process.exit(1);
26
+ })
27
+ .finally(async () => {
28
+ await prisma.$disconnect();
29
+ });
@@ -0,0 +1,314 @@
1
+ /**
2
+ * @swagger
3
+ * /api/auth/register:
4
+ * post:
5
+ * summary: Register User
6
+ * tags: [Auth]
7
+ * requestBody:
8
+ * required: true
9
+ * content:
10
+ * application/json:
11
+ * schema:
12
+ * type: object
13
+ * required:
14
+ * - fullName
15
+ * - email
16
+ * - password
17
+ * properties:
18
+ * fullName:
19
+ * type: string
20
+ * email:
21
+ * type: string
22
+ * password:
23
+ * type: string
24
+ * responses:
25
+ * 201:
26
+ * description: Register berhasil
27
+ * content:
28
+ * application/json:
29
+ * schema:
30
+ * type: object
31
+ * properties:
32
+ * status:
33
+ * type: boolean
34
+ * status_code:
35
+ * type: integer
36
+ * message:
37
+ * type: string
38
+ * example: Register Berhasil
39
+ * data:
40
+ * type: object
41
+ * properties:
42
+ * id:
43
+ * type: string
44
+ * format: uuid
45
+ * fullName:
46
+ * type: string
47
+ * email:
48
+ * type: string
49
+ * 400:
50
+ * $ref: '#/components/responses/ValidationError'
51
+ * 409:
52
+ * description: Konflik - akun sudah terdaftar
53
+ * content:
54
+ * application/json:
55
+ * schema:
56
+ * type: object
57
+ * properties:
58
+ * status:
59
+ * type: boolean
60
+ * status_code:
61
+ * type: integer
62
+ * message:
63
+ * type: string
64
+ * example: Akun Sudah Terdaftar!
65
+ */
66
+ /**
67
+ * @swagger
68
+ * /api/auth/login:
69
+ * post:
70
+ * summary: Login User
71
+ * tags: [Auth]
72
+ * requestBody:
73
+ * required: true
74
+ * content:
75
+ * application/json:
76
+ * schema:
77
+ * type: object
78
+ * required:
79
+ * - email
80
+ * - password
81
+ * properties:
82
+ * email:
83
+ * type: string
84
+ * example: tes@gmail.com
85
+ * password:
86
+ * type: string
87
+ * example: 12345678
88
+ * responses:
89
+ * 200:
90
+ * description: Login berhasil
91
+ * content:
92
+ * application/json:
93
+ * schema:
94
+ * type: object
95
+ * properties:
96
+ * status:
97
+ * type: boolean
98
+ * example: true
99
+ * status_code:
100
+ * type: integer
101
+ * example: 200
102
+ * message:
103
+ * type: string
104
+ * example: Login Berhasil
105
+ * data:
106
+ * type: object
107
+ * properties:
108
+ * user:
109
+ * type: object
110
+ * properties:
111
+ * id:
112
+ * type: string
113
+ * fullName:
114
+ * type: string
115
+ * email:
116
+ * type: string
117
+ * accessToken:
118
+ * type: string
119
+ * refreshToken:
120
+ * type: string
121
+ * 400:
122
+ * $ref: '#/components/responses/ValidationError'
123
+ * 401:
124
+ * description: Login gagal - username,email atau password salah
125
+ * content:
126
+ * application/json:
127
+ * example:
128
+ * status: false
129
+ * status_code: 401
130
+ * message: Gagal Login! Detail login salah
131
+ */
132
+
133
+ // me
134
+ /**
135
+ * @swagger
136
+ * /api/auth/me:
137
+ * get:
138
+ * summary: Get informasi detail user yang sedang login
139
+ * tags: [Auth]
140
+ * security:
141
+ * - bearerAuth: []
142
+ * responses:
143
+ * 200:
144
+ * description: Get Detail User Berhasil
145
+ * content:
146
+ * application/json:
147
+ * schema:
148
+ * type: object
149
+ * properties:
150
+ * status:
151
+ * type: boolean
152
+ * status_code:
153
+ * type: integer
154
+ * message:
155
+ * type: string
156
+ * data:
157
+ * type: object
158
+ * properties:
159
+ * id:
160
+ * type: string
161
+ * fullName:
162
+ * type: string
163
+ * email:
164
+ * type: string
165
+ * created_at:
166
+ * type: string
167
+ * format: date-time
168
+ * updated_at:
169
+ * type: string
170
+ * format: date-time
171
+ * deleted_at:
172
+ * type: string
173
+ * format: date-time
174
+ * nullable: true
175
+ * 401:
176
+ * $ref: '#/components/responses/UnauthorizedATError'
177
+ */
178
+
179
+ // Logout
180
+ /**
181
+ * @swagger
182
+ * /api/auth/logout:
183
+ * post:
184
+ * summary: Logout User
185
+ * tags: [Auth]
186
+ * security:
187
+ * - bearerAuth: []
188
+ * responses:
189
+ * 200:
190
+ * description: Logout berhasil
191
+ * content:
192
+ * application/json:
193
+ * schema:
194
+ * type: object
195
+ * properties:
196
+ * status:
197
+ * type: boolean
198
+ * status_code:
199
+ * type: integer
200
+ * message:
201
+ * type: string
202
+ * example:
203
+ * status: true
204
+ * status_code: 200
205
+ * message: Logout Berhasil
206
+ * 401:
207
+ * $ref: '#/components/responses/UnauthorizedNotLoginError'
208
+ */
209
+
210
+ // refresh token
211
+ /**
212
+ * @swagger
213
+ * /api/auth/refresh-token:
214
+ * get:
215
+ * summary: Mendapatkan access token baru dari refresh token
216
+ * tags: [Auth]
217
+ * responses:
218
+ * 200:
219
+ * description: Access token berhasil diperbarui
220
+ * content:
221
+ * application/json:
222
+ * schema:
223
+ * type: object
224
+ * properties:
225
+ * status:
226
+ * type: boolean
227
+ * status_code:
228
+ * type: integer
229
+ * message:
230
+ * type: string
231
+ * example: Get Access Token Berhasil
232
+ * data:
233
+ * type: object
234
+ * properties:
235
+ * user:
236
+ * type: object
237
+ * properties:
238
+ * id:
239
+ * type: string
240
+ * fullName:
241
+ * type: string
242
+ * email:
243
+ * type: string
244
+ * password:
245
+ * type: string
246
+ * created_at:
247
+ * type: string
248
+ * updated_at:
249
+ * type: string
250
+ * deleted_at:
251
+ * type: string
252
+ * nullable: true
253
+ * accessToken:
254
+ * type: string
255
+ * 401:
256
+ * $ref: '#/components/responses/UnauthorizedNotLoginError'
257
+ */
258
+ // Update profile
259
+ /**
260
+ * @swagger
261
+ * /api/auth/update-profile:
262
+ * put:
263
+ * summary: Memperbarui profil pengguna yang sedang login
264
+ * tags: [Auth]
265
+ * security:
266
+ * - bearerAuth: []
267
+ * requestBody:
268
+ * required: true
269
+ * content:
270
+ * application/json:
271
+ * schema:
272
+ * type: object
273
+ * properties:
274
+ * fullName:
275
+ * type: string
276
+ * example: tes
277
+ * email:
278
+ * type: string
279
+ * example: tes@gmail.com
280
+ * responses:
281
+ * 200:
282
+ * description: Data Berhasil Diupdate
283
+ * content:
284
+ * application/json:
285
+ * schema:
286
+ * type: object
287
+ * properties:
288
+ * status:
289
+ * type: boolean
290
+ * example: true
291
+ * status_code:
292
+ * type: integer
293
+ * example: 200
294
+ * message:
295
+ * type: string
296
+ * example: Data Berhasil Diupdate
297
+ * data:
298
+ * type: object
299
+ * properties:
300
+ * id:
301
+ * type: string
302
+ * format: uuid
303
+ * example: c1fa015f-48b0-456f-8c41-baf54ce092f5
304
+ * fullName:
305
+ * type: string
306
+ * example: tes update
307
+ * email:
308
+ * type: string
309
+ * example: tes@gmail.com
310
+ * 400:
311
+ * $ref: '#/components/responses/ValidationError'
312
+ * 401:
313
+ * $ref: '#/components/responses/UnauthorizedATError'
314
+ */
@@ -0,0 +1,240 @@
1
+ /**
2
+ * @swagger
3
+ * /api/users:
4
+ * get:
5
+ * summary: Mengambil daftar semua user
6
+ * tags: [Users]
7
+ * security:
8
+ * - bearerAuth: []
9
+ * parameters:
10
+ * - in: query
11
+ * name: page
12
+ * schema:
13
+ * type: integer
14
+ * required: false
15
+ * description: Nomor halaman
16
+ * - in: query
17
+ * name: take
18
+ * schema:
19
+ * type: integer
20
+ * required: false
21
+ * description: Jumlah data per halaman
22
+ * - in: query
23
+ * name: name
24
+ * schema:
25
+ * type: string
26
+ * required: false
27
+ * description: Filter berdasarkan nama (fullName)
28
+ * responses:
29
+ * 200:
30
+ * description: Berhasil Get All Data
31
+ * content:
32
+ * application/json:
33
+ * schema:
34
+ * type: object
35
+ * properties:
36
+ * status:
37
+ * type: boolean
38
+ * status_code:
39
+ * type: integer
40
+ * message:
41
+ * type: string
42
+ * data:
43
+ * type: object
44
+ * properties:
45
+ * data:
46
+ * type: array
47
+ * items:
48
+ * type: object
49
+ * properties:
50
+ * id:
51
+ * type: string
52
+ * format: uuid
53
+ * fullName:
54
+ * type: string
55
+ * email:
56
+ * type: string
57
+ * username:
58
+ * type: string
59
+ * password:
60
+ * type: string
61
+ * description: (Hashed password)
62
+ * image_id:
63
+ * type: string
64
+ * image_url:
65
+ * type: string
66
+ * format: uri
67
+ * is_verify:
68
+ * type: boolean
69
+ * created_at:
70
+ * type: string
71
+ * format: date-time
72
+ * updated_at:
73
+ * type: string
74
+ * format: date-time
75
+ * deleted_at:
76
+ * type: string
77
+ * format: date-time
78
+ * nullable: true
79
+ * role_id:
80
+ * type: string
81
+ * format: uuid
82
+ * total_data:
83
+ * type: integer
84
+ * paging:
85
+ * type: object
86
+ * properties:
87
+ * current_page:
88
+ * type: integer
89
+ * total_page:
90
+ * type: integer
91
+
92
+ */
93
+ /**
94
+ * @swagger
95
+ * /api/users/{id}:
96
+ * get:
97
+ * summary: Mengambil detail user berdasarkan ID
98
+ * tags: [Users]
99
+ * security:
100
+ * - bearerAuth: []
101
+ * parameters:
102
+ * - in: path
103
+ * name: id
104
+ * schema:
105
+ * type: string
106
+ * format: uuid
107
+ * required: true
108
+ * description: ID user yang akan diambil
109
+ * responses:
110
+ * 200:
111
+ * description: Berhasil Get Detail Data
112
+ * content:
113
+ * application/json:
114
+ * schema:
115
+ * type: object
116
+ * properties:
117
+ * status: { type: boolean }
118
+ * status_code: { type: integer }
119
+ * message: { type: string }
120
+ * data:
121
+ * type: object
122
+ * properties:
123
+ * id: { type: string, format: uuid }
124
+ * fullName: { type: string }
125
+ * email: { type: string, format: email }
126
+ * username: { type: string }
127
+ * role_id: { type: string, format: uuid }
128
+ */
129
+ /**
130
+ * @swagger
131
+ * /api/users:
132
+ * post:
133
+ * summary: Menambahkan user baru
134
+ * tags: [Users]
135
+ * security:
136
+ * - bearerAuth: []
137
+ * - apiKeyAuth: []
138
+ * requestBody:
139
+ * required: true
140
+ * content:
141
+ * application/json:
142
+ * schema:
143
+ * type: object
144
+ * required: [ fullName, email, password ]
145
+ * properties:
146
+ * fullName: { type: string }
147
+ * email: { type: string, format: email }
148
+ * password: { type: string, format: password }
149
+ * responses:
150
+ * 201:
151
+ * description: Data Berhasil Ditambahkan
152
+ * content:
153
+ * application/json:
154
+ * schema:
155
+ * type: object
156
+ * properties:
157
+ * status: { type: boolean }
158
+ * status_code: { type: integer }
159
+ * message: { type: string }
160
+ * data:
161
+ * type: object
162
+ * properties:
163
+ * id: { type: string, format: uuid }
164
+ * fullName: { type: string }
165
+ * email: { type: string }
166
+ */
167
+ /**
168
+ * @swagger
169
+ * /api/users/{id}:
170
+ * put:
171
+ * summary: Memperbarui data user berdasarkan ID
172
+ * tags: [Users]
173
+ * security:
174
+ * - bearerAuth: []
175
+ * parameters:
176
+ * - in: path
177
+ * name: id
178
+ * schema:
179
+ * type: string
180
+ * format: uuid
181
+ * required: true
182
+ * description: ID user yang akan diupdate
183
+ * requestBody:
184
+ * required: true
185
+ * content:
186
+ * application/json:
187
+ * schema:
188
+ * type: object
189
+ * properties:
190
+ * fullName: { type: string }
191
+ * email: { type: string, format: email }
192
+ * responses:
193
+ * 200:
194
+ * description: Data Berhasil Diupdate
195
+ * content:
196
+ * application/json:
197
+ * schema:
198
+ * type: object
199
+ * properties:
200
+ * status: { type: boolean }
201
+ * status_code: { type: integer }
202
+ * message: { type: string }
203
+ * data:
204
+ * type: object
205
+ * properties:
206
+ * id: { type: string, format: uuid }
207
+ * fullName: { type: string }
208
+ * email: { type: string }
209
+ */
210
+ /**
211
+ * @swagger
212
+ * /api/users/{id}:
213
+ * delete:
214
+ * summary: Menghapus user secara permanen
215
+ * tags: [Users]
216
+ * security:
217
+ * - bearerAuth: []
218
+ * parameters:
219
+ * - in: path
220
+ * name: id
221
+ * schema:
222
+ * type: string
223
+ * format: uuid
224
+ * required: true
225
+ * description: ID user yang akan dihapus permanen
226
+ * responses:
227
+ * 200:
228
+ * description: Data Berhasil Dihapus
229
+ * content:
230
+ * application/json:
231
+ * schema:
232
+ * type: object
233
+ * properties:
234
+ * status: { type: boolean }
235
+ * status_code: { type: integer }
236
+ * message: { type: string }
237
+ * data:
238
+ * type: object
239
+ * nullable: true
240
+ */