create-craftjs 2.0.3 → 2.0.5
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/bin/index.js +2 -2
- package/package.json +1 -1
- package/template/docker-compose.yml +1 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/src/apidocs/auth-docs.ts +6 -72
- package/template/src/apidocs/users-docs.ts +8 -7
- package/template/src/config/env.ts +1 -1
- package/template/src/config/web.ts +4 -1
- package/template/src/database/seeders/seed.ts +3 -4
- package/template/src/main.ts +5 -3
- package/template/src/services/user-service.ts +4 -2
- package/template/src/utils/swagger.ts +5 -2
- package/template/src/validations/user-validation.ts +6 -9
package/bin/index.js
CHANGED
|
@@ -81,9 +81,9 @@ const ask = (question) => {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
const envContent = `APP_NAME="${projectName}"
|
|
84
|
-
|
|
84
|
+
HOST="localhost"
|
|
85
85
|
BASE_URL="http://localhost:4444"
|
|
86
|
-
|
|
86
|
+
APP_SECRET=
|
|
87
87
|
NODE_ENV="development"
|
|
88
88
|
TZ="Asia/Jakarta"
|
|
89
89
|
DATETIME_FORMAT="dd-MM-yyyy HH:mm:ss"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-craftjs",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "A starter kit backend framework powered by Express, TypeScript, EJS Engine, and Prisma — designed for rapid development, simplicity, and scalability.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-craftjs": "bin/index.js"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "craftjs",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "craftjs",
|
|
9
|
-
"version": "2.0.
|
|
9
|
+
"version": "2.0.4",
|
|
10
10
|
"license": "UNLICENSED",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@prisma/adapter-mariadb": "^7.2.0",
|
package/template/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "craftjs",
|
|
3
3
|
"description": "A starter kit backend framework powered by Express, TypeScript, EJS Engine, and Prisma — designed for rapid development, simplicity, and scalability.",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.5",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"express",
|
|
7
7
|
"typescript",
|
|
@@ -17,10 +17,13 @@
|
|
|
17
17
|
* properties:
|
|
18
18
|
* full_name:
|
|
19
19
|
* type: string
|
|
20
|
+
* example: John Doe
|
|
20
21
|
* email:
|
|
21
22
|
* type: string
|
|
23
|
+
* example: john@example.com
|
|
22
24
|
* password:
|
|
23
25
|
* type: string
|
|
26
|
+
* example: 12345678
|
|
24
27
|
* responses:
|
|
25
28
|
* 201:
|
|
26
29
|
* description: Register berhasil
|
|
@@ -31,8 +34,10 @@
|
|
|
31
34
|
* properties:
|
|
32
35
|
* status:
|
|
33
36
|
* type: boolean
|
|
37
|
+
* example: true
|
|
34
38
|
* status_code:
|
|
35
|
-
*
|
|
39
|
+
* type: integer
|
|
40
|
+
* example: 201
|
|
36
41
|
* message:
|
|
37
42
|
* type: string
|
|
38
43
|
* example: Register Berhasil
|
|
@@ -48,20 +53,6 @@
|
|
|
48
53
|
* type: string
|
|
49
54
|
* 400:
|
|
50
55
|
* $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
56
|
*/
|
|
66
57
|
/**
|
|
67
58
|
* @swagger
|
|
@@ -235,60 +226,3 @@
|
|
|
235
226
|
* 401:
|
|
236
227
|
* $ref: '#/components/responses/UnauthorizedNotLoginError'
|
|
237
228
|
*/
|
|
238
|
-
// Update profile
|
|
239
|
-
/**
|
|
240
|
-
* @swagger
|
|
241
|
-
* /api/auth/update-profile:
|
|
242
|
-
* put:
|
|
243
|
-
* summary: Memperbarui profil pengguna yang sedang login
|
|
244
|
-
* tags: [Auth]
|
|
245
|
-
* security:
|
|
246
|
-
* - bearerAuth: []
|
|
247
|
-
* requestBody:
|
|
248
|
-
* required: true
|
|
249
|
-
* content:
|
|
250
|
-
* application/json:
|
|
251
|
-
* schema:
|
|
252
|
-
* type: object
|
|
253
|
-
* properties:
|
|
254
|
-
* full_name:
|
|
255
|
-
* type: string
|
|
256
|
-
* example: tes
|
|
257
|
-
* email:
|
|
258
|
-
* type: string
|
|
259
|
-
* example: tes@gmail.com
|
|
260
|
-
* responses:
|
|
261
|
-
* 200:
|
|
262
|
-
* description: Data Berhasil Diupdate
|
|
263
|
-
* content:
|
|
264
|
-
* application/json:
|
|
265
|
-
* schema:
|
|
266
|
-
* type: object
|
|
267
|
-
* properties:
|
|
268
|
-
* status:
|
|
269
|
-
* type: boolean
|
|
270
|
-
* example: true
|
|
271
|
-
* status_code:
|
|
272
|
-
* type: integer
|
|
273
|
-
* example: 200
|
|
274
|
-
* message:
|
|
275
|
-
* type: string
|
|
276
|
-
* example: Data Berhasil Diupdate
|
|
277
|
-
* data:
|
|
278
|
-
* type: object
|
|
279
|
-
* properties:
|
|
280
|
-
* id:
|
|
281
|
-
* type: string
|
|
282
|
-
* format: uuid
|
|
283
|
-
* example: c1fa015f-48b0-456f-8c41-baf54ce092f5
|
|
284
|
-
* full_name:
|
|
285
|
-
* type: string
|
|
286
|
-
* example: tes update
|
|
287
|
-
* email:
|
|
288
|
-
* type: string
|
|
289
|
-
* example: tes@gmail.com
|
|
290
|
-
* 400:
|
|
291
|
-
* $ref: '#/components/responses/ValidationError'
|
|
292
|
-
* 401:
|
|
293
|
-
* $ref: '#/components/responses/UnauthorizedATError'
|
|
294
|
-
*/
|
|
@@ -144,17 +144,18 @@
|
|
|
144
144
|
* schema:
|
|
145
145
|
* type: object
|
|
146
146
|
* properties:
|
|
147
|
-
* status: { type: boolean }
|
|
148
|
-
* status_code: { type: integer }
|
|
149
|
-
* message: { type: string }
|
|
147
|
+
* status: { type: boolean, example: true }
|
|
148
|
+
* status_code: { type: integer, example: 200 }
|
|
149
|
+
* message: { type: string, example: Berhasil Get Detail Data }
|
|
150
150
|
* data:
|
|
151
151
|
* type: object
|
|
152
152
|
* properties:
|
|
153
153
|
* id: { type: string, format: uuid }
|
|
154
|
-
* full_name: { type: string }
|
|
155
|
-
* email: { type: string, format: email }
|
|
156
|
-
*
|
|
157
|
-
*
|
|
154
|
+
* full_name: { type: string, example: John Doe }
|
|
155
|
+
* email: { type: string, format: email, example: johndoe@example.com }
|
|
156
|
+
* created_at: { type: string, format: datetime, example: 18-01-2026 21:55:22 }
|
|
157
|
+
* updated_at: { type: string, format: datetime, example: 18-01-2026 21:55:22 }
|
|
158
|
+
* deleted_at: { type: string, format: datetime, example: 18-01-2026 21:55:22, nullable: true }
|
|
158
159
|
*/
|
|
159
160
|
/**
|
|
160
161
|
* @swagger
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import "dotenv/config";
|
|
3
3
|
|
|
4
4
|
const envSchema = z.object({
|
|
5
|
+
HOST: z.string().default("localhost"),
|
|
5
6
|
APP_NAME: z.string().default("Craft JS"),
|
|
6
7
|
APP_SECRET: z.string(),
|
|
7
8
|
NODE_ENV: z
|
|
@@ -18,7 +19,6 @@ const envSchema = z.object({
|
|
|
18
19
|
DATABASE_NAME: z.string(),
|
|
19
20
|
DATABASE_CONNECTION_LIMIT: z.coerce.number().default(4444),
|
|
20
21
|
BASE_URL: z.string().url(),
|
|
21
|
-
BASE_API_URL: z.string().url(),
|
|
22
22
|
CLIENT_URL: z
|
|
23
23
|
.string()
|
|
24
24
|
.optional()
|
|
@@ -13,6 +13,7 @@ import { setupSwagger } from "@utils/swagger";
|
|
|
13
13
|
import { mainRouter } from "@routes/main-route";
|
|
14
14
|
|
|
15
15
|
export const web = express();
|
|
16
|
+
web.set("trust proxy", true);
|
|
16
17
|
// EJS View Engine Setup
|
|
17
18
|
// web.set("view engine", "ejs");
|
|
18
19
|
// web.set("views", path.join(__dirname, "..", "views"));
|
|
@@ -33,7 +34,9 @@ web.use(express.static("public"));
|
|
|
33
34
|
web.use(httpLogger);
|
|
34
35
|
|
|
35
36
|
// Swagger Setup
|
|
36
|
-
|
|
37
|
+
if (env.NODE_ENV !== "production") {
|
|
38
|
+
setupSwagger(web);
|
|
39
|
+
}
|
|
37
40
|
|
|
38
41
|
// Routes
|
|
39
42
|
web.use(mainRouter);
|
|
@@ -4,19 +4,18 @@ import dotenv from "dotenv";
|
|
|
4
4
|
|
|
5
5
|
dotenv.config();
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
async function main() {
|
|
9
8
|
await prismaClient.user.upsert({
|
|
10
9
|
where: { email: "tes@gmail.com" },
|
|
11
10
|
update: {},
|
|
12
11
|
create: {
|
|
13
|
-
full_name: "
|
|
12
|
+
full_name: "Akun Test",
|
|
14
13
|
email: "tes@gmail.com",
|
|
15
|
-
password: await argon2.hash("
|
|
14
|
+
password: await argon2.hash("123456"),
|
|
16
15
|
},
|
|
17
16
|
});
|
|
18
17
|
|
|
19
|
-
console.log("Users seeded");
|
|
18
|
+
console.log("Users seeded Successfully");
|
|
20
19
|
}
|
|
21
20
|
main()
|
|
22
21
|
.catch((e) => {
|
package/template/src/main.ts
CHANGED
|
@@ -11,9 +11,11 @@ async function startServer() {
|
|
|
11
11
|
process.exit(0);
|
|
12
12
|
}
|
|
13
13
|
await connectDatabase();
|
|
14
|
-
web.listen(env.PORT, () => {
|
|
15
|
-
logger.info(`🚀 Server is listening on:
|
|
16
|
-
logger.info(
|
|
14
|
+
web.listen(env.PORT, env.HOST, () => {
|
|
15
|
+
logger.info(`🚀 Server is listening on: http://${env.HOST}:${env.PORT}`);
|
|
16
|
+
logger.info(
|
|
17
|
+
`🔗 API Docs available at: http://${env.HOST}:${env.PORT}/api/docs`
|
|
18
|
+
);
|
|
17
19
|
});
|
|
18
20
|
} catch (error) {
|
|
19
21
|
process.exit(0);
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
UserResponse,
|
|
7
7
|
toUserListItemResponse,
|
|
8
8
|
UserListItemResponse,
|
|
9
|
+
toUserDetailResponse,
|
|
10
|
+
UserDetailResponse,
|
|
9
11
|
} from "@dtos/user-dto";
|
|
10
12
|
import { ResponseError } from "@utils/response-error";
|
|
11
13
|
import { UserValidation } from "@validations/user-validation";
|
|
@@ -88,12 +90,12 @@ export class UserService {
|
|
|
88
90
|
};
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
static async detail(id: string): Promise<
|
|
93
|
+
static async detail(id: string): Promise<UserDetailResponse> {
|
|
92
94
|
const data = await UserRepository.findById(id);
|
|
93
95
|
if (!data) {
|
|
94
96
|
throw new ResponseError(404, "Data Tidak Ditemukan");
|
|
95
97
|
}
|
|
96
|
-
return
|
|
98
|
+
return toUserDetailResponse(data);
|
|
97
99
|
}
|
|
98
100
|
static async update(
|
|
99
101
|
id: string,
|
|
@@ -100,8 +100,8 @@ const swaggerOptions: swaggerJSDoc.Options = {
|
|
|
100
100
|
status_code: 400,
|
|
101
101
|
message: "Validation Error",
|
|
102
102
|
errors: {
|
|
103
|
-
field: "
|
|
104
|
-
message: "
|
|
103
|
+
field: "email",
|
|
104
|
+
message: "Format Email Tidak Valid",
|
|
105
105
|
},
|
|
106
106
|
},
|
|
107
107
|
},
|
|
@@ -140,6 +140,9 @@ export function setupSwagger(app: Express) {
|
|
|
140
140
|
swaggerUi.setup(swaggerSpec, {
|
|
141
141
|
customCss: themeCss,
|
|
142
142
|
customSiteTitle: `${formatAppNameForTitle(env.APP_NAME)} Api Documentation`,
|
|
143
|
+
swaggerOptions: {
|
|
144
|
+
deepLinking: true,
|
|
145
|
+
},
|
|
143
146
|
})
|
|
144
147
|
);
|
|
145
148
|
}
|
|
@@ -25,10 +25,9 @@ export class UserValidation {
|
|
|
25
25
|
(v) => (v === null ? undefined : v),
|
|
26
26
|
z
|
|
27
27
|
.string({
|
|
28
|
-
required_error: "
|
|
28
|
+
required_error: "Password Wajib Diisi",
|
|
29
29
|
})
|
|
30
|
-
.min(
|
|
31
|
-
.max(100, { message: "Kata Sandi Maksimal 100 Karakter" })
|
|
30
|
+
.min(6, { message: "Password Minimal 6 Karakter" })
|
|
32
31
|
),
|
|
33
32
|
});
|
|
34
33
|
|
|
@@ -56,10 +55,9 @@ export class UserValidation {
|
|
|
56
55
|
(v) => (v === null ? undefined : v),
|
|
57
56
|
z
|
|
58
57
|
.string({
|
|
59
|
-
required_error: "
|
|
58
|
+
required_error: "Password Wajib Diisi",
|
|
60
59
|
})
|
|
61
|
-
.min(
|
|
62
|
-
.max(100, { message: "Kata Sandi Maksimal 100 Karakter" })
|
|
60
|
+
.min(6, { message: "Password Minimal 6 Karakter" })
|
|
63
61
|
),
|
|
64
62
|
});
|
|
65
63
|
|
|
@@ -77,10 +75,9 @@ export class UserValidation {
|
|
|
77
75
|
(v) => (v === null ? undefined : v),
|
|
78
76
|
z
|
|
79
77
|
.string({
|
|
80
|
-
required_error: "
|
|
78
|
+
required_error: "Password Wajib Diisi",
|
|
81
79
|
})
|
|
82
|
-
.min(
|
|
83
|
-
.max(100, { message: "Kata Sandi Maksimal 100 Karakter" })
|
|
80
|
+
.min(6, { message: "Password Minimal 6 Karakter" })
|
|
84
81
|
),
|
|
85
82
|
});
|
|
86
83
|
|