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.
- package/README.md +137 -0
- package/bin/index.js +158 -0
- package/package.json +24 -0
- package/template/.dockerignore +4 -0
- package/template/Dockerfile +12 -0
- package/template/babel.config.json +3 -0
- package/template/craft/commands/build.js +15 -0
- package/template/craft/commands/db-fresh.js +22 -0
- package/template/craft/commands/db-generate.js +23 -0
- package/template/craft/commands/db-migrate.js +22 -0
- package/template/craft/commands/dev.js +16 -0
- package/template/craft/commands/key-generate.js +41 -0
- package/template/craft/commands/make-apidocs.js +121 -0
- package/template/craft/commands/make-command.js +38 -0
- package/template/craft/commands/make-dto.js +39 -0
- package/template/craft/commands/make-middleware.js +46 -0
- package/template/craft/commands/make-repository.js +36 -0
- package/template/craft/commands/make-route.js +88 -0
- package/template/craft/commands/make-service.js +39 -0
- package/template/craft/commands/make-test.js +48 -0
- package/template/craft/commands/make-utils.js +30 -0
- package/template/craft/commands/make-validation.js +42 -0
- package/template/craft/commands/make-view.js +42 -0
- package/template/craft/commands/start.js +29 -0
- package/template/craft/commands/test.js +20 -0
- package/template/craft.js +256 -0
- package/template/nodemon.json +6 -0
- package/template/package-lock.json +8777 -0
- package/template/package.json +79 -0
- package/template/prisma/migrations/20250518142257_create_table_users/migration.sql +13 -0
- package/template/prisma/migrations/migration_lock.toml +3 -0
- package/template/prisma/schema.prisma +22 -0
- package/template/prisma/seed.ts +29 -0
- package/template/public/assets/images/default-user.png +0 -0
- package/template/src/apidocs/auth-docs.ts +314 -0
- package/template/src/apidocs/users-docs.ts +240 -0
- package/template/src/config/database.ts +90 -0
- package/template/src/config/env.ts +29 -0
- package/template/src/config/logger.ts +116 -0
- package/template/src/config/web.ts +40 -0
- package/template/src/controllers/auth-controller.ts +88 -0
- package/template/src/controllers/user-controller.ts +79 -0
- package/template/src/dtos/list-dto.ts +12 -0
- package/template/src/dtos/user-dto.ts +57 -0
- package/template/src/main.ts +28 -0
- package/template/src/middleware/auth-middleware.ts +44 -0
- package/template/src/middleware/error-middleware.ts +27 -0
- package/template/src/middleware/http-logger-middleware.ts +31 -0
- package/template/src/repositories/user-repository.ts +75 -0
- package/template/src/routes/auth-route.ts +20 -0
- package/template/src/routes/main-route.ts +25 -0
- package/template/src/routes/user-route.ts +35 -0
- package/template/src/services/auth-service.ts +162 -0
- package/template/src/services/user-service.ts +102 -0
- package/template/src/types/type-request.ts +6 -0
- package/template/src/utils/async-handler.ts +9 -0
- package/template/src/utils/response-error.ts +10 -0
- package/template/src/utils/response.ts +60 -0
- package/template/src/utils/swagger.ts +135 -0
- package/template/src/utils/validation.ts +7 -0
- package/template/src/validations/user-validation.ts +127 -0
- package/template/src/views/index.ejs +6 -0
- package/template/src/views/layouts/main.ejs +14 -0
- package/template/src/views/partials/header.ejs +3 -0
- package/template/test/user.test.ts +16 -0
- package/template/tsconfig.json +13 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { prismaClient } from "../config/database";
|
|
2
|
+
|
|
3
|
+
export class UserRepository {
|
|
4
|
+
static async countByEmail(email: string): Promise<number> {
|
|
5
|
+
return prismaClient.user.count({ where: { email } });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static async create(data: any) {
|
|
9
|
+
return prismaClient.user.create({ data });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static async findMany(filters: any, skip: number, take: number) {
|
|
13
|
+
return prismaClient.user.findMany({
|
|
14
|
+
where: {
|
|
15
|
+
AND: filters,
|
|
16
|
+
deleted_at: null,
|
|
17
|
+
},
|
|
18
|
+
skip,
|
|
19
|
+
take,
|
|
20
|
+
orderBy: { updated_at: "desc" },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static async count(filters: any) {
|
|
25
|
+
return prismaClient.user.count({
|
|
26
|
+
where: {
|
|
27
|
+
AND: filters,
|
|
28
|
+
deleted_at: null,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static async findById(id: string) {
|
|
34
|
+
return prismaClient.user.findUnique({
|
|
35
|
+
where: { id },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static async update(id: string, data: any) {
|
|
40
|
+
return prismaClient.user.update({
|
|
41
|
+
where: { id },
|
|
42
|
+
data,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static async delete(id: string) {
|
|
47
|
+
return prismaClient.user.delete({ where: { id } });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static async findUserByEmail(login: string) {
|
|
51
|
+
return prismaClient.user.findFirst({
|
|
52
|
+
where: {
|
|
53
|
+
email: login,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static async updateUser(data: any, id: string) {
|
|
59
|
+
return prismaClient.user.update({
|
|
60
|
+
where: { id },
|
|
61
|
+
data,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static async findemailExistsNotUserLoggedIn(email: string, idUser: string) {
|
|
66
|
+
return prismaClient.user.count({
|
|
67
|
+
where: {
|
|
68
|
+
email: email,
|
|
69
|
+
NOT: {
|
|
70
|
+
id: idUser,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { asyncHandler } from "../utils/async-handler";
|
|
3
|
+
import { authMiddleware } from "../middleware/auth-middleware";
|
|
4
|
+
|
|
5
|
+
import { AuthController } from "../controllers/auth-controller";
|
|
6
|
+
export const authRouter = express.Router();
|
|
7
|
+
authRouter.post("/api/auth/register", AuthController.register);
|
|
8
|
+
authRouter.post("/api/auth/login", AuthController.login);
|
|
9
|
+
authRouter.get("/api/auth/me", asyncHandler(authMiddleware), AuthController.me);
|
|
10
|
+
authRouter.post(
|
|
11
|
+
"/api/auth/logout",
|
|
12
|
+
asyncHandler(authMiddleware),
|
|
13
|
+
AuthController.logout
|
|
14
|
+
);
|
|
15
|
+
authRouter.get("/api/auth/refresh-token", AuthController.refreshToken);
|
|
16
|
+
authRouter.put(
|
|
17
|
+
"/api/auth/update-profile",
|
|
18
|
+
asyncHandler(authMiddleware),
|
|
19
|
+
AuthController.updateProfile
|
|
20
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { successResponse } from "../utils/response";
|
|
3
|
+
import { authRouter } from "./auth-route";
|
|
4
|
+
import { userRouter } from "./user-route";
|
|
5
|
+
|
|
6
|
+
export const mainRouter = express.Router();
|
|
7
|
+
|
|
8
|
+
// mainRouter.get("/", (req, res) => {
|
|
9
|
+
// res.render("index", { title: "Home Page" });
|
|
10
|
+
// });
|
|
11
|
+
|
|
12
|
+
mainRouter.get("/", (req, res) => {
|
|
13
|
+
res
|
|
14
|
+
.status(200)
|
|
15
|
+
.json(successResponse(`${process.env.APP_NAME} is running`, 200))
|
|
16
|
+
.end();
|
|
17
|
+
});
|
|
18
|
+
mainRouter.get("/api", (req, res) => {
|
|
19
|
+
res
|
|
20
|
+
.status(200)
|
|
21
|
+
.json(successResponse(`${process.env.APP_NAME} api is running`, 200))
|
|
22
|
+
.end();
|
|
23
|
+
});
|
|
24
|
+
mainRouter.use(authRouter);
|
|
25
|
+
mainRouter.use(userRouter);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { UserController } from "../controllers/user-controller";
|
|
3
|
+
import { asyncHandler } from "../utils/async-handler";
|
|
4
|
+
import { authMiddleware } from "../middleware/auth-middleware";
|
|
5
|
+
|
|
6
|
+
export const userRouter = express.Router();
|
|
7
|
+
|
|
8
|
+
userRouter.post(
|
|
9
|
+
"/api/users",
|
|
10
|
+
asyncHandler(authMiddleware),
|
|
11
|
+
UserController.create
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
userRouter.get(
|
|
15
|
+
"/api/users",
|
|
16
|
+
asyncHandler(authMiddleware),
|
|
17
|
+
UserController.getAll
|
|
18
|
+
);
|
|
19
|
+
userRouter.get(
|
|
20
|
+
"/api/users/:id",
|
|
21
|
+
asyncHandler(authMiddleware),
|
|
22
|
+
UserController.getOne
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
userRouter.put(
|
|
26
|
+
"/api/users/:id",
|
|
27
|
+
asyncHandler(authMiddleware),
|
|
28
|
+
UserController.update
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
userRouter.delete(
|
|
32
|
+
"/api/users/:id",
|
|
33
|
+
asyncHandler(authMiddleware),
|
|
34
|
+
UserController.delete
|
|
35
|
+
);
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toUserDetailResponse,
|
|
3
|
+
toUserResponse,
|
|
4
|
+
UserDetailResponse,
|
|
5
|
+
UserResponse,
|
|
6
|
+
loginRequest,
|
|
7
|
+
CreateUserRequest,
|
|
8
|
+
UpdateUserRequest,
|
|
9
|
+
} from "../dtos/user-dto";
|
|
10
|
+
import { ResponseError } from "../utils/response-error";
|
|
11
|
+
import { UserValidation } from "../validations/user-validation";
|
|
12
|
+
import { Validation } from "../utils/validation";
|
|
13
|
+
import * as argon2 from "argon2";
|
|
14
|
+
import { User } from "@prisma/client";
|
|
15
|
+
import jwt from "jsonwebtoken";
|
|
16
|
+
import { Request } from "express";
|
|
17
|
+
import { UserRepository } from "../repositories/user-repository";
|
|
18
|
+
import { UserRequest } from "../types/type-request";
|
|
19
|
+
import { prismaClient } from "../config/database";
|
|
20
|
+
import { env } from "../config/env";
|
|
21
|
+
|
|
22
|
+
export class AuthService {
|
|
23
|
+
static async register(request: CreateUserRequest): Promise<UserResponse> {
|
|
24
|
+
const data = Validation.validate(UserValidation.REGISTER, request);
|
|
25
|
+
const emailExits = await UserRepository.countByEmail(data.email);
|
|
26
|
+
|
|
27
|
+
if (emailExits != 0) {
|
|
28
|
+
throw new ResponseError(409, "Akun Sudah Terdaftar!");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
data.password = await argon2.hash(data.password);
|
|
32
|
+
|
|
33
|
+
const response = await UserRepository.create({
|
|
34
|
+
fullName: data.fullName,
|
|
35
|
+
email: data.email,
|
|
36
|
+
password: data.password,
|
|
37
|
+
});
|
|
38
|
+
return toUserResponse(response);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static async login(request: loginRequest) {
|
|
42
|
+
const data = Validation.validate(UserValidation.LOGIN, request);
|
|
43
|
+
const userExits = await UserRepository.findUserByEmail(data.email);
|
|
44
|
+
|
|
45
|
+
if (!userExits) {
|
|
46
|
+
throw new ResponseError(401, "Gagal Login! Detail login salah");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const isPasswordValid = await argon2.verify(
|
|
50
|
+
userExits.password,
|
|
51
|
+
data.password
|
|
52
|
+
);
|
|
53
|
+
if (!isPasswordValid) {
|
|
54
|
+
throw new ResponseError(401, "Gagal Login! Detail login salah");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const refreshToken = jwt.sign(
|
|
58
|
+
{
|
|
59
|
+
user_id: userExits.id,
|
|
60
|
+
user_fullName: userExits.fullName,
|
|
61
|
+
user_email: userExits.email,
|
|
62
|
+
},
|
|
63
|
+
env.JWT_SECRET as string,
|
|
64
|
+
{
|
|
65
|
+
expiresIn: "1d",
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const accessToken = jwt.sign(
|
|
70
|
+
{
|
|
71
|
+
user_id: userExits.id,
|
|
72
|
+
user_fullName: userExits.fullName,
|
|
73
|
+
user_email: userExits.email,
|
|
74
|
+
},
|
|
75
|
+
env.JWT_SECRET as string,
|
|
76
|
+
{
|
|
77
|
+
expiresIn: "5m",
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const user = toUserResponse(userExits);
|
|
82
|
+
return { user, refreshToken, accessToken };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static async me(user: User): Promise<UserDetailResponse> {
|
|
86
|
+
return toUserDetailResponse(user);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static async updateProfile(
|
|
90
|
+
user: User,
|
|
91
|
+
request: UpdateUserRequest
|
|
92
|
+
): Promise<UserResponse> {
|
|
93
|
+
const data = Validation.validate(UserValidation.UPDATE, request);
|
|
94
|
+
if (data.fullName) {
|
|
95
|
+
user.fullName = data.fullName;
|
|
96
|
+
}
|
|
97
|
+
if (data.password) {
|
|
98
|
+
user.password = await argon2.hash(data.password);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (data.email && data.email !== user.email) {
|
|
102
|
+
const emailExists = await UserRepository.findemailExistsNotUserLoggedIn(
|
|
103
|
+
data.email,
|
|
104
|
+
user.id
|
|
105
|
+
);
|
|
106
|
+
if (emailExists != 0) {
|
|
107
|
+
throw new ResponseError(409, "Email Sudah Ada");
|
|
108
|
+
}
|
|
109
|
+
user.email = data.email;
|
|
110
|
+
}
|
|
111
|
+
const result = await UserRepository.updateUser(
|
|
112
|
+
{
|
|
113
|
+
fullName: user.fullName,
|
|
114
|
+
password: user.password,
|
|
115
|
+
email: user.email,
|
|
116
|
+
},
|
|
117
|
+
user.id
|
|
118
|
+
);
|
|
119
|
+
return toUserResponse(result);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static async logout(req: UserRequest) {
|
|
123
|
+
const refreshToken = req.cookies.refresh_token;
|
|
124
|
+
if (!req.user) {
|
|
125
|
+
throw new ResponseError(401, "Unauthorized: Anda Belum Login.");
|
|
126
|
+
}
|
|
127
|
+
if (!refreshToken) {
|
|
128
|
+
throw new ResponseError(401, "Unauthorized: Anda Belum Login.");
|
|
129
|
+
}
|
|
130
|
+
return refreshToken;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static async refreshToken(req: Request) {
|
|
134
|
+
const refreshToken = req.cookies.refresh_token;
|
|
135
|
+
if (!refreshToken) {
|
|
136
|
+
throw new ResponseError(401, "Unauthorized, Anda Belum Login");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const decoded = jwt.verify(refreshToken, env.JWT_SECRET as string) as any;
|
|
141
|
+
|
|
142
|
+
const user = await prismaClient.user.findUnique({
|
|
143
|
+
where: { id: decoded.user_id },
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (!user) {
|
|
147
|
+
throw new ResponseError(401, "Unauthorized, Anda Belum Login.");
|
|
148
|
+
}
|
|
149
|
+
const payload = {
|
|
150
|
+
user_id: user.id,
|
|
151
|
+
user_fullName: user.fullName,
|
|
152
|
+
user_email: user.email,
|
|
153
|
+
};
|
|
154
|
+
const accessToken = jwt.sign(payload, env.JWT_SECRET as string, {
|
|
155
|
+
expiresIn: "6m",
|
|
156
|
+
});
|
|
157
|
+
return { accessToken, user: payload };
|
|
158
|
+
} catch (err) {
|
|
159
|
+
throw new ResponseError(401, "Token Tidak Valid Atau Kadaluarsa");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CreateUserRequest,
|
|
3
|
+
ListUserRequest,
|
|
4
|
+
UpdateUserRequest,
|
|
5
|
+
toUserResponse,
|
|
6
|
+
UserResponse,
|
|
7
|
+
} from "../dtos/user-dto";
|
|
8
|
+
import { ResponseError } from "../utils/response-error";
|
|
9
|
+
import { UserValidation } from "../validations/user-validation";
|
|
10
|
+
import { Validation } from "../utils/validation";
|
|
11
|
+
import * as argon2 from "argon2";
|
|
12
|
+
import { listResponse, tolistResponse } from "../dtos/list-dto";
|
|
13
|
+
import { UserRepository } from "../repositories/user-repository";
|
|
14
|
+
import { env } from "../config/env";
|
|
15
|
+
export class UserService {
|
|
16
|
+
static async create(request: CreateUserRequest): Promise<UserResponse> {
|
|
17
|
+
const data = Validation.validate(UserValidation.CREATE, request);
|
|
18
|
+
const emailExits = await UserRepository.countByEmail(data.email);
|
|
19
|
+
if (emailExits != 0) {
|
|
20
|
+
throw new ResponseError(409, "Email Sudah Terdaftar");
|
|
21
|
+
}
|
|
22
|
+
data.password = await argon2.hash(data.password);
|
|
23
|
+
|
|
24
|
+
const response = await UserRepository.create({
|
|
25
|
+
fullName: data.fullName,
|
|
26
|
+
email: data.email,
|
|
27
|
+
password: data.password,
|
|
28
|
+
});
|
|
29
|
+
return toUserResponse(response);
|
|
30
|
+
}
|
|
31
|
+
static async getAll(request: ListUserRequest): Promise<listResponse> {
|
|
32
|
+
const requestList = Validation.validate(UserValidation.LIST, request);
|
|
33
|
+
const filters = [];
|
|
34
|
+
if (requestList.name) {
|
|
35
|
+
filters.push({
|
|
36
|
+
fullName: {
|
|
37
|
+
contains: requestList.name,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const data = await UserRepository.findMany(
|
|
42
|
+
filters,
|
|
43
|
+
requestList.skip,
|
|
44
|
+
requestList.take
|
|
45
|
+
);
|
|
46
|
+
const totalData = await UserRepository.count(filters);
|
|
47
|
+
const result = {
|
|
48
|
+
data,
|
|
49
|
+
total_data: totalData,
|
|
50
|
+
paging: {
|
|
51
|
+
current_page: requestList.page,
|
|
52
|
+
total_page: Math.ceil(totalData / requestList.take),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
return tolistResponse(result);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static async getOne(id: string): Promise<UserResponse> {
|
|
59
|
+
const data = await UserRepository.findById(id);
|
|
60
|
+
if (!data) {
|
|
61
|
+
throw new ResponseError(404, "Data Tidak Ditemukan");
|
|
62
|
+
}
|
|
63
|
+
return toUserResponse(data);
|
|
64
|
+
}
|
|
65
|
+
static async update(
|
|
66
|
+
id: string,
|
|
67
|
+
request: UpdateUserRequest
|
|
68
|
+
): Promise<UserResponse> {
|
|
69
|
+
const data = Validation.validate(UserValidation.UPDATE, request);
|
|
70
|
+
const user = await UserRepository.findById(id);
|
|
71
|
+
if (!user) {
|
|
72
|
+
throw new ResponseError(404, "Data Tidak Ditemukan");
|
|
73
|
+
}
|
|
74
|
+
if (data.fullName) {
|
|
75
|
+
user.fullName = data.fullName;
|
|
76
|
+
}
|
|
77
|
+
if (data.password) {
|
|
78
|
+
user.password = await argon2.hash(data.password);
|
|
79
|
+
}
|
|
80
|
+
if (data.email && data.email !== user.email) {
|
|
81
|
+
const emailExists = await UserRepository.countByEmail(data.email);
|
|
82
|
+
if (emailExists != 0) {
|
|
83
|
+
throw new ResponseError(409, "Email Sudah Ada");
|
|
84
|
+
}
|
|
85
|
+
user.email = data.email;
|
|
86
|
+
}
|
|
87
|
+
const result = await UserRepository.update(id, {
|
|
88
|
+
fullName: user.fullName,
|
|
89
|
+
password: user.password,
|
|
90
|
+
email: user.email,
|
|
91
|
+
});
|
|
92
|
+
return toUserResponse(result);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static async delete(id: string) {
|
|
96
|
+
const data = await UserRepository.findById(id);
|
|
97
|
+
if (!data) {
|
|
98
|
+
throw new ResponseError(404, "Data Tidak Ditemukan");
|
|
99
|
+
}
|
|
100
|
+
await UserRepository.delete(id);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
|
|
3
|
+
export const asyncHandler = (
|
|
4
|
+
fn: (req: Request, res: Response, next: NextFunction) => Promise<any>
|
|
5
|
+
) => {
|
|
6
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
7
|
+
fn(req, res, next).catch(next);
|
|
8
|
+
};
|
|
9
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export function successResponse(
|
|
2
|
+
message: string,
|
|
3
|
+
statusCode: number = 200,
|
|
4
|
+
data?: any
|
|
5
|
+
) {
|
|
6
|
+
return {
|
|
7
|
+
status: true,
|
|
8
|
+
status_code: statusCode,
|
|
9
|
+
message,
|
|
10
|
+
...(data !== undefined && { data }),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function successCreateResponse(data?: any) {
|
|
15
|
+
return {
|
|
16
|
+
status: true,
|
|
17
|
+
status_code: 201,
|
|
18
|
+
message: "Data Berhasil Ditambahkan",
|
|
19
|
+
...(data !== undefined && { data }),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function successUpdateResponse(data?: any) {
|
|
24
|
+
return {
|
|
25
|
+
status: true,
|
|
26
|
+
status_code: 200,
|
|
27
|
+
message: "Data Berhasil Diupdate",
|
|
28
|
+
...(data !== undefined && { data }),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function successDeleteResponse() {
|
|
33
|
+
return {
|
|
34
|
+
status: true,
|
|
35
|
+
status_code: 200,
|
|
36
|
+
message: "Data Berhasil Dihapus",
|
|
37
|
+
data: null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function successRestoreResponse() {
|
|
41
|
+
return {
|
|
42
|
+
status: true,
|
|
43
|
+
status_code: 200,
|
|
44
|
+
message: "Data Berhasil Direstore",
|
|
45
|
+
data: null,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function errorResponse(
|
|
50
|
+
message: string,
|
|
51
|
+
statusCode: number = 400,
|
|
52
|
+
errors?: any
|
|
53
|
+
) {
|
|
54
|
+
return {
|
|
55
|
+
status: false,
|
|
56
|
+
status_code: statusCode,
|
|
57
|
+
message,
|
|
58
|
+
...(errors !== undefined && { errors }),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import swaggerJSDoc from "swagger-jsdoc";
|
|
2
|
+
import swaggerUi from "swagger-ui-express";
|
|
3
|
+
import { Express } from "express";
|
|
4
|
+
import { SwaggerTheme } from "swagger-themes";
|
|
5
|
+
import { env } from "../config/env";
|
|
6
|
+
|
|
7
|
+
const swaggerOptions: swaggerJSDoc.Options = {
|
|
8
|
+
definition: {
|
|
9
|
+
openapi: "3.0.0",
|
|
10
|
+
info: {
|
|
11
|
+
title: `${env.APP_NAME} Api Documentation`,
|
|
12
|
+
version: "1.0.0",
|
|
13
|
+
},
|
|
14
|
+
servers: [
|
|
15
|
+
{
|
|
16
|
+
url: `${env.BASE_URL}`,
|
|
17
|
+
description: "Local server",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
components: {
|
|
21
|
+
securitySchemes: {
|
|
22
|
+
bearerAuth: {
|
|
23
|
+
type: "http",
|
|
24
|
+
scheme: "bearer",
|
|
25
|
+
bearerFormat: "JWT",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
schemas: {
|
|
29
|
+
User: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
id: {
|
|
33
|
+
type: "string",
|
|
34
|
+
format: "uuid",
|
|
35
|
+
},
|
|
36
|
+
fullName: {
|
|
37
|
+
type: "string",
|
|
38
|
+
},
|
|
39
|
+
email: {
|
|
40
|
+
type: "string",
|
|
41
|
+
format: "email",
|
|
42
|
+
},
|
|
43
|
+
password: {
|
|
44
|
+
type: "string",
|
|
45
|
+
},
|
|
46
|
+
created_at: {
|
|
47
|
+
type: "string",
|
|
48
|
+
format: "date-time",
|
|
49
|
+
},
|
|
50
|
+
updated_at: {
|
|
51
|
+
type: "string",
|
|
52
|
+
format: "date-time",
|
|
53
|
+
},
|
|
54
|
+
deleted_at: {
|
|
55
|
+
type: "string",
|
|
56
|
+
format: "date-time",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
required: ["fullName", "email", "password"],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
responses: {
|
|
63
|
+
UnauthorizedATError: {
|
|
64
|
+
description: "Unauthorized – token tidak valid atau tidak ada",
|
|
65
|
+
content: {
|
|
66
|
+
"application/json": {
|
|
67
|
+
example: {
|
|
68
|
+
status: false,
|
|
69
|
+
status_code: 401,
|
|
70
|
+
message: "Unauthorized: Access Token Tidak Valid.",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
UnauthorizedNotLoginError: {
|
|
76
|
+
description: "Unauthorized - Belum Login.",
|
|
77
|
+
content: {
|
|
78
|
+
"application/json": {
|
|
79
|
+
example: {
|
|
80
|
+
status: false,
|
|
81
|
+
status_code: 401,
|
|
82
|
+
message: "Unauthorized: Anda Belum Login.",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
ValidationError: {
|
|
88
|
+
description: "Validation Error",
|
|
89
|
+
content: {
|
|
90
|
+
"application/json": {
|
|
91
|
+
example: {
|
|
92
|
+
status: false,
|
|
93
|
+
status_code: 400,
|
|
94
|
+
message: "Validation Error",
|
|
95
|
+
errors: {
|
|
96
|
+
field: "string",
|
|
97
|
+
message: "string",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
NotFoundError: {
|
|
104
|
+
description: "Data tidak ditemukan",
|
|
105
|
+
content: {
|
|
106
|
+
"application/json": {
|
|
107
|
+
example: {
|
|
108
|
+
status: false,
|
|
109
|
+
status_code: 404,
|
|
110
|
+
message: "Data Tidak Ditemukan",
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
security: [{ bearerAuth: [] }],
|
|
118
|
+
},
|
|
119
|
+
// apis: ["./src/route/**/*.ts"],
|
|
120
|
+
apis: ["./src/apidocs/**/*.ts"],
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const swaggerSpec = swaggerJSDoc(swaggerOptions);
|
|
124
|
+
const theme = new SwaggerTheme();
|
|
125
|
+
const themeCss = theme.getBuffer("dark-monokai" as any);
|
|
126
|
+
export function setupSwagger(app: Express) {
|
|
127
|
+
app.use(
|
|
128
|
+
"/api/docs",
|
|
129
|
+
swaggerUi.serve,
|
|
130
|
+
swaggerUi.setup(swaggerSpec, {
|
|
131
|
+
customCss: themeCss,
|
|
132
|
+
customSiteTitle: `${env.APP_NAME} Api Documentation`,
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
}
|