Compare commits
127 Commits
251f7148b6
...
00239db472
| Author | SHA1 | Date | |
|---|---|---|---|
| 00239db472 | |||
| 8761f1e07e | |||
|
|
c772b7a9df | ||
|
|
d0394f27eb | ||
| c71da2d3bb | |||
| 4083e8544e | |||
| 90f529bfde | |||
| f2109e5fdf | |||
| 7e769a1fac | |||
| 1aec3825e7 | |||
| 2bb8712430 | |||
| b146fe3ffa | |||
| 4d080946a6 | |||
| 5c44b8c3f0 | |||
| c4be580448 | |||
|
|
ba8c7a5346 | ||
|
|
b2399d486e | ||
| a036326792 | |||
| bf9cdf5eeb | |||
| d56d23cd71 | |||
| b1feffe39e | |||
| 7364c3b9c1 | |||
| 2907cb6347 | |||
| 1be51f634b | |||
| c76953bf89 | |||
| 7ad8c6b3fe | |||
| 7e05fc589b | |||
|
|
32d25cef6a | ||
| 7aaf35f7e9 | |||
|
|
243416fbd6 | ||
|
|
7f06416abf | ||
|
|
3896f4103d | ||
| 2c295ffd36 | |||
| 110c73ae9d | |||
|
|
9b8cb9d752 | ||
|
|
8de83c2716 | ||
|
|
662038d953 | ||
| 95189e2014 | |||
| d5c53b2953 | |||
| ace419fa3e | |||
| 7c55e786f3 | |||
|
|
47051766e3 | ||
|
|
988bcaf301 | ||
| e581f5b5bb | |||
| 751cd0911e | |||
| cc4d135a53 | |||
| 62823524bc | |||
|
|
36a2609512 | ||
| 7302a3320c | |||
|
|
425b1ed554 | ||
| ee30308112 | |||
|
|
c51c686cce | ||
| 6c7d92deae | |||
| 05668b4a39 | |||
| c5b5f2ba10 | |||
| 82563469bd | |||
| e13cb845bc | |||
| bdd8ec02dd | |||
| ab3b38eb49 | |||
|
|
6eed13bc4f | ||
|
|
8add2618ce | ||
|
|
fa91ce124a | ||
|
|
6b419495f7 | ||
| 3fd4a4c1b7 | |||
| ad0f44669b | |||
| 9c23dbe97b | |||
|
|
c81e8ef22d | ||
| 5d27056906 | |||
| 0ae2903035 | |||
| fdeb8eb26d | |||
| 7d7891f6ca | |||
| 1b384a56b5 | |||
| a6c2e7fc7e | |||
| 45968832f0 | |||
| cf37732ebe | |||
| 20b70edaa6 | |||
| b16e65463d | |||
| 58cb0c8425 | |||
| 0ae39aa504 | |||
| 9ad16fcff7 | |||
| 453b5eb5af | |||
| 76c5eef2f7 | |||
| f9d3bd913f | |||
| 4b60f922ee | |||
| a4ef76e74e | |||
| 33e70721d9 | |||
| 8fca2d3cd2 | |||
| a632791a4d | |||
| 2eec70b7e3 | |||
| ba7f746433 | |||
| ddf9784213 | |||
| 48cb3af91d | |||
| 3e6877ee07 | |||
| 857e9ecf63 | |||
| 315537fc6e | |||
| fe5241a1e1 | |||
| 8375c0c2f2 | |||
| 1987508887 | |||
| f4580c42ee | |||
| ec81b4b311 | |||
| 373caf307b | |||
| cdb9a7e0ef | |||
| 8d2a8565ff | |||
| 446e393ee8 | |||
| e577179358 | |||
| a2124ce5ea | |||
| 18cf2dd73e | |||
| c1ff968c19 | |||
| ed92e20033 | |||
| 909c6d5fba | |||
| 1cadf8c69d | |||
| 0590773d64 | |||
| 15851e5853 | |||
| 8e3c2df276 | |||
| 86cd21ca0b | |||
| 27d4541cfc | |||
| 4bd50c7a4c | |||
| c0aca9ea87 | |||
| 7b2509ffa1 | |||
| 050529cf78 | |||
| d41c0421c4 | |||
| 373b707954 | |||
| b022e86e02 | |||
| 6f66f0c2f3 | |||
| a28c3c22d9 | |||
| 8142ceda30 | |||
| ce64671551 |
22
.env.example
22
.env.example
@@ -1,18 +1,18 @@
|
||||
# # SQL DB Connection Colo
|
||||
# SQL_HOST=117.102.231.130
|
||||
# SQL DB Connection Colo
|
||||
SQL_HOST=117.102.231.130
|
||||
SQL_DATABASE=cod_piu
|
||||
SQL_USERNAME=sa
|
||||
SQL_PASSWORD=@R3M4niA.
|
||||
SQL_PORT=1433
|
||||
|
||||
# SQL_HOST=203.153.114.226
|
||||
# SQL_PORT=1112
|
||||
# SQL_DATABASE=piu
|
||||
# SQL_USERNAME=sa
|
||||
# SQL_PASSWORD=@R3M4niA.
|
||||
# SQL_PORT=1433
|
||||
|
||||
SQL_HOST=203.153.114.226
|
||||
SQL_PORT=1112
|
||||
SQL_DATABASE=piu
|
||||
SQL_USERNAME=sa
|
||||
SQL_PASSWORD=piu123
|
||||
# SQL_PASSWORD=piu123
|
||||
|
||||
# Application Port - express server listens on this port (default 9000).
|
||||
PORT=9528
|
||||
PORT=9530
|
||||
ENDPOINT_WA=http://203.153.114.226:9529/send
|
||||
# ENDPOINT_WA=http://localhost:9529/send
|
||||
ENDPOINT_FE=http://203.153.114.226:9527
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ node_modules
|
||||
.vscode
|
||||
request.http
|
||||
*.rest
|
||||
package-lock.json
|
||||
20
app.js
20
app.js
@@ -8,6 +8,7 @@ const helmet = require("helmet");
|
||||
const compression = require("compression");
|
||||
const unknownEndpoint = require("./middleware/unKnownEndpoint");
|
||||
const { handleError } = require("./helpers/error");
|
||||
const { checkConnection } = require("./config");
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -24,6 +25,25 @@ app.use("/api", routes);
|
||||
app.get("/", (req, res) =>
|
||||
res.send("<h1 style='text-align: center'>HAHALO</h1>")
|
||||
);
|
||||
|
||||
app.get("/check-db", async (req, res) => {
|
||||
try {
|
||||
const isConnected = await checkConnection();
|
||||
res.json({
|
||||
success: isConnected,
|
||||
message: isConnected
|
||||
? "Koneksi database OK"
|
||||
: "Koneksi database gagal",
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat cek koneksi database",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.use(unknownEndpoint);
|
||||
app.use(handleError);
|
||||
|
||||
|
||||
@@ -28,6 +28,18 @@ const poolPromise = new sql.ConnectionPool(config)
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
async function checkConnection() {
|
||||
try {
|
||||
const pool = await poolPromise;
|
||||
await pool.request().query('SELECT 1 AS isConnected');
|
||||
console.log('🔍 SQL Server terkoneksi dengan baik');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('⚠️ Gagal cek koneksi SQL Server:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper query (auto konversi $1 → @p1)
|
||||
*/
|
||||
@@ -46,6 +58,11 @@ async function query(text, params = []) {
|
||||
return request.query(sqlText);
|
||||
}
|
||||
|
||||
function isValidDate(dateStr) {
|
||||
const d = new Date(dateStr);
|
||||
return !isNaN(d.getTime()); // true kalau valid
|
||||
}
|
||||
|
||||
/**
|
||||
* Build filter query
|
||||
*/
|
||||
@@ -71,10 +88,24 @@ function buildFilterQuery(filterQuery = [], fixedParams = []) {
|
||||
queryParams.push(f.param ? 1 : 0);
|
||||
whereConditions.push(`${f.column} = $${queryParams.length}`);
|
||||
break;
|
||||
|
||||
case 'between':
|
||||
if (Array.isArray(f.param) && f.param.length === 2) {
|
||||
const from = f.param[0];
|
||||
const to = f.param[1];
|
||||
if (isValidDate(from) && isValidDate(to)) {
|
||||
queryParams.push(from);
|
||||
queryParams.push(to);
|
||||
whereConditions.push(
|
||||
`${f.column} BETWEEN $${queryParams.length - 1} AND $${queryParams.length}`
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return { whereConditions, queryParams };
|
||||
return { whereConditions, whereParamAnd: queryParams };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,13 +127,17 @@ function buildStringOrIlike(columnParam, criteria, fixedParams = []) {
|
||||
? `AND (${orStringConditions.join(" OR ")})`
|
||||
: "";
|
||||
|
||||
return { whereOrConditions: whereClause, whereParam: queryParams };
|
||||
return { whereOrConditions: whereClause, whereParamOr: queryParams };
|
||||
}
|
||||
|
||||
/**
|
||||
* Build dynamic UPDATE
|
||||
*/
|
||||
function buildDynamicUpdate(table, data, where) {
|
||||
|
||||
data.updated_by = data.userId
|
||||
delete data.userId;
|
||||
|
||||
const setParts = [];
|
||||
const values = [];
|
||||
let index = 1;
|
||||
@@ -118,8 +153,8 @@ function buildDynamicUpdate(table, data, where) {
|
||||
throw new Error("Tidak ada kolom untuk diupdate");
|
||||
}
|
||||
|
||||
// updated_at otomatis pakai GETDATE()
|
||||
setParts.push(`updated_at = GETDATE()`);
|
||||
// updated_at otomatis pakai CURRENT_TIMESTAMP
|
||||
setParts.push(`updated_at = CURRENT_TIMESTAMP`);
|
||||
|
||||
const whereParts = [];
|
||||
for (const [key, value] of Object.entries(where)) {
|
||||
@@ -140,6 +175,11 @@ function buildDynamicUpdate(table, data, where) {
|
||||
* Build dynamic INSERT
|
||||
*/
|
||||
function buildDynamicInsert(table, data) {
|
||||
|
||||
data.created_by = data.userId
|
||||
data.updated_by = data.userId
|
||||
delete data.userId;
|
||||
|
||||
const columns = [];
|
||||
const placeholders = [];
|
||||
const values = [];
|
||||
@@ -159,7 +199,7 @@ function buildDynamicInsert(table, data) {
|
||||
|
||||
// created_at & updated_at otomatis
|
||||
columns.push("created_at", "updated_at");
|
||||
placeholders.push("GETDATE()", "GETDATE()");
|
||||
placeholders.push("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP");
|
||||
|
||||
const query = `
|
||||
INSERT INTO ${table} (${columns.join(", ")})
|
||||
@@ -195,6 +235,7 @@ async function generateKode(prefix, tableName, columnName) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkConnection,
|
||||
query,
|
||||
buildFilterQuery,
|
||||
buildStringOrIlike,
|
||||
|
||||
@@ -1,26 +1,99 @@
|
||||
const authService = require("../services/auth.service");
|
||||
const AuthService = require('../services/auth.service');
|
||||
const { setResponse, checkValidate } = require('../helpers/utils');
|
||||
const { registerSchema, loginSchema } = require('../validate/auth.schema');
|
||||
const { createCaptcha } = require('../utils/captcha');
|
||||
|
||||
const loginUser = async (req, res) => {
|
||||
const { username, password, role, tenant } = req.body;
|
||||
const { token, refreshToken, user } = await authService.login(
|
||||
username,
|
||||
password,
|
||||
tenant
|
||||
);
|
||||
class AuthController {
|
||||
// Register
|
||||
static async register(req, res) {
|
||||
const { error, value } = await checkValidate(registerSchema, req);
|
||||
|
||||
res.header("auth-token", token);
|
||||
res.cookie("refreshToken", refreshToken, {
|
||||
httpOnly: true,
|
||||
sameSite: process.env.NODE_ENV === "development" ? true : "none",
|
||||
secure: process.env.NODE_ENV === "development" ? false : true,
|
||||
});
|
||||
res.status(200).json({
|
||||
token,
|
||||
refreshToken,
|
||||
user,
|
||||
});
|
||||
};
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loginUser,
|
||||
};
|
||||
// Format nomor HP Indonesia
|
||||
if (value.user_phone && value.user_phone.startsWith('0')) {
|
||||
value.user_phone = '+62' + value.user_phone.slice(1);
|
||||
}
|
||||
|
||||
const results = await AuthService.register(value);
|
||||
|
||||
const response = await setResponse(
|
||||
{
|
||||
user: { ...results.user, approved: false },
|
||||
},
|
||||
'User registered successfully. Waiting for admin approval.'
|
||||
);
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Login
|
||||
static async login(req, res) {
|
||||
const { error, value } = await checkValidate(loginSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
const results = await AuthService.login(value);
|
||||
|
||||
// Simpan refresh token di cookie
|
||||
res.cookie('refreshToken', results.tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'lax',
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000
|
||||
});
|
||||
|
||||
const response = await setResponse(
|
||||
{
|
||||
user: { ...results.user, approved: true },
|
||||
accessToken: results.tokens.accessToken
|
||||
},
|
||||
'Login successful'
|
||||
);
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Refresh Token
|
||||
static async refreshToken(req, res) {
|
||||
const refreshToken = req.cookies?.refreshToken;
|
||||
|
||||
if (!refreshToken) {
|
||||
return res.status(401).json(setResponse(null, 'Refresh token is required', 401));
|
||||
}
|
||||
|
||||
const results = await AuthService.refreshToken(refreshToken);
|
||||
const response = await setResponse(results, 'Token refreshed successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Logout
|
||||
static async logout(req, res) {
|
||||
res.clearCookie('refreshToken', {
|
||||
httpOnly: true,
|
||||
sameSite: 'none',
|
||||
secure: true
|
||||
});
|
||||
|
||||
const response = await setResponse(null, 'Logged out successfully');
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Captcha
|
||||
static async generateCaptcha(req, res) {
|
||||
const { svg, text } = createCaptcha();
|
||||
|
||||
// Tampilkan captcha di header untuk dev
|
||||
res.setHeader('X-Captcha-Text', text);
|
||||
|
||||
const response = await setResponse({ svg, text }, 'Captcha generated');
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthController;
|
||||
|
||||
71
controllers/device.controller.js
Normal file
71
controllers/device.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const DeviceService = require('../services/device.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { insertDeviceSchema, updateDeviceSchema } = require('../validate/device.schema');
|
||||
|
||||
class DeviceController {
|
||||
// Get all devices
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await DeviceService.getAllDevices(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Device found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get device by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await DeviceService.getDeviceById(id);
|
||||
const response = await setResponse(results, 'Device found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create device
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertDeviceSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await DeviceService.createDevice(value);
|
||||
const response = await setResponse(results, 'Device created successfully')
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update device
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateDeviceSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await DeviceService.updateDevice(id, value);
|
||||
const response = await setResponse(results, 'Device updated successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete device
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await DeviceService.deleteDevice(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Device deleted successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeviceController;
|
||||
71
controllers/roles.controller.js
Normal file
71
controllers/roles.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const RolesService = require('../services/roles.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { updateRolesSchema, insertRolesSchema } = require('../validate/roles.schema');
|
||||
|
||||
class RolesController {
|
||||
// Get all Roles
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await RolesService.getAllRoles(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Roles found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get Roles by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await RolesService.getRolesById(id);
|
||||
const response = await setResponse(results, 'Roles found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create Roles
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertRolesSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await RolesService.createRoles(value);
|
||||
const response = await setResponse(results, 'Roles created successfully')
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update Roles
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateRolesSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await RolesService.updateRoles(id, value);
|
||||
const response = await setResponse(results, 'Roles updated successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete Roles
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await RolesService.deleteRoles(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Roles deleted successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RolesController;
|
||||
71
controllers/schedule.controller.js
Normal file
71
controllers/schedule.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const ScheduleService = require('../services/schedule.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { updateScheduleSchema, insertScheduleSchema } = require('../validate/schedule.schema');
|
||||
|
||||
class ScheduleController {
|
||||
// Get all Schedule
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await ScheduleService.getAllSchedule(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Schedule found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get Schedule by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await ScheduleService.getScheduleById(id);
|
||||
const response = await setResponse(results, 'Schedule found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create Schedule
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertScheduleSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await ScheduleService.insertScheduleDb(value);
|
||||
const response = await setResponse(results, 'Schedule created successfully')
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update Schedule
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateScheduleSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await ScheduleService.updateSchedule(id, value);
|
||||
const response = await setResponse(results, 'Schedule updated successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete Schedule
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await ScheduleService.deleteSchedule(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Schedule deleted successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ScheduleController;
|
||||
71
controllers/shift.controller.js
Normal file
71
controllers/shift.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const ShiftService = require('../services/shift.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { updateShiftSchema, insertShiftSchema } = require('../validate/shift.schema');
|
||||
|
||||
class ShiftController {
|
||||
// Get all Shift
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await ShiftService.getAllShift(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Shift found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get Shift by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await ShiftService.getShiftById(id);
|
||||
const response = await setResponse(results, 'Shift found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create Shift
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertShiftSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await ShiftService.createShift(value);
|
||||
const response = await setResponse(results, 'Shift created successfully')
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update Shift
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateShiftSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await ShiftService.updateShift(id, value);
|
||||
const response = await setResponse(results, 'Shift updated successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete Shift
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await ShiftService.deleteShift(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Shift deleted successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ShiftController;
|
||||
73
controllers/status.controller.js
Normal file
73
controllers/status.controller.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const StatusService = require('../services/status.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { insertStatusSchema, updateStatusSchema } = require('../validate/status.schema');
|
||||
|
||||
class StatusController {
|
||||
// Get all status
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await StatusService.getAllStatus(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Status found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get status by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await StatusService.getStatusById(id);
|
||||
const response = await setResponse(results, 'Status found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create status
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertStatusSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id;
|
||||
|
||||
const results = await StatusService.createStatus(value);
|
||||
const response = await setResponse(results, 'Status created successfully');
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update status
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
console.log("REQ BODY:", req.body);
|
||||
|
||||
const { error, value } = await checkValidate(updateStatusSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id;
|
||||
|
||||
const results = await StatusService.updateStatus(id, value);
|
||||
const response = await setResponse(results, 'Status updated successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete status
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await StatusService.deleteStatus(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Status deleted successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StatusController;
|
||||
71
controllers/sub_section.controller.js
Normal file
71
controllers/sub_section.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const SubSectionService = require('../services/sub_section.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { insertSubSectionSchema, updateSubSectionSchema } = require('../validate/sub_section.schema');
|
||||
|
||||
class SubSectionController {
|
||||
// Get all sub sections
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await SubSectionService.getAll(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Sub section found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get sub section by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await SubSectionService.getById(id);
|
||||
const response = await setResponse(results, 'Sub section found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create sub section
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertSubSectionSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id;
|
||||
|
||||
const results = await SubSectionService.create(value);
|
||||
const response = await setResponse(results, 'Sub section created successfully');
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update sub section
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateSubSectionSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id;
|
||||
|
||||
const results = await SubSectionService.update(id, value);
|
||||
const response = await setResponse(results, 'Sub section updated successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete sub section
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await SubSectionService.delete(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Sub section deleted successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SubSectionController;
|
||||
71
controllers/tags.controller.js
Normal file
71
controllers/tags.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const TagsService = require('../services/tags.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { insertTagsSchema, updateTagsSchema } = require('../validate/tags.schema');
|
||||
|
||||
class TagsController {
|
||||
// Get all devices
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await TagsService.getAllTags(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Tags found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get device by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await TagsService.getTagByID(id);
|
||||
const response = await setResponse(results, 'Tags found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create device
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertTagsSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await TagsService.createTags(value);
|
||||
const response = await setResponse(results, 'Tags created successfully')
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update device
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateTagsSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await TagsService.updateTags(id, value);
|
||||
const response = await setResponse(results, 'Tags updated successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete device
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await TagsService.deleteTags(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Tags deleted successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagsController;
|
||||
71
controllers/unit.controller.js
Normal file
71
controllers/unit.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const UnitService = require('../services/unit.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { insertUnitSchema, updateUnitSchema } = require('../validate/unit.schema');
|
||||
|
||||
class UnitController {
|
||||
// Get all units
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await UnitService.getAllUnits(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Unit found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get unit by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await UnitService.getUnitById(id);
|
||||
const response = await setResponse(results, 'Unit found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create unit
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertUnitSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.created_by = req.user.user_id;
|
||||
|
||||
const results = await UnitService.createUnit(value);
|
||||
const response = await setResponse(results, 'Unit created successfully');
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update unit
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateUnitSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.updated_by = req.user.user_id;
|
||||
|
||||
const results = await UnitService.updateUnit(id, value);
|
||||
const response = await setResponse(results, 'Unit updated successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete unit
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await UnitService.deleteUnit(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Unit deleted successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UnitController;
|
||||
@@ -1,172 +1,108 @@
|
||||
const userService = require("../services/user.service");
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
const { hashPassword } = require("../helpers/hashPassword");
|
||||
const { setResponse, setPaging, setResponsePaging } = require("../helpers/utils");
|
||||
const Joi = require("joi");
|
||||
const UserService = require("../services/user.service");
|
||||
const { setResponse, setResponsePaging, checkValidate } = require("../helpers/utils");
|
||||
const { userSchema, updateUserSchema, newPasswordSchema } = require("../validate/user.schema");
|
||||
|
||||
// Definisikan skema validasi
|
||||
const validateTerm = Joi.object({
|
||||
user_fullname: Joi.string().max(255).required(),
|
||||
user_name: Joi.string().max(255).required(),
|
||||
user_email: Joi.string().max(255).email().allow(null),
|
||||
user_password: Joi.string().max(255).required(),
|
||||
role_id: Joi.number().integer().allow(null),
|
||||
is_active: Joi.boolean().required()
|
||||
});
|
||||
class UserController {
|
||||
// Get all users
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const getAllUsers = async (req, res) => {
|
||||
const results = await UserService.getAllUsers(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Users found');
|
||||
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
fullname: userFullname,
|
||||
username: userName,
|
||||
is_active: isActive,
|
||||
criteria,
|
||||
tenantID,
|
||||
} = req.query
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
// Get user by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const filterQuery = {
|
||||
fixed: {
|
||||
limit, offset, tenantID
|
||||
},
|
||||
filterQuery: [
|
||||
{
|
||||
type: 'string',
|
||||
column: 'user_fullname',
|
||||
param: userFullname
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
column: 'user_name',
|
||||
param: userName
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
column: 'is_active',
|
||||
param: isActive
|
||||
}
|
||||
],
|
||||
filterCriteria:
|
||||
{
|
||||
criteria,
|
||||
column: [
|
||||
'user_fullname', 'user_name'
|
||||
]
|
||||
const results = await UserService.getUserById(id);
|
||||
const response = await setResponse(results, 'User found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create user
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(userSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.approved_by = req.user.user_id;
|
||||
|
||||
const results = await UserService.createUser(value);
|
||||
const response = await setResponse(results, 'User created successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
const results = await userService.getAllUsers(filterQuery)
|
||||
const response = await setResponsePaging(results.data, results.total, parseInt(limit), parseInt(page))
|
||||
// Update user
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
res.status(response.statusCode).json(response)
|
||||
};
|
||||
const { error, value } = await checkValidate(updateUserSchema, req);
|
||||
|
||||
const getAllStatusUsers = async (req, res) => {
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
const results = await userService.getAllStatusUsers();
|
||||
const response = await setResponse(results)
|
||||
value.userId = req.user.user_id;
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
};
|
||||
const results = await UserService.updateUser(id, value);
|
||||
const response = await setResponse(results, 'User updated successfully');
|
||||
|
||||
const createUser = async (req, res) => {
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Approve user
|
||||
static async approve(req, res) {
|
||||
const { id } = req.params;
|
||||
const approverId = req.user.user_id;
|
||||
|
||||
const updatedUser = await UserService.approveUser(id, approverId);
|
||||
const response = await setResponse(updatedUser, 'User approved successfully');
|
||||
|
||||
// Lakukan validasi
|
||||
const { error } = validateTerm.validate(req.body, { stripUnknown: true });
|
||||
if (error) {
|
||||
const response = await setResponse([], error.details[0].message, 400)
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
const results = await userService.createUser({
|
||||
userFullname: req.body.user_fullname,
|
||||
userName: req.body.user_name,
|
||||
userEmail: req.body.user_email,
|
||||
userPassword: req.body.user_password,
|
||||
roleId: req.body.role_id,
|
||||
isActive: req.body.is_active, // default 1 jika tidak dikirim
|
||||
userID: req.body.userID,
|
||||
tenantID: req.body.tenantID
|
||||
});
|
||||
// Reject user
|
||||
static async reject(req, res) {
|
||||
const { id } = req.params;
|
||||
const approverId = req.user.user_id;
|
||||
|
||||
const response = await setResponse(results);
|
||||
const updatedUser = await UserService.rejectUser(id, approverId);
|
||||
const response = await setResponse(updatedUser, 'User rejected successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
};
|
||||
|
||||
const getUserById = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await userService.getUserById(id);
|
||||
const response = await setResponse(results)
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
};
|
||||
|
||||
const getUserProfile = async (req, res) => {
|
||||
const { id } = req.user;
|
||||
|
||||
const results = await userService.getUserById(id);
|
||||
const response = await setResponse(results)
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
};
|
||||
|
||||
const updateUser = async (req, res) => {
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
// Lakukan validasi
|
||||
const { error } = validateTerm.validate(req.body, { stripUnknown: true });
|
||||
if (error) {
|
||||
const response = await setResponse([], error.details[0].message, 400)
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
const results = await userService.updateUser({
|
||||
userFullname: req.body.user_fullname,
|
||||
userName: req.body.user_name,
|
||||
userEmail: req.body.user_email,
|
||||
userPassword: req.body.user_password,
|
||||
roleId: req.body.role_id,
|
||||
isActive: req.body.is_active, // default 1 jika tidak dikirim
|
||||
userID: req.body.userID,
|
||||
tenantID: req.body.tenantID,
|
||||
id
|
||||
});
|
||||
// Soft delete user
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const response = await setResponse(results);
|
||||
const results = await UserService.deleteUser(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'User deleted successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
};
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
const deleteUser = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const userID = req.userID
|
||||
// Change password
|
||||
static async changePassword(req, res) {
|
||||
const { id } = req.params;
|
||||
const { error, value } = await checkValidate(newPasswordSchema, req);
|
||||
|
||||
const results = await userService.deleteUser(id, userID);
|
||||
const response = await setResponse(results)
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
};
|
||||
const results = await UserService.changeUserPassword(id, value.new_password);
|
||||
const response = await setResponse(results, 'Password changed successfully');
|
||||
|
||||
const getAllRoles = async (req, res) => {
|
||||
const results = await userService.getAllRoles(req.body.tenantID);
|
||||
const response = await setResponse(results)
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllUsers,
|
||||
createUser,
|
||||
getUserById,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
getUserProfile,
|
||||
getAllRoles,
|
||||
getAllStatusUsers
|
||||
};
|
||||
module.exports = UserController;
|
||||
|
||||
116
db/brand.db.js
Normal file
116
db/brand.db.js
Normal file
@@ -0,0 +1,116 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get all brands
|
||||
const getAllBrandsDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["b.brand_name"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "b.brand_name", param: searchParams.name, type: "string" },
|
||||
{ column: "b.created_by", param: searchParams.created_by, type: "number" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ? whereParamAnd : queryParams;
|
||||
|
||||
const queryText = `
|
||||
SELECT COUNT(*) OVER() AS total_data, b.*
|
||||
FROM m_brands b
|
||||
WHERE b.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? whereOrConditions : ""}
|
||||
ORDER BY b.brand_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""};
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
|
||||
const total = result?.recordset.length > 0 ? parseInt(result.recordset[0].total_data, 10) : 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
// Get brand by ID
|
||||
const getBrandByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT b.*
|
||||
FROM m_brands b
|
||||
WHERE b.brand_id = $1 AND b.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Get brand by name
|
||||
const getBrandByNameDb = async (name) => {
|
||||
const queryText = `
|
||||
SELECT b.*
|
||||
FROM m_brands b
|
||||
WHERE b.brand_name = $1 AND b.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [name]);
|
||||
return result.recordset[0];
|
||||
};
|
||||
|
||||
// Create brand
|
||||
const createBrandDb = async (data) => {
|
||||
const store = {
|
||||
...data,
|
||||
created_at: new Date(),
|
||||
};
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("m_brands", store);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset[0]?.inserted_id;
|
||||
return insertedId ? await getBrandByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
// Update brand
|
||||
const updateBrandDb = async (id, data) => {
|
||||
const store = {
|
||||
...data,
|
||||
updated_at: new Date(),
|
||||
};
|
||||
|
||||
const whereData = {
|
||||
brand_id: id,
|
||||
};
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate("m_brands", store, whereData);
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getBrandByIdDb(id);
|
||||
};
|
||||
|
||||
// Soft delete brand
|
||||
const deleteBrandDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_brands
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE brand_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllBrandsDb,
|
||||
getBrandByIdDb,
|
||||
getBrandByNameDb,
|
||||
createBrandDb,
|
||||
updateBrandDb,
|
||||
deleteBrandDb,
|
||||
};
|
||||
133
db/device.db.js
Normal file
133
db/device.db.js
Normal file
@@ -0,0 +1,133 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get all devices
|
||||
const getAllDevicesDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
// Pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
// Search
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
[
|
||||
"a.device_name",
|
||||
"a.device_code",
|
||||
"a.device_location",
|
||||
"a.ip_address",
|
||||
"b.brand_name",
|
||||
],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||
|
||||
// Filter
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.device_code", param: searchParams.code, type: "string" },
|
||||
{ column: "a.device_location", param: searchParams.location, type: "string" },
|
||||
{ column: "b.brand_name", param: searchParams.brand, type: "string" },
|
||||
{ column: "a.is_active", param: searchParams.status, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ? whereParamAnd : queryParams;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*,
|
||||
b.brand_name,
|
||||
COALESCE(a.device_code, '') + ' - ' + COALESCE(a.device_name, '') AS device_code_name
|
||||
FROM m_device a
|
||||
LEFT JOIN m_brands b ON a.brand_id = b.brand_id
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
||||
${whereOrConditions ? whereOrConditions : ''}
|
||||
ORDER BY a.device_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ''};
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
|
||||
const total =
|
||||
result?.recordset.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
|
||||
const getDeviceByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*,
|
||||
b.brand_name,
|
||||
COALESCE(a.device_code, '') + ' - ' + COALESCE(a.device_name, '') AS device_code_name
|
||||
FROM m_device a
|
||||
LEFT JOIN m_brands b ON a.brand_id = b.brand_id
|
||||
WHERE a.device_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
const createDeviceDb = async (data) => {
|
||||
const newCode = await pool.generateKode("DVC", "m_device", "device_code");
|
||||
|
||||
const store = {
|
||||
...data,
|
||||
device_code: newCode,
|
||||
};
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicInsert(
|
||||
"m_device",
|
||||
store
|
||||
);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset[0]?.inserted_id;
|
||||
return insertedId ? await getDeviceByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const updateDeviceDb = async (id, data) => {
|
||||
const store = {
|
||||
...data,
|
||||
};
|
||||
|
||||
// Kondisi WHERE
|
||||
const whereData = {
|
||||
device_id: id,
|
||||
};
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"m_device",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getDeviceByIdDb(id);
|
||||
};
|
||||
|
||||
const deleteDeviceDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_device
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE device_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllDevicesDb,
|
||||
getDeviceByIdDb,
|
||||
createDeviceDb,
|
||||
updateDeviceDb,
|
||||
deleteDeviceDb,
|
||||
};
|
||||
99
db/roles.db.js
Normal file
99
db/roles.db.js
Normal file
@@ -0,0 +1,99 @@
|
||||
const pool = require("../config");
|
||||
|
||||
const getAllRolesDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
// Handle pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.role_name", "a.role_level", "a.role_description"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
if (whereParamOr) queryParams = whereParamOr;
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.role_name", param: searchParams.role_name, type: "string" },
|
||||
{ column: "a.role_level", param: searchParams.start_time, type: "string" },
|
||||
{ column: "a.role_description", param: searchParams.role_description, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
if (whereParamAnd) queryParams = whereParamAnd;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*
|
||||
FROM m_roles a
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||
ORDER BY a.role_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
const total =
|
||||
result?.recordset?.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
const getRolesByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*
|
||||
FROM m_roles a
|
||||
WHERE a.role_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset?.[0] || null;
|
||||
};
|
||||
|
||||
const insertRolesDb = async (store) => {
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("m_roles", store);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset?.[0]?.inserted_id;
|
||||
|
||||
return insertedId ? await getRolesByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const updateRolesDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { role_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"m_roles",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getRolesByIdDb(id);
|
||||
};
|
||||
|
||||
const deleteRolesDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_roles
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE role_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllRolesDb,
|
||||
getRolesByIdDb,
|
||||
insertRolesDb,
|
||||
updateRolesDb,
|
||||
deleteRolesDb,
|
||||
};
|
||||
126
db/schedule.db.js
Normal file
126
db/schedule.db.js
Normal file
@@ -0,0 +1,126 @@
|
||||
const pool = require("../config");
|
||||
const { formattedDate } = require("../utils/date");
|
||||
|
||||
// Get all schedules
|
||||
const getAllScheduleDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.schedule_date"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
if (whereParamOr) queryParams = whereParamOr;
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[{ column: "a.schedule_date", param: searchParams.name, type: "date" }],
|
||||
queryParams
|
||||
);
|
||||
if (whereParamAnd) queryParams = whereParamAnd;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*,
|
||||
b.shift_name,
|
||||
b.start_time,
|
||||
b.end_time
|
||||
FROM schedule a
|
||||
LEFT JOIN m_shift b ON a.shift_id = b.shift_id
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||
ORDER BY a.schedule_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
const total =
|
||||
result?.recordset?.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
const getScheduleByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*,
|
||||
b.shift_name,
|
||||
b.start_time,
|
||||
b.end_time
|
||||
FROM schedule a
|
||||
LEFT JOIN m_shift b ON a.shift_id = b.shift_id
|
||||
WHERE a.schedule_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset?.[0] || null;
|
||||
};
|
||||
|
||||
const insertScheduleDb = async (store) => {
|
||||
const nextDays = Number(store.next_day ?? 0); // default 0 kalau tidak diisi
|
||||
const insertedRecords = [];
|
||||
|
||||
for (let i = 0; i <= nextDays; i++) {
|
||||
const nextDate = new Date(store.schedule_date);
|
||||
nextDate.setDate(nextDate.getDate() + i);
|
||||
|
||||
const formatted = formattedDate(nextDate);
|
||||
|
||||
const newStore = {
|
||||
...store,
|
||||
schedule_date: formatted,
|
||||
};
|
||||
delete newStore.next_day;
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("schedule", newStore);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset?.[0]?.inserted_id;
|
||||
|
||||
if (insertedId) {
|
||||
const record = await getScheduleByIdDb(insertedId);
|
||||
insertedRecords.push(record);
|
||||
}
|
||||
}
|
||||
|
||||
return insertedRecords;
|
||||
};
|
||||
|
||||
const updateScheduleDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { schedule_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"schedule",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getScheduleByIdDb(id);
|
||||
};
|
||||
|
||||
// Soft delete schedule
|
||||
const deleteScheduleDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE schedule
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE schedule_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllScheduleDb,
|
||||
getScheduleByIdDb,
|
||||
insertScheduleDb,
|
||||
updateScheduleDb,
|
||||
deleteScheduleDb,
|
||||
};
|
||||
100
db/shift.db.js
Normal file
100
db/shift.db.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const pool = require("../config");
|
||||
|
||||
const getAllShiftDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
// Handle pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.shift_name", "a.start_time", "a.end_time"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
if (whereParamOr) queryParams = whereParamOr;
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.shift_name", param: searchParams.name, type: "string" },
|
||||
{ column: "a.start_time", param: searchParams.start_time, type: "time" },
|
||||
{ column: "a.end_time", param: searchParams.end_time, type: "time" },
|
||||
{ column: "a.is_active", param: searchParams.status, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
if (whereParamAnd) queryParams = whereParamAnd;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*
|
||||
FROM m_shift a
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||
ORDER BY a.shift_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
const total =
|
||||
result?.recordset?.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
const getShiftByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*
|
||||
FROM m_shift a
|
||||
WHERE a.shift_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset?.[0] || null;
|
||||
};
|
||||
|
||||
const insertShiftDb = async (store) => {
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("m_shift", store);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset?.[0]?.inserted_id;
|
||||
|
||||
return insertedId ? await getShiftByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const updateShiftDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { shift_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"m_shift",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getShiftByIdDb(id);
|
||||
};
|
||||
|
||||
const deleteShiftDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_shift
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE shift_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllShiftDb,
|
||||
getShiftByIdDb,
|
||||
insertShiftDb,
|
||||
updateShiftDb,
|
||||
deleteShiftDb,
|
||||
};
|
||||
117
db/status.db.js
Normal file
117
db/status.db.js
Normal file
@@ -0,0 +1,117 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get all status
|
||||
const getAllStatusDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
// Pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
// Search
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.status_name", "a.status_description"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||
|
||||
// Filter
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.status_number", param: searchParams.number, type: "number" },
|
||||
{ column: "a.is_active", param: searchParams.is_active, type: "boolean" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ? whereParamAnd : queryParams;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*
|
||||
FROM m_status a
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
||||
${whereOrConditions ? whereOrConditions : ''}
|
||||
ORDER BY a.status_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ''};
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
|
||||
const total =
|
||||
result?.recordset.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
const getStatusByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT *
|
||||
FROM m_status a
|
||||
WHERE a.status_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Check if status_number already exists
|
||||
const checkStatusNumberExistsDb = async (status_number) => {
|
||||
const queryText = `
|
||||
SELECT 1
|
||||
FROM m_status
|
||||
WHERE status_number = $1 AND deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [status_number]);
|
||||
return result.recordset.length > 0;
|
||||
};
|
||||
|
||||
|
||||
const createStatusDb = async (data) => {
|
||||
const { query: queryText, values } = pool.buildDynamicInsert(
|
||||
"m_status",
|
||||
data
|
||||
);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset[0]?.inserted_id;
|
||||
return insertedId ? await getStatusByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const updateStatusDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { status_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"m_status",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getStatusByIdDb(id);
|
||||
};
|
||||
|
||||
const deleteStatusDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_status
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE status_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllStatusDb,
|
||||
getStatusByIdDb,
|
||||
createStatusDb,
|
||||
updateStatusDb,
|
||||
deleteStatusDb,
|
||||
checkStatusNumberExistsDb,
|
||||
};
|
||||
108
db/sub_section.db.js
Normal file
108
db/sub_section.db.js
Normal file
@@ -0,0 +1,108 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get all sub sections
|
||||
const getAllSubSectionsDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
// OR condition (pencarian bebas)
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.sub_section_code", "a.sub_section_name"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamOr ?? queryParams;
|
||||
|
||||
// AND condition (filter spesifik)
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.sub_section_code", param: searchParams.code, type: "string" },
|
||||
{ column: "a.sub_section_name", param: searchParams.name, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ?? queryParams;
|
||||
|
||||
// Query utama
|
||||
const queryText = `
|
||||
SELECT COUNT(*) OVER() AS total_data, a.*
|
||||
FROM m_plant_sub_section a
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? whereOrConditions : ""}
|
||||
ORDER BY a.sub_section_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""};
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
const total =
|
||||
result?.recordset.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
// Get sub section by ID
|
||||
const getSubSectionByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT a.*
|
||||
FROM m_plant_sub_section a
|
||||
WHERE a.sub_section_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Create new sub section
|
||||
const createSubSectionDb = async (data) => {
|
||||
// Generate kode otomatis
|
||||
const newCode = await pool.generateKode("SUB", "m_plant_sub_section", "sub_section_code");
|
||||
|
||||
const store = {
|
||||
...data,
|
||||
sub_section_code: newCode
|
||||
};
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("m_plant_sub_section", store);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset[0]?.inserted_id;
|
||||
|
||||
return insertedId ? await getSubSectionByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
// Update sub section
|
||||
const updateSubSectionDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { sub_section_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate("m_plant_sub_section", store, whereData);
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
|
||||
return getSubSectionByIdDb(id);
|
||||
};
|
||||
|
||||
// Soft delete sub section
|
||||
const deleteSubSectionDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_plant_sub_section
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE sub_section_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllSubSectionsDb,
|
||||
getSubSectionByIdDb,
|
||||
createSubSectionDb,
|
||||
updateSubSectionDb,
|
||||
deleteSubSectionDb,
|
||||
};
|
||||
140
db/tags.db.js
Normal file
140
db/tags.db.js
Normal file
@@ -0,0 +1,140 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get all tags
|
||||
const getAllTagsDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
[
|
||||
"a.tag_name",
|
||||
"a.tag_code",
|
||||
"a.tag_number",
|
||||
"a.data_type",
|
||||
"a.unit",
|
||||
"b.device_name",
|
||||
"c.sub_section_name",
|
||||
],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
if (whereParamOr) queryParams = whereParamOr;
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.tag_name", param: searchParams.name, type: "string" },
|
||||
{ column: "a.tag_code", param: searchParams.code, type: "string" },
|
||||
{ column: "a.data_type", param: searchParams.data, type: "string" },
|
||||
{ column: "a.unit", param: searchParams.unit, type: "string" },
|
||||
{ column: "b.device_name", param: searchParams.device, type: "string" },
|
||||
{
|
||||
column: "b.device_code",
|
||||
param: searchParams.device,
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
column: "c.sub_section_name",
|
||||
param: searchParams.subsection,
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
if (whereParamAnd) queryParams = whereParamAnd;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*,
|
||||
b.device_name,
|
||||
b.device_code,
|
||||
c.sub_section_name
|
||||
FROM m_tags a
|
||||
LEFT JOIN m_device b ON a.device_id = b.device_id
|
||||
LEFT JOIN m_plant_sub_section c ON a.sub_section_id = c.sub_section_id
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||
ORDER BY a.tag_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
|
||||
const total =
|
||||
result?.recordset?.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
const getTagsByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*,
|
||||
b.device_name,
|
||||
b.device_code,
|
||||
c.sub_section_name
|
||||
FROM m_tags a
|
||||
LEFT JOIN m_device b ON a.device_id = b.device_id
|
||||
LEFT JOIN m_plant_sub_section c ON a.sub_section_id = c.sub_section_id
|
||||
WHERE a.tag_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
const createTagsDb = async (data) => {
|
||||
const newCode = await pool.generateKode("TAG", "m_tags", "tag_code");
|
||||
|
||||
const store = {
|
||||
...data,
|
||||
tag_code: newCode,
|
||||
};
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("m_tags", store);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset[0]?.inserted_id;
|
||||
|
||||
return insertedId ? await getTagsByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const updateTagsDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { tag_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"m_tags",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getTagsByIdDb(id);
|
||||
};
|
||||
|
||||
// Soft delete tag
|
||||
const deleteTagsDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_tags
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE tag_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllTagsDb,
|
||||
getTagsByIdDb,
|
||||
createTagsDb,
|
||||
updateTagsDb,
|
||||
deleteTagsDb,
|
||||
};
|
||||
119
db/unit.db.js
Normal file
119
db/unit.db.js
Normal file
@@ -0,0 +1,119 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get all units
|
||||
const getAllUnitsDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
// Pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
// Search
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.unit_code", "a.unit_name"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||
|
||||
// Filter
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.unit_code", param: searchParams.code, type: "string" },
|
||||
{ column: "a.unit_name", param: searchParams.name, type: "string" },
|
||||
{ column: "a.tag_id", param: searchParams.tag, type: "number" },
|
||||
{ column: "a.is_active", param: searchParams.status, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ? whereParamAnd : queryParams;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*,
|
||||
COALESCE(a.unit_code, '') + ' - ' + COALESCE(a.unit_name, '') AS unit_code_name
|
||||
FROM m_unit a
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? whereOrConditions : ""}
|
||||
ORDER BY a.unit_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""};
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
|
||||
const total =
|
||||
result?.recordset.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
// Get unit by ID
|
||||
const getUnitByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*,
|
||||
COALESCE(a.unit_code, '') + ' - ' + COALESCE(a.unit_name, '') AS unit_code_name
|
||||
FROM m_unit a
|
||||
WHERE a.unit_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Create unit
|
||||
const createUnitDb = async (data) => {
|
||||
const newCode = await pool.generateKode("UNT", "m_unit", "unit_code");
|
||||
|
||||
const store = {
|
||||
...data,
|
||||
unit_code: newCode,
|
||||
};
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("m_unit", store);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset[0]?.inserted_id;
|
||||
return insertedId ? await getUnitByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
// Update unit
|
||||
const updateUnitDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { unit_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"m_unit",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getUnitByIdDb(id);
|
||||
};
|
||||
|
||||
// Soft delete unit
|
||||
const deleteUnitDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_unit
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE unit_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
// Export
|
||||
module.exports = {
|
||||
getAllUnitsDb,
|
||||
getUnitByIdDb,
|
||||
createUnitDb,
|
||||
updateUnitDb,
|
||||
deleteUnitDb,
|
||||
};
|
||||
266
db/user.db.js
266
db/user.db.js
@@ -1,142 +1,204 @@
|
||||
const pool = require("../config");
|
||||
|
||||
const getAllUsersDb = async (param) => {
|
||||
// limit & offset masuk fixed param
|
||||
let fixedParams = [param.fixed.limit, param.fixed.offset, param.fixed.tenantID];
|
||||
// Get all users
|
||||
const getAllUsersDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
const { whereOrConditions, whereParam } = pool.buildStringOrIlike(
|
||||
param.filterCriteria.column,
|
||||
param.filterCriteria.criteria,
|
||||
fixedParams
|
||||
// Pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
// Search
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
[
|
||||
"u.user_fullname",
|
||||
"u.user_name",
|
||||
"u.user_email",
|
||||
"r.role_name"
|
||||
],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
const { whereConditions, queryParams } = pool.buildFilterQuery(param.filterQuery, whereParam);npm
|
||||
|
||||
const query = `
|
||||
SELECT mut.*, mr.role_name, COUNT(*) OVER() AS total
|
||||
FROM m_users mut
|
||||
LEFT JOIN system.role_tenant mr ON mr.role_id = mut.role_id
|
||||
WHERE mut.deleted_at IS NULL AND mut.is_sa != 1 AND mut.tenant_id = $3
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? whereOrConditions : ""}
|
||||
ORDER BY mut.user_id
|
||||
OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY
|
||||
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||
|
||||
// Filter
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "u.user_fullname", param: searchParams.fullname, type: "string" },
|
||||
{ column: "u.user_name", param: searchParams.username, type: "string" },
|
||||
{ column: "u.user_email", param: searchParams.email, type: "string" },
|
||||
{ column: "r.role_name", param: searchParams.role, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ? whereParamAnd : queryParams;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
u.user_id, u.user_fullname, u.user_name, u.user_email, u.user_phone,
|
||||
u.is_active, u.is_sa, u.is_approve, u.approved_by,
|
||||
approver.user_fullname AS approved_by_name,
|
||||
u.approved_at, u.created_at, u.updated_at, u.deleted_at,
|
||||
u.updated_by, u.deleted_by,
|
||||
r.role_id, r.role_name, r.role_description, r.role_level
|
||||
FROM m_users u
|
||||
LEFT JOIN m_roles r ON u.role_id = r.role_id
|
||||
LEFT JOIN m_users approver ON u.approved_by = approver.user_id
|
||||
WHERE u.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(' AND ')}` : ''}
|
||||
${whereOrConditions ? whereOrConditions : ''}
|
||||
ORDER BY u.user_id ASC
|
||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ''};
|
||||
`;
|
||||
|
||||
const result = await pool.query(query, queryParams);
|
||||
const rows = result.recordset;
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
|
||||
const total = rows.length > 0 ? parseInt(rows[0].total, 10) : 0;
|
||||
return { data: rows, total };
|
||||
};
|
||||
|
||||
const createUserDb = async (param) => {
|
||||
const insertData = {
|
||||
tenant_id: param.tenantID,
|
||||
user_fullname: param.userFullname,
|
||||
user_name: param.userName,
|
||||
user_email: param.userEmail ?? null,
|
||||
user_password: param.userPassword,
|
||||
role_id: param.roleId ?? null,
|
||||
is_active: param.isActive ? 1 : 0,
|
||||
created_by: param.userID,
|
||||
updated_by: param.userID,
|
||||
};
|
||||
|
||||
const { query, values } = pool.buildDynamicInsert("m_users", insertData);
|
||||
|
||||
const result = await pool.query(query, values);
|
||||
return result.recordset[0];
|
||||
const total =
|
||||
result?.recordset.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
// Get user by ID
|
||||
const getUserByIdDb = async (id) => {
|
||||
const query = `
|
||||
SELECT mut.*
|
||||
FROM m_users mut
|
||||
WHERE mut.user_id = $1
|
||||
const queryText = `
|
||||
SELECT
|
||||
u.user_id, u.user_fullname, u.user_name, u.user_email, u.user_phone,
|
||||
u.is_active, u.is_sa, u.is_approve, u.approved_by,
|
||||
approver.user_fullname AS approved_by_name,
|
||||
u.approved_at, u.created_at, u.updated_at, u.deleted_at,
|
||||
u.updated_by, u.deleted_by,
|
||||
r.role_id, r.role_name, r.role_description, r.role_level
|
||||
FROM m_users u
|
||||
LEFT JOIN m_roles r ON u.role_id = r.role_id
|
||||
LEFT JOIN m_users approver ON u.approved_by = approver.user_id
|
||||
WHERE u.user_id = $1 AND u.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(query, [id]);
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset[0];
|
||||
};
|
||||
|
||||
// Get user by email
|
||||
const getUserByUserEmailDb = async (email) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
u.user_id, u.user_fullname, u.user_name, u.user_email, u.user_phone,
|
||||
u.user_password, u.is_active, u.is_sa, u.is_approve, u.role_id,
|
||||
r.role_name, r.role_description, r.role_level
|
||||
FROM m_users u
|
||||
LEFT JOIN m_roles r ON u.role_id = r.role_id
|
||||
WHERE u.user_email = $1 AND u.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [email]);
|
||||
return result.recordset[0];
|
||||
};
|
||||
|
||||
// Get user by username
|
||||
const getUserByUsernameDb = async (username) => {
|
||||
const query = `
|
||||
SELECT mut.*
|
||||
FROM m_users mut
|
||||
WHERE LOWER(mut.username) = LOWER($1)
|
||||
const queryText = `
|
||||
SELECT
|
||||
u.user_id, u.user_fullname, u.user_name, u.user_email, u.user_phone,
|
||||
u.user_password, u.is_active, u.is_sa, u.is_approve, u.role_id,
|
||||
r.role_name, r.role_description, r.role_level
|
||||
FROM m_users u
|
||||
LEFT JOIN m_roles r ON u.role_id = r.role_id
|
||||
WHERE u.user_name = $1 AND u.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(query, [username]);
|
||||
const result = await pool.query(queryText, [username]);
|
||||
return result.recordset[0];
|
||||
};
|
||||
|
||||
const getUserByUserEmailDb = async (userEmail) => {
|
||||
const query = `
|
||||
SELECT mut.*
|
||||
FROM m_users mut
|
||||
WHERE LOWER(mut.user_email) = LOWER($1)
|
||||
`;
|
||||
const result = await pool.query(query, [userEmail]);
|
||||
return result.recordset[0];
|
||||
// Create user
|
||||
const createUserDb = async (data) => {
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("m_users", data);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset[0]?.inserted_id;
|
||||
return insertedId ? await getUserByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const updateUserDb = async (param) => {
|
||||
const updateData = {
|
||||
tenant_id: param.tenantID,
|
||||
user_fullname: param.userFullname,
|
||||
user_name: param.userName,
|
||||
user_email: param.userEmail ?? null,
|
||||
user_password: param.userPassword,
|
||||
role_id: param.roleId ?? null,
|
||||
is_active: param.isActive ? 1 : 0,
|
||||
updated_by: param.userID,
|
||||
};
|
||||
|
||||
const whereData = { user_id: param.id };
|
||||
|
||||
const { query, values } = pool.buildDynamicUpdate("m_users", updateData, whereData);
|
||||
|
||||
const result = await pool.query(query, values);
|
||||
return result.recordset[0];
|
||||
// Update user
|
||||
const updateUserDb = async (userId, data) => {
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate("m_users", data, {
|
||||
user_id: userId,
|
||||
});
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getUserByIdDb(userId);
|
||||
};
|
||||
|
||||
const deleteUserDb = async (id, userID) => {
|
||||
const query = `
|
||||
UPDATE m_users
|
||||
SET deleted_at = GETDATE(), deleted_by = $1
|
||||
WHERE user_id = $2;
|
||||
|
||||
SELECT * FROM m_users WHERE user_id = $2
|
||||
// Approve user
|
||||
const approveUserDb = async (userId, approverId) => {
|
||||
const queryText = `
|
||||
UPDATE m_users
|
||||
SET
|
||||
is_approve = 2,
|
||||
approved_by = $1,
|
||||
approved_at = CURRENT_TIMESTAMP,
|
||||
updated_by = $1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE user_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(query, [userID, id]);
|
||||
return result.recordset[0];
|
||||
await pool.query(queryText, [approverId, userId]);
|
||||
return true;
|
||||
};
|
||||
|
||||
const changeUserPasswordDb = async (hashedPassword, userEmail, tenantId) => {
|
||||
const query = `
|
||||
UPDATE m_users
|
||||
SET user_password = $1
|
||||
WHERE user_email = $2 AND tenant_id = $3
|
||||
// Reject user
|
||||
const rejectUserDb = async (userId, approverId) => {
|
||||
const queryText = `
|
||||
UPDATE m_users
|
||||
SET
|
||||
is_approve = 0,
|
||||
approved_by = $1,
|
||||
approved_at = CURRENT_TIMESTAMP,
|
||||
updated_by = $1,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE user_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
return pool.query(query, [hashedPassword, userEmail, tenantId]);
|
||||
await pool.query(queryText, [approverId, userId]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Change user password
|
||||
const changeUserPasswordDb = async (userId, newPassword) => {
|
||||
const queryText = `
|
||||
UPDATE m_users
|
||||
SET user_password = $1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE user_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [newPassword, userId]);
|
||||
return true;
|
||||
};
|
||||
|
||||
const getAllRoleDb = async (tenantId) => {
|
||||
const query = `
|
||||
SELECT *
|
||||
FROM system.role_tenant
|
||||
WHERE deleted_at IS NULL AND tenant_id = $1
|
||||
// Soft delete user
|
||||
const deleteUserDb = async (userId, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE m_users
|
||||
SET
|
||||
deleted_at = CURRENT_TIMESTAMP,
|
||||
deleted_by = $1,
|
||||
is_active = 0
|
||||
WHERE user_id = $2
|
||||
AND deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(query, [tenantId]);
|
||||
return result.recordset;
|
||||
await pool.query(queryText, [deletedBy, userId]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllUsersDb,
|
||||
getUserByIdDb,
|
||||
getUserByUserEmailDb,
|
||||
updateUserDb,
|
||||
createUserDb,
|
||||
deleteUserDb,
|
||||
getUserByUsernameDb,
|
||||
createUserDb,
|
||||
updateUserDb,
|
||||
approveUserDb,
|
||||
rejectUserDb,
|
||||
changeUserPasswordDb,
|
||||
getAllRoleDb,
|
||||
deleteUserDb,
|
||||
};
|
||||
|
||||
@@ -1,43 +1,29 @@
|
||||
const setResponse = async (data = [], message = "success", statusCode = 200) => {
|
||||
const response = {
|
||||
data,
|
||||
total: data.length,
|
||||
message,
|
||||
statusCode
|
||||
}
|
||||
const setResponse = (data = null, message = "success", statusCode = 200) => {
|
||||
const total = Array.isArray(data) ? data.length : null;
|
||||
|
||||
return response
|
||||
return {
|
||||
message,
|
||||
statusCode,
|
||||
rows: total,
|
||||
data,
|
||||
};
|
||||
};
|
||||
|
||||
const setResponsePaging = async (data = [], total, limit, page, message = "success", statusCode = 200) => {
|
||||
const setResponsePaging = async (queryParam, data = [], message = "success", statusCode = 200) => {
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
const totalPages = Math.ceil(data?.total / Number(queryParam.limit ?? 0));
|
||||
|
||||
const response = {
|
||||
message,
|
||||
statusCode,
|
||||
data,
|
||||
total: data.length,
|
||||
rows: data?.data?.length,
|
||||
paging: {
|
||||
total,
|
||||
limit,
|
||||
page,
|
||||
page_total: totalPages
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
};
|
||||
|
||||
const setPaging = async (total, limit, page) => {
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
const response = {
|
||||
total,
|
||||
limit,
|
||||
page,
|
||||
page_total: totalPages
|
||||
current_limit: Number(queryParam.limit ?? 0),
|
||||
current_page: Number(queryParam.page ?? 0),
|
||||
total_limit: data?.total,
|
||||
total_page: totalPages
|
||||
},
|
||||
data: data?.data ?? []
|
||||
}
|
||||
|
||||
return response
|
||||
@@ -86,4 +72,19 @@ function orderByClauseQuery(orderParams) {
|
||||
return orderByClause
|
||||
}
|
||||
|
||||
module.exports = { setResponse, setResponsePaging, setPaging, convertId, formatToYYYYMMDD, orderByClauseQuery };
|
||||
const checkValidate = (validateSchema, req) => {
|
||||
const { error, value } = validateSchema.validate(req.body || {}, { abortEarly: false });
|
||||
if (error) {
|
||||
const errors = error.details.reduce((acc, cur) => {
|
||||
const field = Array.isArray(cur.path) ? cur.path.join('.') : String(cur.path);
|
||||
if (!acc[field]) acc[field] = [];
|
||||
acc[field].push(cur.message);
|
||||
return acc;
|
||||
}, {});
|
||||
return { error: errors, value }
|
||||
}
|
||||
|
||||
return { error, value }
|
||||
}
|
||||
|
||||
module.exports = { setResponse, setResponsePaging, convertId, formatToYYYYMMDD, orderByClauseQuery, checkValidate };
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
const validateUser = (email, password) => {
|
||||
const validEmail = typeof email === "string" && email.trim() !== "";
|
||||
const validPassword =
|
||||
typeof password === "string" && password.trim().length >= 6;
|
||||
|
||||
return validEmail && validPassword;
|
||||
};
|
||||
|
||||
module.exports = validateUser;
|
||||
2
index.js
2
index.js
@@ -5,6 +5,6 @@ const { logger } = require("./utils/logger");
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
const PORT = process.env.PORT || 9524;
|
||||
const PORT = process.env.PORT || 9533;
|
||||
|
||||
server.listen(PORT, () => logger.info(`Magic happening on port: ${PORT}`));
|
||||
|
||||
38
middleware/verifyAccess.js
Normal file
38
middleware/verifyAccess.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
const { getUserByIdDb } = require("../db/user.db");
|
||||
|
||||
const verifyAccess = (minLevel = 1, allowUnapprovedReadOnly = false) => {
|
||||
return async (req, res, next) => {
|
||||
try {
|
||||
const user = req.user;
|
||||
|
||||
if (!user) throw new ErrorHandler(401, "Unauthorized: User not found");
|
||||
|
||||
// Super Admin bypass semua
|
||||
if (user.is_sa) return next();
|
||||
|
||||
const fullUser = await getUserByIdDb(user.user_id);
|
||||
if (!fullUser) throw new ErrorHandler(403, "Forbidden: User not found");
|
||||
|
||||
if (!fullUser.is_approve) {
|
||||
if (req.method !== "GET") {
|
||||
throw new ErrorHandler(403, "Account not approved — read-only access");
|
||||
}
|
||||
|
||||
if (allowUnapprovedReadOnly) return next();
|
||||
|
||||
throw new ErrorHandler(403, "Account not approved");
|
||||
}
|
||||
|
||||
if (!fullUser.role_level || fullUser.role_level < minLevel) {
|
||||
throw new ErrorHandler(403, "Forbidden: Insufficient role level");
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = verifyAccess;
|
||||
@@ -1,14 +0,0 @@
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
|
||||
module.exports = (req, res, next) => {
|
||||
const { roles } = req.user;
|
||||
if (roles && roles.includes("admin")) {
|
||||
req.user = {
|
||||
...req.user,
|
||||
roles,
|
||||
};
|
||||
return next();
|
||||
} else {
|
||||
throw new ErrorHandler(401, "require admin role");
|
||||
}
|
||||
};
|
||||
@@ -1,47 +1,48 @@
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
const JWTService = require('../utils/jwt');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
const verifyToken = (req, res, next) => {
|
||||
const authHeader = req.header("Authorization");
|
||||
// console.log("authHeader", authHeader)
|
||||
|
||||
// Pastikan header Authorization ada dan berisi token
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
throw new ErrorHandler(401, "Token missing or invalid");
|
||||
}
|
||||
|
||||
// Ambil token dari header Authorization
|
||||
const token = authHeader.split(" ")[1];
|
||||
function setUser(req, decoded) {
|
||||
req.user = {
|
||||
userId: decoded.user_id,
|
||||
fullname: decoded.user_fullname,
|
||||
username: decoded.user_name,
|
||||
email: decoded.user_email,
|
||||
roleId: decoded.role_id,
|
||||
roleName: decoded.role_name,
|
||||
is_sa: decoded.is_sa
|
||||
};
|
||||
}
|
||||
|
||||
function verifyAccessToken(req, res, next) {
|
||||
try {
|
||||
// const decoded = jwt.decode(token, { complete: true });
|
||||
// console.log("decoded", decoded)
|
||||
// console.log("==============================")
|
||||
// console.log("token", token)
|
||||
// console.log("process.env.SECRET", process.env.SECRET)
|
||||
// // console.log("==============================> ", jwt.verify(token, process.env.SECRET))
|
||||
// jwt.verify(token, process.env.SECRET, (err, decoded) => {
|
||||
// if (err) {
|
||||
// console.error('Error verifying token: ==============================>', err.message);
|
||||
// } else {
|
||||
// console.log('Decoded payload: ==============================>', decoded);
|
||||
// }
|
||||
// });
|
||||
let token = req.cookies?.accessToken;
|
||||
|
||||
if (!token) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer')) {
|
||||
throw new ErrorHandler(401, 'Access Token is required');
|
||||
}
|
||||
token = authHeader.split(' ')[1];
|
||||
}
|
||||
|
||||
const decoded = JWTService.verifyToken(token);
|
||||
|
||||
req.user = decoded;
|
||||
|
||||
const verified = jwt.verify(token, process.env.SECRET);
|
||||
req.tokenExtract = verified;
|
||||
// console.log(req.tokenExtract);
|
||||
|
||||
req.userID = req.tokenExtract.user_id
|
||||
req.tenantID = req.tokenExtract.tenant_id
|
||||
req.roleID = req.tokenExtract.role_id
|
||||
req.body.userID = req.tokenExtract.user_id
|
||||
req.body.tenantID = req.tokenExtract.tenant_id
|
||||
req.query.tenantID = req.tokenExtract.tenant_id
|
||||
next();
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(401, error.message || "Invalid Token");
|
||||
}
|
||||
};
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
return next(new ErrorHandler(401, 'Access token expired'));
|
||||
}
|
||||
|
||||
module.exports = verifyToken;
|
||||
if (error.name === 'JsonWebTokenError') {
|
||||
return next(new ErrorHandler(401, 'Invalid access token'));
|
||||
}
|
||||
|
||||
return next(new ErrorHandler(500, 'Internal authentication error'));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
verifyAccessToken
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
const router = require("express").Router();
|
||||
const {
|
||||
loginUser,
|
||||
} = require("../controllers/auth.controller");
|
||||
|
||||
router.post("/login", loginUser);
|
||||
|
||||
module.exports = router;
|
||||
11
routes/auth.route.js
Normal file
11
routes/auth.route.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const express = require('express');
|
||||
const AuthController = require("../controllers/auth.controller");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/login', AuthController.login);
|
||||
router.post('/register', AuthController.register);
|
||||
router.get('/generate-captcha', AuthController.generateCaptcha);
|
||||
router.post('/refresh-token', AuthController.refreshToken);
|
||||
|
||||
module.exports = router;
|
||||
17
routes/device.route.js
Normal file
17
routes/device.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const DeviceController = require('../controllers/device.controller');
|
||||
const verifyToken = require("../middleware/verifyToken")
|
||||
const verifyAccess = require("../middleware/verifyAccess")
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken.verifyAccessToken, DeviceController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), DeviceController.create);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken.verifyAccessToken, DeviceController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), DeviceController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), DeviceController.delete);
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,8 +1,25 @@
|
||||
const router = require("express").Router();
|
||||
const auth = require("./auth");
|
||||
const users = require("./users");
|
||||
const auth = require("./auth.route");
|
||||
const users = require("./users.route");
|
||||
const device = require('./device.route');
|
||||
const roles = require('./roles.route');
|
||||
const tags = require("./tags.route");
|
||||
const subSection = require("./sub_section.route");
|
||||
const shift = require("./shift.route");
|
||||
const schedule = require("./schedule.route");
|
||||
const status = require("./status.route");
|
||||
const unit = require("./unit.route")
|
||||
|
||||
router.use("/auth", auth);
|
||||
router.use("/users", users);
|
||||
router.use("/user", users);
|
||||
router.use("/device", device);
|
||||
router.use("/roles", roles);
|
||||
router.use("/tags", tags);
|
||||
router.use("/plant-sub-section", subSection);
|
||||
router.use("/shift", shift);
|
||||
router.use("/schedule", schedule);
|
||||
router.use("/status", status);
|
||||
router.use("/unit", unit);
|
||||
|
||||
|
||||
module.exports = router;
|
||||
|
||||
17
routes/roles.route.js
Normal file
17
routes/roles.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const RolesController = require('../controllers/roles.controller');
|
||||
const verifyToken = require("../middleware/verifyToken")
|
||||
const verifyAccess = require("../middleware/verifyAccess")
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken.verifyAccessToken, RolesController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), RolesController.create);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken.verifyAccessToken, RolesController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), RolesController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), RolesController.delete);
|
||||
|
||||
module.exports = router;
|
||||
17
routes/schedule.route.js
Normal file
17
routes/schedule.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const ScheduleController = require('../controllers/schedule.controller');
|
||||
const verifyToken = require("../middleware/verifyToken")
|
||||
const verifyAccess = require("../middleware/verifyAccess")
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken.verifyAccessToken, ScheduleController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), ScheduleController.create);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken.verifyAccessToken, ScheduleController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), ScheduleController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), ScheduleController.delete);
|
||||
|
||||
module.exports = router;
|
||||
17
routes/shift.route.js
Normal file
17
routes/shift.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const ShiftController = require('../controllers/shift.controller');
|
||||
const verifyToken = require("../middleware/verifyToken")
|
||||
const verifyAccess = require("../middleware/verifyAccess")
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken.verifyAccessToken, ShiftController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), ShiftController.create);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken.verifyAccessToken, ShiftController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), ShiftController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), ShiftController.delete);
|
||||
|
||||
module.exports = router;
|
||||
17
routes/status.route.js
Normal file
17
routes/status.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const StatusController = require('../controllers/status.controller');
|
||||
const verifyToken = require("../middleware/verifyToken");
|
||||
const verifyAccess = require("../middleware/verifyAccess");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken.verifyAccessToken, StatusController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), StatusController.create);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken.verifyAccessToken, StatusController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), StatusController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), StatusController.delete);
|
||||
|
||||
module.exports = router;
|
||||
17
routes/sub_section.route.js
Normal file
17
routes/sub_section.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const PlantSubSectionController = require('../controllers/sub_section.controller');
|
||||
const verifyToken = require('../middleware/verifyToken');
|
||||
const verifyAccess = require('../middleware/verifyAccess');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route('/')
|
||||
.get(verifyToken.verifyAccessToken, PlantSubSectionController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), PlantSubSectionController.create);
|
||||
|
||||
router.route('/:id')
|
||||
.get(verifyToken.verifyAccessToken, PlantSubSectionController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), PlantSubSectionController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), PlantSubSectionController.delete);
|
||||
|
||||
module.exports = router;
|
||||
17
routes/tags.route.js
Normal file
17
routes/tags.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const TagsController = require('../controllers/tags.controller');
|
||||
const verifyToken = require("../middleware/verifyToken")
|
||||
const verifyAccess = require("../middleware/verifyAccess")
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken.verifyAccessToken, TagsController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), TagsController.create);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken.verifyAccessToken, TagsController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), TagsController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), TagsController.delete);
|
||||
|
||||
module.exports = router;
|
||||
17
routes/unit.route.js
Normal file
17
routes/unit.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const UnitController = require('../controllers/unit.controller');
|
||||
const verifyToken = require('../middleware/verifyToken');
|
||||
const verifyAccess = require('../middleware/verifyAccess');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route('/')
|
||||
.get(verifyToken.verifyAccessToken, UnitController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), UnitController.create);
|
||||
|
||||
router.route('/:id')
|
||||
.get(verifyToken.verifyAccessToken, UnitController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), UnitController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), UnitController.delete);
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,33 +0,0 @@
|
||||
const {
|
||||
getAllUsers,
|
||||
createUser,
|
||||
deleteUser,
|
||||
getUserById,
|
||||
updateUser,
|
||||
getUserProfile,
|
||||
getAllRoles,
|
||||
getAllStatusUsers
|
||||
} = require("../controllers/users.controller");
|
||||
const router = require("express").Router();
|
||||
const verifyAdmin = require("../middleware/verifyAdmin");
|
||||
const verifyToken = require("../middleware/verifyToken");
|
||||
|
||||
router.get("/roles", getAllRoles);
|
||||
|
||||
router.route("/profile")
|
||||
.get(getUserProfile);
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken, getAllUsers)
|
||||
.post(verifyToken, createUser);
|
||||
|
||||
router
|
||||
.route("/status")
|
||||
.get(verifyToken, getAllStatusUsers);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken, getUserById)
|
||||
.put(verifyToken, updateUser)
|
||||
.delete(verifyToken, deleteUser);
|
||||
|
||||
module.exports = router;
|
||||
26
routes/users.route.js
Normal file
26
routes/users.route.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const express = require('express');
|
||||
const UserController = require('../controllers/users.controller');
|
||||
const verifyToken = require('../middleware/verifyToken');
|
||||
const verifyAccess = require('../middleware/verifyAccess');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route('/')
|
||||
.get(verifyToken.verifyAccessToken, UserController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), UserController.create);
|
||||
|
||||
router.route('/:id')
|
||||
.get(verifyToken.verifyAccessToken, UserController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), UserController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), UserController.delete);
|
||||
|
||||
router.route('/change-password/:id')
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), UserController.changePassword);
|
||||
|
||||
router.route('/:id/approve')
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), UserController.approve);
|
||||
|
||||
router.route('/:id/reject')
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), UserController.reject);
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,77 +1,130 @@
|
||||
const bcrypt = require("bcrypt");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const validateUser = require("../helpers/validateUser");
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
const {
|
||||
getUserByUserEmailDb,
|
||||
createUserDb,
|
||||
getUserByUsernameDb
|
||||
} = require("../db/user.db");
|
||||
const { logger } = require("../utils/logger");
|
||||
} = require('../db/user.db');
|
||||
const { hashPassword, comparePassword } = require('../helpers/hashPassword');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
const JWTService = require('../utils/jwt');
|
||||
|
||||
class AuthService {
|
||||
|
||||
async login(username, password, tenantId) {
|
||||
// Register
|
||||
static async register(data) {
|
||||
try {
|
||||
// if (!validateUser(username, password)) {
|
||||
// throw new ErrorHandler(403, "Invalid login");
|
||||
// }
|
||||
const existingEmail = await getUserByUserEmailDb(data.user_email);
|
||||
const existingUsername = await getUserByUsernameDb(data.user_name);
|
||||
|
||||
const user = await getUserByUsernameDb(username, tenantId);
|
||||
console.log(user);
|
||||
|
||||
if (!user) {
|
||||
throw new ErrorHandler(403, "Username not found.");
|
||||
if (existingUsername) {
|
||||
throw new ErrorHandler(400, 'Username is already taken');
|
||||
}
|
||||
if (existingEmail) {
|
||||
throw new ErrorHandler(400, 'Email is already taken');
|
||||
}
|
||||
|
||||
const isCorrectPassword = password === user.password
|
||||
if (!isCorrectPassword) {
|
||||
throw new ErrorHandler(403, "Username or password incorrect.");
|
||||
}
|
||||
const hashedPassword = await hashPassword(data.user_password);
|
||||
|
||||
const dataToken = {
|
||||
tenant_id: tenantId,
|
||||
user_id: user.user_id,
|
||||
username,
|
||||
fullname: user.full_name,
|
||||
role_id: user.role_id
|
||||
}
|
||||
const userId = await createUserDb({
|
||||
user_fullname: data.user_fullname,
|
||||
user_name: data.user_name,
|
||||
user_email: data.user_email,
|
||||
user_phone: data.user_phone,
|
||||
user_password: hashedPassword,
|
||||
is_sa: 0,
|
||||
is_active: 1,
|
||||
is_approve: 1,
|
||||
});
|
||||
|
||||
const token = await this.signToken(dataToken);
|
||||
const refreshToken = await this.signRefreshToken(dataToken);
|
||||
|
||||
return {
|
||||
token,
|
||||
refreshToken,
|
||||
role_id: dataToken.role_id,
|
||||
tenant_id: tenantId,
|
||||
user: {
|
||||
user_id: dataToken.user_id,
|
||||
fullname: dataToken.fullname,
|
||||
username: dataToken.username,
|
||||
},
|
||||
const newUser = {
|
||||
user_id: userId,
|
||||
user_fullname: data.user_fullname,
|
||||
user_name: data.user_name,
|
||||
user_email: data.user_email,
|
||||
user_phone: data.user_phone
|
||||
};
|
||||
|
||||
return { user: newUser };
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async signToken(data) {
|
||||
// Login
|
||||
static async login(data) {
|
||||
try {
|
||||
// console.log("signToken process.env.SECRET", process.env.SECRET)
|
||||
return jwt.sign(data, process.env.SECRET, { expiresIn: "23h" });
|
||||
const { identifier, password, captcha, captchaText } = data;
|
||||
|
||||
if (!captcha || captcha.toLowerCase() !== captchaText.toLowerCase()) {
|
||||
throw new ErrorHandler(400, 'Invalid captcha');
|
||||
}
|
||||
|
||||
let user;
|
||||
if (identifier.includes('@')) {
|
||||
user = await getUserByUserEmailDb(identifier);
|
||||
} else {
|
||||
user = await getUserByUsernameDb(identifier);
|
||||
}
|
||||
|
||||
if (!user) throw new ErrorHandler(401, 'Invalid credentials');
|
||||
|
||||
const passwordMatch = await comparePassword(password, user.user_password);
|
||||
if (!passwordMatch) throw new ErrorHandler(401, 'Invalid credentials');
|
||||
|
||||
if (!user.is_active) throw new ErrorHandler(403, 'User is inactive');
|
||||
if (!user.is_approve) throw new ErrorHandler(403, 'Your account has not been approved by admin yet.');
|
||||
|
||||
const payload = {
|
||||
user_id: user.user_id,
|
||||
user_fullname: user.user_fullname,
|
||||
user_name: user.user_name,
|
||||
user_email: user.user_email,
|
||||
user_phone: user.user_phone,
|
||||
role_id: user.role_id,
|
||||
role_name: user.role_name,
|
||||
is_sa: user.is_sa
|
||||
};
|
||||
|
||||
const tokens = JWTService.generateTokenPair(payload);
|
||||
return { user: payload, tokens };
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
throw new ErrorHandler(500, "An error occurred");
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async signRefreshToken(data) {
|
||||
// Refresh Token
|
||||
static async refreshToken(refreshToken) {
|
||||
try {
|
||||
return jwt.sign(data, process.env.REFRESH_SECRET, { expiresIn: "23h" });
|
||||
let decoded;
|
||||
try {
|
||||
decoded = JWTService.verifyRefreshToken(refreshToken);
|
||||
} catch (err) {
|
||||
if (err.message.includes('expired')) {
|
||||
throw new ErrorHandler(401, 'Refresh token expired');
|
||||
}
|
||||
throw new ErrorHandler(401, 'Invalid refresh token');
|
||||
}
|
||||
|
||||
const payload = {
|
||||
user_id: decoded.user_id,
|
||||
user_fullname: decoded.user_fullname,
|
||||
user_name: decoded.user_name,
|
||||
user_email: decoded.user_email,
|
||||
user_phone: decoded.user_phone,
|
||||
role_id: decoded.role_id,
|
||||
role_name: decoded.role_name,
|
||||
is_sa: decoded.is_sa
|
||||
};
|
||||
|
||||
const accessToken = JWTService.generateAccessToken(payload);
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
tokenType: 'Bearer',
|
||||
expiresIn: 900
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
throw new ErrorHandler(500, error.message);
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AuthService();
|
||||
module.exports = AuthService;
|
||||
|
||||
88
services/device.service.js
Normal file
88
services/device.service.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const {
|
||||
getAllDevicesDb,
|
||||
getDeviceByIdDb,
|
||||
createDeviceDb,
|
||||
updateDeviceDb,
|
||||
deleteDeviceDb
|
||||
} = require('../db/device.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class DeviceService {
|
||||
// Get all devices
|
||||
static async getAllDevices(param) {
|
||||
try {
|
||||
const results = await getAllDevicesDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get device by ID
|
||||
static async getDeviceById(id) {
|
||||
try {
|
||||
const result = await getDeviceByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Device not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create device
|
||||
static async createDevice(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await createDeviceDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update device
|
||||
static async updateDevice(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getDeviceByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Device not found');
|
||||
}
|
||||
|
||||
const result = await updateDeviceDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete device
|
||||
static async deleteDevice(id, userId) {
|
||||
try {
|
||||
const dataExist = await getDeviceByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Device not found');
|
||||
}
|
||||
|
||||
const result = await deleteDeviceDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeviceService;
|
||||
88
services/roles.service.js
Normal file
88
services/roles.service.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const {
|
||||
getAllRolesDb,
|
||||
getRolesByIdDb,
|
||||
insertRolesDb,
|
||||
updateRolesDb,
|
||||
deleteRolesDb
|
||||
} = require('../db/roles.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class RolesService {
|
||||
// Get all Roles
|
||||
static async getAllRoles(param) {
|
||||
try {
|
||||
const results = await getAllRolesDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get Roles by ID
|
||||
static async getRolesById(id) {
|
||||
try {
|
||||
const result = await getRolesByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Roles not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create Roles
|
||||
static async createRoles(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await insertRolesDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Roles
|
||||
static async updateRoles(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getRolesByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Roles not found');
|
||||
}
|
||||
|
||||
const result = await updateRolesDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete Roles
|
||||
static async deleteRoles(id, userId) {
|
||||
try {
|
||||
const dataExist = await getRolesByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Roles not found');
|
||||
}
|
||||
|
||||
const result = await deleteRolesDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RolesService;
|
||||
88
services/schedule.service.js
Normal file
88
services/schedule.service.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const {
|
||||
getAllScheduleDb,
|
||||
getScheduleByIdDb,
|
||||
insertScheduleDb,
|
||||
updateScheduleDb,
|
||||
deleteScheduleDb
|
||||
} = require('../db/schedule.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class ScheduleService {
|
||||
// Get all Schedule
|
||||
static async getAllSchedule(param) {
|
||||
try {
|
||||
const results = await getAllScheduleDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get Schedule by ID
|
||||
static async getScheduleById(id) {
|
||||
try {
|
||||
const result = await getScheduleByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Schedule not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create Schedule
|
||||
static async insertScheduleDb(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await insertScheduleDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Schedule
|
||||
static async updateSchedule(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getScheduleByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Schedule not found');
|
||||
}
|
||||
|
||||
const result = await updateScheduleDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete Schedule
|
||||
static async deleteSchedule(id, userId) {
|
||||
try {
|
||||
const dataExist = await getScheduleByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Schedule not found');
|
||||
}
|
||||
|
||||
const result = await deleteScheduleDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ScheduleService;
|
||||
88
services/shift.service.js
Normal file
88
services/shift.service.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const {
|
||||
getAllShiftDb,
|
||||
getShiftByIdDb,
|
||||
insertShiftDb,
|
||||
updateShiftDb,
|
||||
deleteShiftDb
|
||||
} = require('../db/shift.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class ShiftService {
|
||||
// Get all Shift
|
||||
static async getAllShift(param) {
|
||||
try {
|
||||
const results = await getAllShiftDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get Shift by ID
|
||||
static async getShiftById(id) {
|
||||
try {
|
||||
const result = await getShiftByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Shift not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create Shift
|
||||
static async createShift(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await insertShiftDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Shift
|
||||
static async updateShift(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getShiftByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Shift not found');
|
||||
}
|
||||
|
||||
const result = await updateShiftDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete Shift
|
||||
static async deleteShift(id, userId) {
|
||||
try {
|
||||
const dataExist = await getShiftByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Shift not found');
|
||||
}
|
||||
|
||||
const result = await deleteShiftDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ShiftService;
|
||||
92
services/status.service.js
Normal file
92
services/status.service.js
Normal file
@@ -0,0 +1,92 @@
|
||||
const {
|
||||
getAllStatusDb,
|
||||
getStatusByIdDb,
|
||||
createStatusDb,
|
||||
updateStatusDb,
|
||||
deleteStatusDb,
|
||||
checkStatusNumberExistsDb
|
||||
} = require('../db/status.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class StatusService {
|
||||
// Get all status
|
||||
static async getAllStatus(param) {
|
||||
try {
|
||||
const results = await getAllStatusDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get status by ID
|
||||
static async getStatusById(id) {
|
||||
try {
|
||||
const result = await getStatusByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Status not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
static async createStatus(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
if (data.status_number) {
|
||||
const exists = await checkStatusNumberExistsDb(data.status_number);
|
||||
if (exists) throw new ErrorHandler(400, 'Status number already exists');
|
||||
}
|
||||
|
||||
const result = await createStatusDb(data);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update status
|
||||
static async updateStatus(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getStatusByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Status not found');
|
||||
}
|
||||
|
||||
const result = await updateStatusDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete status
|
||||
static async deleteStatus(id, userId) {
|
||||
try {
|
||||
const dataExist = await getStatusByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Status not found');
|
||||
}
|
||||
|
||||
const result = await deleteStatusDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StatusService;
|
||||
87
services/sub_section.service.js
Normal file
87
services/sub_section.service.js
Normal file
@@ -0,0 +1,87 @@
|
||||
const {
|
||||
getAllSubSectionsDb,
|
||||
getSubSectionByIdDb,
|
||||
createSubSectionDb,
|
||||
updateSubSectionDb,
|
||||
deleteSubSectionDb
|
||||
} = require('../db/sub_section.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class SubSectionService {
|
||||
// Get all sub sections
|
||||
static async getAll(param) {
|
||||
try {
|
||||
const results = await getAllSubSectionsDb(param);
|
||||
|
||||
results.data.map(el => {});
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get sub section by ID
|
||||
static async getById(id) {
|
||||
try {
|
||||
const result = await getSubSectionByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Sub section not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create sub section
|
||||
static async create(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await createSubSectionDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update sub section
|
||||
static async update(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getSubSectionByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Sub section not found');
|
||||
}
|
||||
|
||||
const result = await updateSubSectionDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete sub section
|
||||
static async delete(id, userId) {
|
||||
try {
|
||||
const dataExist = await getSubSectionByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Sub section not found');
|
||||
}
|
||||
|
||||
const result = await deleteSubSectionDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SubSectionService;
|
||||
89
services/tags.service.js
Normal file
89
services/tags.service.js
Normal file
@@ -0,0 +1,89 @@
|
||||
const {
|
||||
getAllTagsDb,
|
||||
getTagsByIdDb,
|
||||
createTagsDb,
|
||||
updateTagsDb,
|
||||
deleteTagsDb
|
||||
} = require('../db/tags.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class TagsService {
|
||||
// Get all devices
|
||||
static async getAllTags(param) {
|
||||
try {
|
||||
const results = await getAllTagsDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get device by ID
|
||||
static async getTagByID(id) {
|
||||
try {
|
||||
const result = await getTagsByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Tags not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create device
|
||||
static async createTags(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await createTagsDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update device
|
||||
static async updateTags(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getTagsByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Tags not found');
|
||||
}
|
||||
|
||||
const result = await updateTagsDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete device
|
||||
static async deleteTags(id, userId) {
|
||||
try {
|
||||
const dataExist = await getTagsByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Tags not found');
|
||||
}
|
||||
|
||||
const result = await deleteTagsDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagsService;
|
||||
|
||||
88
services/unit.service.js
Normal file
88
services/unit.service.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const {
|
||||
getAllUnitsDb,
|
||||
getUnitByIdDb,
|
||||
createUnitDb,
|
||||
updateUnitDb,
|
||||
deleteUnitDb
|
||||
} = require('../db/unit.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class UnitService {
|
||||
// Get all units
|
||||
static async getAllUnits(param) {
|
||||
try {
|
||||
const results = await getAllUnitsDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get unit by ID
|
||||
static async getUnitById(id) {
|
||||
try {
|
||||
const result = await getUnitByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'Unit not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create unit
|
||||
static async createUnit(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await createUnitDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update unit
|
||||
static async updateUnit(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getUnitByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Unit not found');
|
||||
}
|
||||
|
||||
const result = await updateUnitDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete unit
|
||||
static async deleteUnit(id, userId) {
|
||||
try {
|
||||
const dataExist = await getUnitByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Unit not found');
|
||||
}
|
||||
|
||||
const result = await deleteUnitDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UnitService;
|
||||
@@ -1,124 +1,188 @@
|
||||
const {
|
||||
createUserDb,
|
||||
changeUserPasswordDb,
|
||||
getUserByIdDb,
|
||||
updateUserDb,
|
||||
deleteUserDb,
|
||||
getAllUsersDb,
|
||||
getUserByIdDb,
|
||||
getUserByUserEmailDb,
|
||||
getUserByUsernameDb,
|
||||
getAllRoleDb
|
||||
} = require("../db/user.db");
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
const { convertId } = require("../helpers/utils");
|
||||
|
||||
const statusName = [
|
||||
{
|
||||
status: true,
|
||||
status_name: "Aktif"
|
||||
}, {
|
||||
status: false,
|
||||
status_name: "NonAktif"
|
||||
}
|
||||
];
|
||||
createUserDb,
|
||||
updateUserDb,
|
||||
approveUserDb,
|
||||
rejectUserDb,
|
||||
deleteUserDb,
|
||||
changeUserPasswordDb
|
||||
} = require('../db/user.db');
|
||||
const { hashPassword } = require('../helpers/hashPassword');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class UserService {
|
||||
|
||||
getAllStatusUsers = async () => {
|
||||
try {
|
||||
return statusName;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
|
||||
getAllUsers = async (param) => {
|
||||
// Get all users
|
||||
static async getAllUsers(param) {
|
||||
try {
|
||||
const results = await getAllUsersDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
element.is_active = element.is_active == 1 ? true : false
|
||||
element.is_active_name = convertId(statusName, element.is_active, 'status', 'status_name')
|
||||
});
|
||||
|
||||
return results
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
createUser = async (param) => {
|
||||
// Get user by ID
|
||||
static async getUserById(id) {
|
||||
try {
|
||||
const userByUsername = await getUserByUsernameDb(param.userName, param.tenantID);
|
||||
const result = await getUserByIdDb(id);
|
||||
|
||||
if (userByUsername) {
|
||||
throw new ErrorHandler(401, "username taken already");
|
||||
if (!result) throw new ErrorHandler(404, 'User not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create user
|
||||
static async createUser(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const creatorId = data.userId || null;
|
||||
|
||||
// cek duplikasi username & email
|
||||
const [existingUsername, existingEmail] = await Promise.all([
|
||||
getUserByUsernameDb(data.user_name),
|
||||
getUserByUserEmailDb(data.user_email)
|
||||
]);
|
||||
|
||||
if (existingUsername) throw new ErrorHandler(400, 'Username is already taken');
|
||||
if (existingEmail) throw new ErrorHandler(400, 'Email is already taken');
|
||||
|
||||
// hash password
|
||||
const hashedPassword = await hashPassword(data.user_password);
|
||||
|
||||
// siapkan data untuk insert
|
||||
const userData = {
|
||||
...data,
|
||||
user_password: hashedPassword,
|
||||
is_approve: 2,
|
||||
approved_by: creatorId,
|
||||
created_by: creatorId,
|
||||
updated_by: creatorId,
|
||||
is_sa: 0,
|
||||
is_active: 1
|
||||
};
|
||||
|
||||
delete userData.userId;
|
||||
|
||||
const result = await createUserDb(userData);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update user
|
||||
static async updateUser(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const existingEmail = await getUserByUserEmailDb(data.user_email);
|
||||
const existingUsername = await getUserByUsernameDb(data.user_name);
|
||||
|
||||
if (existingUsername) {
|
||||
throw new ErrorHandler(400, 'Username is already taken');
|
||||
}
|
||||
if (existingEmail) {
|
||||
throw new ErrorHandler(400, 'Email is already taken')
|
||||
}
|
||||
|
||||
return await createUserDb(param);
|
||||
const userExist = await getUserByIdDb(id);
|
||||
if (!userExist) throw new ErrorHandler(404, 'User not found');
|
||||
|
||||
const result = await updateUserDb(id, data);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getUserById = async (id) => {
|
||||
// Approve user
|
||||
static async approveUser(userId, approverId) {
|
||||
try {
|
||||
const user = await getUserByIdDb(id);
|
||||
// user.password = undefined;
|
||||
user.is_active = user.is_active == 1 ? true : false
|
||||
return user;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
|
||||
changeUserPassword = async (password, email, tenantID) => {
|
||||
try {
|
||||
return await changeUserPasswordDb(password, email, tenantID);
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
|
||||
updateUser = async (param) => {
|
||||
const { userName, id } = param;
|
||||
const errors = {};
|
||||
try {
|
||||
|
||||
const user = await getUserByIdDb(id);
|
||||
|
||||
const findUserByUsername = await getUserByUsernameDb(userName, param.tenantID);
|
||||
|
||||
const usernameChanged = userName && user.user_name.toLowerCase() !== userName.toLowerCase();
|
||||
|
||||
if (usernameChanged && typeof findUserByUsername === "object") {
|
||||
errors["username"] = "Username is already taken";
|
||||
if (!userId) {
|
||||
throw new ErrorHandler(400, 'User ID is required');
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
throw new ErrorHandler(403, errors);
|
||||
const existingUser = await getUserByIdDb(userId);
|
||||
if (!existingUser) {
|
||||
throw new ErrorHandler(404, 'User not found');
|
||||
}
|
||||
|
||||
return await updateUserDb(param);
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
if (existingUser.is_approve === 2) {
|
||||
throw new ErrorHandler(400, 'User is already approved');
|
||||
}
|
||||
|
||||
deleteUser = async (id, userID) => {
|
||||
try {
|
||||
return await deleteUserDb(id, userID);
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
if (existingUser.is_approve === 0) {
|
||||
throw new ErrorHandler(400, 'User is already rejected');
|
||||
}
|
||||
|
||||
getAllRoles = async (tenantID) => {
|
||||
const updatedUser = await approveUserDb(userId, approverId);
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Reject user
|
||||
static async rejectUser(userId, approverId) {
|
||||
try {
|
||||
return await getAllRoleDb(tenantID);
|
||||
if (!userId) {
|
||||
throw new ErrorHandler(400, 'User ID is required');
|
||||
}
|
||||
|
||||
const existingUser = await getUserByIdDb(userId);
|
||||
if (!existingUser) {
|
||||
throw new ErrorHandler(404, 'User not found');
|
||||
}
|
||||
|
||||
if (existingUser.is_approve === 2) {
|
||||
throw new ErrorHandler(400, 'User is already approved');
|
||||
}
|
||||
|
||||
if (existingUser.is_approve === 0) {
|
||||
throw new ErrorHandler(400, 'User is already rejected');
|
||||
}
|
||||
|
||||
const updatedUser = await rejectUserDb(userId, approverId);
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete user
|
||||
static async deleteUser(id, userId) {
|
||||
try {
|
||||
const userExist = await getUserByIdDb(id);
|
||||
if (!userExist) throw new ErrorHandler(404, 'User not found');
|
||||
|
||||
const result = await deleteUserDb(id, userId);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Change password
|
||||
static async changeUserPassword(id, newPassword) {
|
||||
try {
|
||||
const userExist = await getUserByIdDb(id);
|
||||
if (!userExist) throw new ErrorHandler(404, 'User not found');
|
||||
|
||||
const hashedPassword = await hashPassword(newPassword);
|
||||
const result = await changeUserPasswordDb(id, hashedPassword);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new UserService();
|
||||
module.exports = UserService;
|
||||
|
||||
8
utils/captcha.js
Normal file
8
utils/captcha.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const svgCaptcha = require('svg-captcha');
|
||||
|
||||
function createCaptcha() {
|
||||
const captcha = svgCaptcha.create({ size: 5, noise: 7, color: true });
|
||||
return { svg: captcha.data, text: captcha.text };
|
||||
}
|
||||
|
||||
module.exports = { createCaptcha };
|
||||
8
utils/date.js
Normal file
8
utils/date.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
formattedDate: (timestamp) => {
|
||||
let date = new Date(timestamp);
|
||||
let options = { day: "numeric", month: "long", year: "numeric" };
|
||||
let formattedDate = date.toISOString("id-ID", options);
|
||||
return formattedDate;
|
||||
},
|
||||
};
|
||||
73
utils/jwt.js
Normal file
73
utils/jwt.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const tokenSettings = {
|
||||
access: {
|
||||
expiresIn: '15m',
|
||||
type: 'access',
|
||||
secret: process.env.SECRET
|
||||
},
|
||||
refresh: {
|
||||
expiresIn: '7d',
|
||||
type: 'refresh',
|
||||
secret: process.env.REFRESH_SECRET
|
||||
}
|
||||
};
|
||||
|
||||
function generateTokenId() {
|
||||
return crypto.randomBytes(32).toString('hex');
|
||||
}
|
||||
|
||||
function generateToken(payload, type) {
|
||||
const settings = tokenSettings[type];
|
||||
if (!settings) throw new Error(`Invalid token type: ${type}`);
|
||||
|
||||
const tokenPayload = { ...payload, type: settings.type };
|
||||
|
||||
return jwt.sign(tokenPayload, settings.secret, {
|
||||
expiresIn: settings.expiresIn,
|
||||
jwtid: generateTokenId()
|
||||
});
|
||||
}
|
||||
|
||||
function verifyTokenType(token, type) {
|
||||
const settings = tokenSettings[type];
|
||||
const decoded = jwt.verify(token, settings.secret);
|
||||
if (decoded.type !== type) throw new Error('Invalid token type');
|
||||
return decoded;
|
||||
}
|
||||
|
||||
function generateAccessToken(payload) {
|
||||
return generateToken(payload, 'access');
|
||||
}
|
||||
|
||||
function generateRefreshToken(payload) {
|
||||
return generateToken(payload, 'refresh');
|
||||
}
|
||||
|
||||
function verifyToken(token) {
|
||||
return verifyTokenType(token, 'access');
|
||||
}
|
||||
|
||||
function verifyRefreshToken(token) {
|
||||
return verifyTokenType(token, 'refresh');
|
||||
}
|
||||
|
||||
function generateTokenPair(payload) {
|
||||
const accessToken = generateAccessToken(payload);
|
||||
const refreshToken = generateRefreshToken(payload);
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
tokenType: 'Bearer',
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateAccessToken,
|
||||
generateRefreshToken,
|
||||
verifyToken,
|
||||
verifyRefreshToken,
|
||||
generateTokenPair,
|
||||
};
|
||||
40
validate/auth.schema.js
Normal file
40
validate/auth.schema.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
// Auth Validation
|
||||
// ========================
|
||||
const registerSchema = Joi.object({
|
||||
user_fullname: Joi.string().min(3).max(100).required(),
|
||||
user_name: Joi.string().alphanum().min(3).max(50).required(),
|
||||
user_email: Joi.string().email().required(),
|
||||
user_phone: Joi.string()
|
||||
.pattern(/^(?:\+62|0)8\d{7,10}$/)
|
||||
.required()
|
||||
.messages({
|
||||
'string.pattern.base':
|
||||
'Phone number must be a valid Indonesian number in format +628XXXXXXXXX'
|
||||
}),
|
||||
user_password: Joi.string()
|
||||
.min(8)
|
||||
.pattern(/[A-Z]/, 'uppercase letter')
|
||||
.pattern(/[a-z]/, 'lowercase letter')
|
||||
.pattern(/\d/, 'number')
|
||||
.pattern(/[!@#$%^&*(),.?":{}|<>]/, 'special character')
|
||||
.required()
|
||||
.messages({
|
||||
'string.min': 'Password must be at least 8 characters long',
|
||||
'string.pattern.name': 'Password must contain at least one {#name}'
|
||||
})
|
||||
});
|
||||
|
||||
const loginSchema = Joi.object({
|
||||
identifier: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
captcha: Joi.string().required(),
|
||||
captchaText: Joi.string().required()
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
registerSchema,
|
||||
loginSchema,
|
||||
};
|
||||
36
validate/device.schema.js
Normal file
36
validate/device.schema.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// ========================
|
||||
// Device Validation
|
||||
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
const insertDeviceSchema = Joi.object({
|
||||
device_name: Joi.string().max(100).required(),
|
||||
is_active: Joi.boolean().required(),
|
||||
device_location: Joi.string().max(100).required(),
|
||||
device_description: Joi.string().required(),
|
||||
ip_address: Joi.string()
|
||||
.ip({ version: ['ipv4', 'ipv6'] })
|
||||
.required()
|
||||
.messages({
|
||||
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
||||
})
|
||||
});
|
||||
|
||||
const updateDeviceSchema = Joi.object({
|
||||
device_name: Joi.string().max(100),
|
||||
is_active: Joi.boolean(),
|
||||
device_location: Joi.string().max(100),
|
||||
device_description: Joi.string(),
|
||||
ip_address: Joi.string()
|
||||
.ip({ version: ['ipv4', 'ipv6'] })
|
||||
.messages({
|
||||
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
||||
})
|
||||
}).min(1);
|
||||
|
||||
|
||||
// ✅ Export dengan CommonJS
|
||||
module.exports = {
|
||||
insertDeviceSchema, updateDeviceSchema
|
||||
};
|
||||
25
validate/roles.schema.js
Normal file
25
validate/roles.schema.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// ========================
|
||||
// Device Validation
|
||||
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
const insertRolesSchema = Joi.object({
|
||||
role_name: Joi.string().max(100).required(),
|
||||
role_level: Joi.number().required(),
|
||||
role_description: Joi.string().max(100).required(),
|
||||
is_active: Joi.boolean().required(),
|
||||
});
|
||||
|
||||
const updateRolesSchema = Joi.object({
|
||||
role_name: Joi.string().max(100).optional(),
|
||||
role_level: Joi.number().optional(),
|
||||
role_description: Joi.string().max(100).optional(),
|
||||
is_active: Joi.boolean().optional()
|
||||
}).min(1);
|
||||
|
||||
|
||||
// ✅ Export dengan CommonJS
|
||||
module.exports = {
|
||||
insertRolesSchema, updateRolesSchema
|
||||
};
|
||||
29
validate/schedule.schema.js
Normal file
29
validate/schedule.schema.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// ========================
|
||||
// Schedule Validation
|
||||
|
||||
const Joi = require("joi");
|
||||
|
||||
const datePattern = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
|
||||
|
||||
const insertScheduleSchema = Joi.object({
|
||||
schedule_date: Joi.string().pattern(datePattern).required().messages({
|
||||
"string.pattern.base": "schedule_date harus dalam format YYYY-MM-DD",
|
||||
"any.required": "schedule_date wajib diisi",
|
||||
}),
|
||||
is_active: Joi.boolean().required(),
|
||||
shift_id: Joi.number(),
|
||||
next_day: Joi.number().required(),
|
||||
});
|
||||
|
||||
const updateScheduleSchema = Joi.object({
|
||||
schedule_date: Joi.string().pattern(datePattern).messages({
|
||||
"string.pattern.base": "schedule_date harus dalam format YYYY-MM-DD",
|
||||
}),
|
||||
is_active: Joi.boolean(),
|
||||
shift_id: Joi.number(),
|
||||
}).min(1);
|
||||
|
||||
module.exports = {
|
||||
insertScheduleSchema,
|
||||
updateScheduleSchema,
|
||||
};
|
||||
44
validate/shift.schema.js
Normal file
44
validate/shift.schema.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// ========================
|
||||
// Device Validation
|
||||
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
const timePattern = /^([01]\d|2[0-3]):([0-5]\d)(:[0-5]\d)?$/;
|
||||
|
||||
const insertShiftSchema = Joi.object({
|
||||
shift_name: Joi.string().max(100).required(),
|
||||
is_active:Joi.boolean().required(),
|
||||
start_time: Joi.string()
|
||||
.pattern(timePattern)
|
||||
.required()
|
||||
.messages({
|
||||
"string.pattern.base": "start_time harus dalam format HH:mm atau HH:mm:ss",
|
||||
}),
|
||||
end_time: Joi.string()
|
||||
.pattern(timePattern)
|
||||
.required()
|
||||
.messages({
|
||||
"string.pattern.base": "end_time harus dalam format HH:mm atau HH:mm:ss",
|
||||
}),
|
||||
});
|
||||
|
||||
const updateShiftSchema = Joi.object({
|
||||
shift_name: Joi.string().max(100).optional(),
|
||||
is_active:Joi.boolean().optional(),
|
||||
start_time: Joi.string()
|
||||
.pattern(timePattern)
|
||||
.messages({
|
||||
"string.pattern.base": "start_time harus dalam format HH:mm atau HH:mm:ss",
|
||||
}).optional(),
|
||||
end_time: Joi.string()
|
||||
.pattern(timePattern)
|
||||
.messages({
|
||||
"string.pattern.base": "end_time harus dalam format HH:mm atau HH:mm:ss",
|
||||
}),
|
||||
}).min(1);
|
||||
|
||||
module.exports = {
|
||||
insertShiftSchema,
|
||||
updateShiftSchema,
|
||||
};
|
||||
26
validate/status.schema.js
Normal file
26
validate/status.schema.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
// Status Validation
|
||||
// ========================
|
||||
const insertStatusSchema = Joi.object({
|
||||
status_number: Joi.number().integer().required(),
|
||||
status_name: Joi.string().max(200).required(),
|
||||
status_color: Joi.string().max(200).required(),
|
||||
status_description: Joi.string().allow('', null),
|
||||
is_active: Joi.boolean().required(),
|
||||
});
|
||||
|
||||
const updateStatusSchema = Joi.object({
|
||||
status_number: Joi.number().integer().optional(),
|
||||
status_name: Joi.string().max(200).optional(),
|
||||
status_color: Joi.string().max(200).optional(),
|
||||
status_description: Joi.string().allow('', null).optional(),
|
||||
is_active: Joi.boolean().optional()
|
||||
}).min(1);
|
||||
|
||||
// ✅ Export dengan CommonJS
|
||||
module.exports = {
|
||||
insertStatusSchema,
|
||||
updateStatusSchema
|
||||
};
|
||||
33
validate/sub_section.schema.js
Normal file
33
validate/sub_section.schema.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
// Plant Sub Section Validation
|
||||
// ========================
|
||||
const insertSubSectionSchema = Joi.object({
|
||||
sub_section_name: Joi.string()
|
||||
.max(200)
|
||||
.required()
|
||||
.messages({
|
||||
"string.base": "Sub section name must be a string",
|
||||
"string.max": "Sub section name cannot exceed 200 characters",
|
||||
"any.required": "Sub section name is required"
|
||||
}).required(),
|
||||
is_active: Joi.boolean().required(),
|
||||
});
|
||||
|
||||
const updateSubSectionSchema = Joi.object({
|
||||
sub_section_name: Joi.string()
|
||||
.max(200)
|
||||
.messages({
|
||||
"string.base": "Sub section name must be a string",
|
||||
"string.max": "Sub section name cannot exceed 200 characters",
|
||||
}).optional(),
|
||||
is_active: Joi.boolean().optional(),
|
||||
}).min(1).messages({
|
||||
"object.min": "At least one field must be provided to update",
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
insertSubSectionSchema,
|
||||
updateSubSectionSchema
|
||||
};
|
||||
33
validate/tags.schema.js
Normal file
33
validate/tags.schema.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// ========================
|
||||
// Device Validation
|
||||
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
const insertTagsSchema = Joi.object({
|
||||
device_id: Joi.number().optional(),
|
||||
tag_name: Joi.string().max(200).required(),
|
||||
tag_number: Joi.number().required(),
|
||||
is_active: Joi.boolean().required(),
|
||||
data_type: Joi.string().max(50).required(),
|
||||
unit: Joi.string().max(50).required(),
|
||||
sub_section_id: Joi.number().optional(),
|
||||
is_alarm: Joi.boolean().required()
|
||||
});
|
||||
|
||||
const updateTagsSchema = Joi.object({
|
||||
device_id: Joi.number(),
|
||||
tag_name: Joi.string().max(200),
|
||||
tag_number: Joi.number(),
|
||||
is_active: Joi.boolean(),
|
||||
data_type: Joi.string().max(50),
|
||||
unit: Joi.string().max(50),
|
||||
is_alarm: Joi.boolean().optional(),
|
||||
sub_section_id: Joi.number().optional(),
|
||||
}).min(1);
|
||||
|
||||
// ✅ Export dengan CommonJS
|
||||
module.exports = {
|
||||
insertTagsSchema,
|
||||
updateTagsSchema,
|
||||
};
|
||||
21
validate/unit.schema.js
Normal file
21
validate/unit.schema.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
// Unit Validation
|
||||
// ========================
|
||||
const insertUnitSchema = Joi.object({
|
||||
unit_name: Joi.string().max(100).required(),
|
||||
tag_id: Joi.number().integer().optional(),
|
||||
is_active: Joi.boolean().required(),
|
||||
});
|
||||
|
||||
const updateUnitSchema = Joi.object({
|
||||
unit_name: Joi.string().max(100).optional(),
|
||||
tag_id: Joi.number().integer().optional(),
|
||||
is_active: Joi.boolean().optional()
|
||||
}).min(1);
|
||||
|
||||
module.exports = {
|
||||
insertUnitSchema,
|
||||
updateUnitSchema
|
||||
};
|
||||
61
validate/user.schema.js
Normal file
61
validate/user.schema.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
// Users Validation
|
||||
// ========================
|
||||
const userSchema = Joi.object({
|
||||
user_fullname: Joi.string().min(3).max(100).required(),
|
||||
user_name: Joi.string().alphanum().min(3).max(50).required(),
|
||||
user_email: Joi.string().email().required(),
|
||||
user_phone: Joi.string()
|
||||
.pattern(/^(?:\+62|0)8\d{7,10}$/)
|
||||
.required()
|
||||
.messages({
|
||||
'string.pattern.base':
|
||||
'Phone number must be a valid Indonesian number in format +628XXXXXXXXX'
|
||||
}),
|
||||
user_password: Joi.string()
|
||||
.min(8)
|
||||
.pattern(/[A-Z]/, 'uppercase letter')
|
||||
.pattern(/[a-z]/, 'lowercase letter')
|
||||
.pattern(/\d/, 'number')
|
||||
.pattern(/[!@#$%^&*(),.?":{}|<>]/, 'special character')
|
||||
.required()
|
||||
.messages({
|
||||
'string.min': 'Password must be at least 8 characters long',
|
||||
'string.pattern.name': 'Password must contain at least one {#name}'
|
||||
}),
|
||||
role_id: Joi.number().integer().min(1)
|
||||
});
|
||||
|
||||
const updateUserSchema = Joi.object({
|
||||
user_fullname: Joi.string().min(3).max(100).optional(),
|
||||
user_name: Joi.string().alphanum().min(3).max(50).optional(),
|
||||
user_email: Joi.string().email().optional(),
|
||||
user_phone: Joi.string()
|
||||
.pattern(/^(?:\+62|0)8\d{7,10}$/)
|
||||
.message('Phone number must be a valid Indonesian number in format +628XXXXXXXXX')
|
||||
.optional(),
|
||||
role_id: Joi.number().integer().min(1).optional(),
|
||||
is_active: Joi.boolean().optional()
|
||||
}).min(1);
|
||||
|
||||
const newPasswordSchema = Joi.object({
|
||||
new_password: Joi.string()
|
||||
.min(8)
|
||||
.pattern(/[A-Z]/, 'uppercase letter')
|
||||
.pattern(/[a-z]/, 'lowercase letter')
|
||||
.pattern(/\d/, 'number')
|
||||
.pattern(/[!@#$%^&*(),.?":{}|<>]/, 'special character')
|
||||
.required()
|
||||
.messages({
|
||||
'string.min': 'Password must be at least 8 characters long',
|
||||
'string.pattern.name': 'Password must contain at least one {#name}'
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
userSchema,
|
||||
newPasswordSchema,
|
||||
updateUserSchema
|
||||
};
|
||||
Reference in New Issue
Block a user