22
.env.example
22
.env.example
@@ -1,18 +1,18 @@
|
|||||||
# # SQL DB Connection Colo
|
# SQL DB Connection Colo
|
||||||
# SQL_HOST=117.102.231.130
|
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_DATABASE=piu
|
||||||
# SQL_USERNAME=sa
|
# SQL_USERNAME=sa
|
||||||
# SQL_PASSWORD=@R3M4niA.
|
# SQL_PASSWORD=piu123
|
||||||
# SQL_PORT=1433
|
|
||||||
|
|
||||||
SQL_HOST=203.153.114.226
|
|
||||||
SQL_PORT=1112
|
|
||||||
SQL_DATABASE=piu
|
|
||||||
SQL_USERNAME=sa
|
|
||||||
SQL_PASSWORD=piu123
|
|
||||||
|
|
||||||
# Application Port - express server listens on this port (default 9000).
|
# 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://203.153.114.226:9529/send
|
||||||
# ENDPOINT_WA=http://localhost:9529/send
|
# ENDPOINT_WA=http://localhost:9529/send
|
||||||
ENDPOINT_FE=http://203.153.114.226:9527
|
ENDPOINT_FE=http://203.153.114.226:9527
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ node_modules
|
|||||||
.vscode
|
.vscode
|
||||||
request.http
|
request.http
|
||||||
*.rest
|
*.rest
|
||||||
|
package-lock.json
|
||||||
20
app.js
20
app.js
@@ -8,6 +8,7 @@ const helmet = require("helmet");
|
|||||||
const compression = require("compression");
|
const compression = require("compression");
|
||||||
const unknownEndpoint = require("./middleware/unKnownEndpoint");
|
const unknownEndpoint = require("./middleware/unKnownEndpoint");
|
||||||
const { handleError } = require("./helpers/error");
|
const { handleError } = require("./helpers/error");
|
||||||
|
const { checkConnection } = require("./config");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@@ -24,6 +25,25 @@ app.use("/api", routes);
|
|||||||
app.get("/", (req, res) =>
|
app.get("/", (req, res) =>
|
||||||
res.send("<h1 style='text-align: center'>HAHALO</h1>")
|
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(unknownEndpoint);
|
||||||
app.use(handleError);
|
app.use(handleError);
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,18 @@ const poolPromise = new sql.ConnectionPool(config)
|
|||||||
process.exit(1);
|
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)
|
* Wrapper query (auto konversi $1 → @p1)
|
||||||
*/
|
*/
|
||||||
@@ -46,6 +58,11 @@ async function query(text, params = []) {
|
|||||||
return request.query(sqlText);
|
return request.query(sqlText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidDate(dateStr) {
|
||||||
|
const d = new Date(dateStr);
|
||||||
|
return !isNaN(d.getTime()); // true kalau valid
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build filter query
|
* Build filter query
|
||||||
*/
|
*/
|
||||||
@@ -71,10 +88,24 @@ function buildFilterQuery(filterQuery = [], fixedParams = []) {
|
|||||||
queryParams.push(f.param ? 1 : 0);
|
queryParams.push(f.param ? 1 : 0);
|
||||||
whereConditions.push(`${f.column} = $${queryParams.length}`);
|
whereConditions.push(`${f.column} = $${queryParams.length}`);
|
||||||
break;
|
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 ")})`
|
? `AND (${orStringConditions.join(" OR ")})`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
return { whereOrConditions: whereClause, whereParam: queryParams };
|
return { whereOrConditions: whereClause, whereParamOr: queryParams };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build dynamic UPDATE
|
* Build dynamic UPDATE
|
||||||
*/
|
*/
|
||||||
function buildDynamicUpdate(table, data, where) {
|
function buildDynamicUpdate(table, data, where) {
|
||||||
|
|
||||||
|
data.updated_by = data.userId
|
||||||
|
delete data.userId;
|
||||||
|
|
||||||
const setParts = [];
|
const setParts = [];
|
||||||
const values = [];
|
const values = [];
|
||||||
let index = 1;
|
let index = 1;
|
||||||
@@ -118,8 +153,8 @@ function buildDynamicUpdate(table, data, where) {
|
|||||||
throw new Error("Tidak ada kolom untuk diupdate");
|
throw new Error("Tidak ada kolom untuk diupdate");
|
||||||
}
|
}
|
||||||
|
|
||||||
// updated_at otomatis pakai GETDATE()
|
// updated_at otomatis pakai CURRENT_TIMESTAMP
|
||||||
setParts.push(`updated_at = GETDATE()`);
|
setParts.push(`updated_at = CURRENT_TIMESTAMP`);
|
||||||
|
|
||||||
const whereParts = [];
|
const whereParts = [];
|
||||||
for (const [key, value] of Object.entries(where)) {
|
for (const [key, value] of Object.entries(where)) {
|
||||||
@@ -140,6 +175,11 @@ function buildDynamicUpdate(table, data, where) {
|
|||||||
* Build dynamic INSERT
|
* Build dynamic INSERT
|
||||||
*/
|
*/
|
||||||
function buildDynamicInsert(table, data) {
|
function buildDynamicInsert(table, data) {
|
||||||
|
|
||||||
|
data.created_by = data.userId
|
||||||
|
data.updated_by = data.userId
|
||||||
|
delete data.userId;
|
||||||
|
|
||||||
const columns = [];
|
const columns = [];
|
||||||
const placeholders = [];
|
const placeholders = [];
|
||||||
const values = [];
|
const values = [];
|
||||||
@@ -159,7 +199,7 @@ function buildDynamicInsert(table, data) {
|
|||||||
|
|
||||||
// created_at & updated_at otomatis
|
// created_at & updated_at otomatis
|
||||||
columns.push("created_at", "updated_at");
|
columns.push("created_at", "updated_at");
|
||||||
placeholders.push("GETDATE()", "GETDATE()");
|
placeholders.push("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP");
|
||||||
|
|
||||||
const query = `
|
const query = `
|
||||||
INSERT INTO ${table} (${columns.join(", ")})
|
INSERT INTO ${table} (${columns.join(", ")})
|
||||||
@@ -195,6 +235,7 @@ async function generateKode(prefix, tableName, columnName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
checkConnection,
|
||||||
query,
|
query,
|
||||||
buildFilterQuery,
|
buildFilterQuery,
|
||||||
buildStringOrIlike,
|
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) => {
|
class AuthController {
|
||||||
const { username, password, role, tenant } = req.body;
|
// Register
|
||||||
const { token, refreshToken, user } = await authService.login(
|
static async register(req, res) {
|
||||||
username,
|
const { error, value } = await checkValidate(registerSchema, req);
|
||||||
password,
|
|
||||||
tenant
|
|
||||||
);
|
|
||||||
|
|
||||||
res.header("auth-token", token);
|
if (error) {
|
||||||
res.cookie("refreshToken", refreshToken, {
|
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||||
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,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
// Format nomor HP Indonesia
|
||||||
loginUser,
|
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 UserService = require("../services/user.service");
|
||||||
const { ErrorHandler } = require("../helpers/error");
|
const { setResponse, setResponsePaging, checkValidate } = require("../helpers/utils");
|
||||||
const { hashPassword } = require("../helpers/hashPassword");
|
const { userSchema, updateUserSchema, newPasswordSchema } = require("../validate/user.schema");
|
||||||
const { setResponse, setPaging, setResponsePaging } = require("../helpers/utils");
|
|
||||||
const Joi = require("joi");
|
|
||||||
|
|
||||||
// Definisikan skema validasi
|
class UserController {
|
||||||
const validateTerm = Joi.object({
|
// Get all users
|
||||||
user_fullname: Joi.string().max(255).required(),
|
static async getAll(req, res) {
|
||||||
user_name: Joi.string().max(255).required(),
|
const queryParams = req.query;
|
||||||
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()
|
|
||||||
});
|
|
||||||
|
|
||||||
const getAllUsers = async (req, res) => {
|
const results = await UserService.getAllUsers(queryParams);
|
||||||
|
const response = await setResponsePaging(queryParams, results, 'Users found');
|
||||||
|
|
||||||
const {
|
res.status(response.statusCode).json(response);
|
||||||
page = 1,
|
}
|
||||||
limit = 10,
|
|
||||||
fullname: userFullname,
|
|
||||||
username: userName,
|
|
||||||
is_active: isActive,
|
|
||||||
criteria,
|
|
||||||
tenantID,
|
|
||||||
} = req.query
|
|
||||||
|
|
||||||
const offset = (page - 1) * limit;
|
// Get user by ID
|
||||||
|
static async getById(req, res) {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
const filterQuery = {
|
const results = await UserService.getUserById(id);
|
||||||
fixed: {
|
const response = await setResponse(results, 'User found');
|
||||||
limit, offset, tenantID
|
|
||||||
},
|
res.status(response.statusCode).json(response);
|
||||||
filterQuery: [
|
}
|
||||||
{
|
|
||||||
type: 'string',
|
// Create user
|
||||||
column: 'user_fullname',
|
static async create(req, res) {
|
||||||
param: userFullname
|
const { error, value } = await checkValidate(userSchema, req);
|
||||||
},
|
|
||||||
{
|
if (error) {
|
||||||
type: 'string',
|
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||||
column: 'user_name',
|
|
||||||
param: userName
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
column: 'is_active',
|
|
||||||
param: isActive
|
|
||||||
}
|
|
||||||
],
|
|
||||||
filterCriteria:
|
|
||||||
{
|
|
||||||
criteria,
|
|
||||||
column: [
|
|
||||||
'user_fullname', 'user_name'
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
// Update user
|
||||||
const response = await setResponsePaging(results.data, results.total, parseInt(limit), parseInt(page))
|
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();
|
value.userId = req.user.user_id;
|
||||||
const response = await setResponse(results)
|
|
||||||
|
|
||||||
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);
|
return res.status(response.statusCode).json(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await userService.createUser({
|
// Reject user
|
||||||
userFullname: req.body.user_fullname,
|
static async reject(req, res) {
|
||||||
userName: req.body.user_name,
|
const { id } = req.params;
|
||||||
userEmail: req.body.user_email,
|
const approverId = req.user.user_id;
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
return res.status(response.statusCode).json(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await userService.updateUser({
|
// Soft delete user
|
||||||
userFullname: req.body.user_fullname,
|
static async delete(req, res) {
|
||||||
userName: req.body.user_name,
|
const { id } = req.params;
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
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) => {
|
// Change password
|
||||||
const { id } = req.params;
|
static async changePassword(req, res) {
|
||||||
const userID = req.userID
|
const { id } = req.params;
|
||||||
|
const { error, value } = await checkValidate(newPasswordSchema, req);
|
||||||
|
|
||||||
const results = await userService.deleteUser(id, userID);
|
if (error) {
|
||||||
const response = await setResponse(results)
|
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) => {
|
res.status(response.statusCode).json(response);
|
||||||
const results = await userService.getAllRoles(req.body.tenantID);
|
}
|
||||||
const response = await setResponse(results)
|
}
|
||||||
|
|
||||||
res.status(response.statusCode).json(response);
|
module.exports = UserController;
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getAllUsers,
|
|
||||||
createUser,
|
|
||||||
getUserById,
|
|
||||||
updateUser,
|
|
||||||
deleteUser,
|
|
||||||
getUserProfile,
|
|
||||||
getAllRoles,
|
|
||||||
getAllStatusUsers
|
|
||||||
};
|
|
||||||
|
|||||||
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 pool = require("../config");
|
||||||
|
|
||||||
const getAllUsersDb = async (param) => {
|
// Get all users
|
||||||
// limit & offset masuk fixed param
|
const getAllUsersDb = async (searchParams = {}) => {
|
||||||
let fixedParams = [param.fixed.limit, param.fixed.offset, param.fixed.tenantID];
|
let queryParams = [];
|
||||||
|
|
||||||
const { whereOrConditions, whereParam } = pool.buildStringOrIlike(
|
// Pagination
|
||||||
param.filterCriteria.column,
|
if (searchParams.limit) {
|
||||||
param.filterCriteria.criteria,
|
const page = Number(searchParams.page ?? 1) - 1;
|
||||||
fixedParams
|
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 = `
|
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||||
SELECT mut.*, mr.role_name, COUNT(*) OVER() AS total
|
|
||||||
FROM m_users mut
|
// Filter
|
||||||
LEFT JOIN system.role_tenant mr ON mr.role_id = mut.role_id
|
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||||
WHERE mut.deleted_at IS NULL AND mut.is_sa != 1 AND mut.tenant_id = $3
|
[
|
||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
{ column: "u.user_fullname", param: searchParams.fullname, type: "string" },
|
||||||
${whereOrConditions ? whereOrConditions : ""}
|
{ column: "u.user_name", param: searchParams.username, type: "string" },
|
||||||
ORDER BY mut.user_id
|
{ column: "u.user_email", param: searchParams.email, type: "string" },
|
||||||
OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY
|
{ 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 result = await pool.query(queryText, queryParams);
|
||||||
const rows = result.recordset;
|
|
||||||
|
|
||||||
const total = rows.length > 0 ? parseInt(rows[0].total, 10) : 0;
|
const total =
|
||||||
return { data: rows, total };
|
result?.recordset.length > 0
|
||||||
};
|
? parseInt(result.recordset[0].total_data, 10)
|
||||||
|
: 0;
|
||||||
const createUserDb = async (param) => {
|
|
||||||
const insertData = {
|
return { data: result.recordset, total };
|
||||||
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];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get user by ID
|
||||||
const getUserByIdDb = async (id) => {
|
const getUserByIdDb = async (id) => {
|
||||||
const query = `
|
const queryText = `
|
||||||
SELECT mut.*
|
SELECT
|
||||||
FROM m_users mut
|
u.user_id, u.user_fullname, u.user_name, u.user_email, u.user_phone,
|
||||||
WHERE mut.user_id = $1
|
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];
|
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 getUserByUsernameDb = async (username) => {
|
||||||
const query = `
|
const queryText = `
|
||||||
SELECT mut.*
|
SELECT
|
||||||
FROM m_users mut
|
u.user_id, u.user_fullname, u.user_name, u.user_email, u.user_phone,
|
||||||
WHERE LOWER(mut.username) = LOWER($1)
|
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];
|
return result.recordset[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserByUserEmailDb = async (userEmail) => {
|
// Create user
|
||||||
const query = `
|
const createUserDb = async (data) => {
|
||||||
SELECT mut.*
|
const { query: queryText, values } = pool.buildDynamicInsert("m_users", data);
|
||||||
FROM m_users mut
|
const result = await pool.query(queryText, values);
|
||||||
WHERE LOWER(mut.user_email) = LOWER($1)
|
const insertedId = result.recordset[0]?.inserted_id;
|
||||||
`;
|
return insertedId ? await getUserByIdDb(insertedId) : null;
|
||||||
const result = await pool.query(query, [userEmail]);
|
|
||||||
return result.recordset[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateUserDb = async (param) => {
|
// Update user
|
||||||
const updateData = {
|
const updateUserDb = async (userId, data) => {
|
||||||
tenant_id: param.tenantID,
|
const { query: queryText, values } = pool.buildDynamicUpdate("m_users", data, {
|
||||||
user_fullname: param.userFullname,
|
user_id: userId,
|
||||||
user_name: param.userName,
|
});
|
||||||
user_email: param.userEmail ?? null,
|
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||||
user_password: param.userPassword,
|
return getUserByIdDb(userId);
|
||||||
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];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteUserDb = async (id, userID) => {
|
// Approve user
|
||||||
const query = `
|
const approveUserDb = async (userId, approverId) => {
|
||||||
UPDATE m_users
|
const queryText = `
|
||||||
SET deleted_at = GETDATE(), deleted_by = $1
|
UPDATE m_users
|
||||||
WHERE user_id = $2;
|
SET
|
||||||
|
is_approve = 2,
|
||||||
SELECT * FROM m_users WHERE user_id = $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]);
|
await pool.query(queryText, [approverId, userId]);
|
||||||
return result.recordset[0];
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeUserPasswordDb = async (hashedPassword, userEmail, tenantId) => {
|
// Reject user
|
||||||
const query = `
|
const rejectUserDb = async (userId, approverId) => {
|
||||||
UPDATE m_users
|
const queryText = `
|
||||||
SET user_password = $1
|
UPDATE m_users
|
||||||
WHERE user_email = $2 AND tenant_id = $3
|
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) => {
|
// Soft delete user
|
||||||
const query = `
|
const deleteUserDb = async (userId, deletedBy) => {
|
||||||
SELECT *
|
const queryText = `
|
||||||
FROM system.role_tenant
|
UPDATE m_users
|
||||||
WHERE deleted_at IS NULL AND tenant_id = $1
|
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]);
|
await pool.query(queryText, [deletedBy, userId]);
|
||||||
return result.recordset;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getAllUsersDb,
|
getAllUsersDb,
|
||||||
getUserByIdDb,
|
getUserByIdDb,
|
||||||
getUserByUserEmailDb,
|
getUserByUserEmailDb,
|
||||||
updateUserDb,
|
|
||||||
createUserDb,
|
|
||||||
deleteUserDb,
|
|
||||||
getUserByUsernameDb,
|
getUserByUsernameDb,
|
||||||
|
createUserDb,
|
||||||
|
updateUserDb,
|
||||||
|
approveUserDb,
|
||||||
|
rejectUserDb,
|
||||||
changeUserPasswordDb,
|
changeUserPasswordDb,
|
||||||
getAllRoleDb,
|
deleteUserDb,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,43 +1,29 @@
|
|||||||
const setResponse = async (data = [], message = "success", statusCode = 200) => {
|
const setResponse = (data = null, message = "success", statusCode = 200) => {
|
||||||
const response = {
|
const total = Array.isArray(data) ? data.length : null;
|
||||||
data,
|
|
||||||
total: data.length,
|
|
||||||
message,
|
|
||||||
statusCode
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = {
|
const response = {
|
||||||
message,
|
message,
|
||||||
statusCode,
|
statusCode,
|
||||||
data,
|
rows: data?.data?.length,
|
||||||
total: data.length,
|
|
||||||
paging: {
|
paging: {
|
||||||
total,
|
current_limit: Number(queryParam.limit ?? 0),
|
||||||
limit,
|
current_page: Number(queryParam.page ?? 0),
|
||||||
page,
|
total_limit: data?.total,
|
||||||
page_total: totalPages
|
total_page: totalPages
|
||||||
}
|
},
|
||||||
}
|
data: data?.data ?? []
|
||||||
|
|
||||||
return response
|
|
||||||
};
|
|
||||||
|
|
||||||
const setPaging = async (total, limit, page) => {
|
|
||||||
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
|
|
||||||
const response = {
|
|
||||||
total,
|
|
||||||
limit,
|
|
||||||
page,
|
|
||||||
page_total: totalPages
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
@@ -86,4 +72,19 @@ function orderByClauseQuery(orderParams) {
|
|||||||
return orderByClause
|
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 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}`));
|
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 JWTService = require('../utils/jwt');
|
||||||
const { ErrorHandler } = require("../helpers/error");
|
const { ErrorHandler } = require('../helpers/error');
|
||||||
|
|
||||||
const verifyToken = (req, res, next) => {
|
function setUser(req, decoded) {
|
||||||
const authHeader = req.header("Authorization");
|
req.user = {
|
||||||
// console.log("authHeader", authHeader)
|
userId: decoded.user_id,
|
||||||
|
fullname: decoded.user_fullname,
|
||||||
// Pastikan header Authorization ada dan berisi token
|
username: decoded.user_name,
|
||||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
email: decoded.user_email,
|
||||||
throw new ErrorHandler(401, "Token missing or invalid");
|
roleId: decoded.role_id,
|
||||||
}
|
roleName: decoded.role_name,
|
||||||
|
is_sa: decoded.is_sa
|
||||||
// Ambil token dari header Authorization
|
};
|
||||||
const token = authHeader.split(" ")[1];
|
}
|
||||||
|
|
||||||
|
function verifyAccessToken(req, res, next) {
|
||||||
try {
|
try {
|
||||||
// const decoded = jwt.decode(token, { complete: true });
|
let token = req.cookies?.accessToken;
|
||||||
// console.log("decoded", decoded)
|
|
||||||
// console.log("==============================")
|
if (!token) {
|
||||||
// console.log("token", token)
|
const authHeader = req.headers.authorization;
|
||||||
// console.log("process.env.SECRET", process.env.SECRET)
|
if (!authHeader || !authHeader.startsWith('Bearer')) {
|
||||||
// // console.log("==============================> ", jwt.verify(token, process.env.SECRET))
|
throw new ErrorHandler(401, 'Access Token is required');
|
||||||
// jwt.verify(token, process.env.SECRET, (err, decoded) => {
|
}
|
||||||
// if (err) {
|
token = authHeader.split(' ')[1];
|
||||||
// console.error('Error verifying token: ==============================>', err.message);
|
}
|
||||||
// } else {
|
|
||||||
// console.log('Decoded payload: ==============================>', decoded);
|
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();
|
next();
|
||||||
} catch (error) {
|
} 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 router = require("express").Router();
|
||||||
const auth = require("./auth");
|
const auth = require("./auth.route");
|
||||||
const users = require("./users");
|
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("/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;
|
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 {
|
const {
|
||||||
|
getUserByUserEmailDb,
|
||||||
|
createUserDb,
|
||||||
getUserByUsernameDb
|
getUserByUsernameDb
|
||||||
} = require("../db/user.db");
|
} = require('../db/user.db');
|
||||||
const { logger } = require("../utils/logger");
|
const { hashPassword, comparePassword } = require('../helpers/hashPassword');
|
||||||
|
const { ErrorHandler } = require('../helpers/error');
|
||||||
|
const JWTService = require('../utils/jwt');
|
||||||
|
|
||||||
class AuthService {
|
class AuthService {
|
||||||
|
// Register
|
||||||
async login(username, password, tenantId) {
|
static async register(data) {
|
||||||
try {
|
try {
|
||||||
// if (!validateUser(username, password)) {
|
const existingEmail = await getUserByUserEmailDb(data.user_email);
|
||||||
// throw new ErrorHandler(403, "Invalid login");
|
const existingUsername = await getUserByUsernameDb(data.user_name);
|
||||||
// }
|
|
||||||
|
|
||||||
const user = await getUserByUsernameDb(username, tenantId);
|
if (existingUsername) {
|
||||||
console.log(user);
|
throw new ErrorHandler(400, 'Username is already taken');
|
||||||
|
}
|
||||||
if (!user) {
|
if (existingEmail) {
|
||||||
throw new ErrorHandler(403, "Username not found.");
|
throw new ErrorHandler(400, 'Email is already taken');
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCorrectPassword = password === user.password
|
const hashedPassword = await hashPassword(data.user_password);
|
||||||
if (!isCorrectPassword) {
|
|
||||||
throw new ErrorHandler(403, "Username or password incorrect.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataToken = {
|
const userId = await createUserDb({
|
||||||
tenant_id: tenantId,
|
user_fullname: data.user_fullname,
|
||||||
user_id: user.user_id,
|
user_name: data.user_name,
|
||||||
username,
|
user_email: data.user_email,
|
||||||
fullname: user.full_name,
|
user_phone: data.user_phone,
|
||||||
role_id: user.role_id
|
user_password: hashedPassword,
|
||||||
}
|
is_sa: 0,
|
||||||
|
is_active: 1,
|
||||||
|
is_approve: 1,
|
||||||
|
});
|
||||||
|
|
||||||
const token = await this.signToken(dataToken);
|
const newUser = {
|
||||||
const refreshToken = await this.signRefreshToken(dataToken);
|
user_id: userId,
|
||||||
|
user_fullname: data.user_fullname,
|
||||||
return {
|
user_name: data.user_name,
|
||||||
token,
|
user_email: data.user_email,
|
||||||
refreshToken,
|
user_phone: data.user_phone
|
||||||
role_id: dataToken.role_id,
|
|
||||||
tenant_id: tenantId,
|
|
||||||
user: {
|
|
||||||
user_id: dataToken.user_id,
|
|
||||||
fullname: dataToken.fullname,
|
|
||||||
username: dataToken.username,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return { user: newUser };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async signToken(data) {
|
// Login
|
||||||
|
static async login(data) {
|
||||||
try {
|
try {
|
||||||
// console.log("signToken process.env.SECRET", process.env.SECRET)
|
const { identifier, password, captcha, captchaText } = data;
|
||||||
return jwt.sign(data, process.env.SECRET, { expiresIn: "23h" });
|
|
||||||
|
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) {
|
} catch (error) {
|
||||||
logger.error(error);
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
throw new ErrorHandler(500, "An error occurred");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async signRefreshToken(data) {
|
// Refresh Token
|
||||||
|
static async refreshToken(refreshToken) {
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
logger.error(error);
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
throw new ErrorHandler(500, 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 {
|
const {
|
||||||
createUserDb,
|
|
||||||
changeUserPasswordDb,
|
|
||||||
getUserByIdDb,
|
|
||||||
updateUserDb,
|
|
||||||
deleteUserDb,
|
|
||||||
getAllUsersDb,
|
getAllUsersDb,
|
||||||
|
getUserByIdDb,
|
||||||
|
getUserByUserEmailDb,
|
||||||
getUserByUsernameDb,
|
getUserByUsernameDb,
|
||||||
getAllRoleDb
|
createUserDb,
|
||||||
} = require("../db/user.db");
|
updateUserDb,
|
||||||
const { ErrorHandler } = require("../helpers/error");
|
approveUserDb,
|
||||||
const { convertId } = require("../helpers/utils");
|
rejectUserDb,
|
||||||
|
deleteUserDb,
|
||||||
const statusName = [
|
changeUserPasswordDb
|
||||||
{
|
} = require('../db/user.db');
|
||||||
status: true,
|
const { hashPassword } = require('../helpers/hashPassword');
|
||||||
status_name: "Aktif"
|
const { ErrorHandler } = require('../helpers/error');
|
||||||
}, {
|
|
||||||
status: false,
|
|
||||||
status_name: "NonAktif"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
class UserService {
|
class UserService {
|
||||||
|
|
||||||
getAllStatusUsers = async () => {
|
// Get all users
|
||||||
try {
|
static async getAllUsers(param) {
|
||||||
return statusName;
|
|
||||||
} catch (error) {
|
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getAllUsers = async (param) => {
|
|
||||||
try {
|
try {
|
||||||
const results = await getAllUsersDb(param);
|
const results = await getAllUsersDb(param);
|
||||||
|
return results;
|
||||||
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
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
createUser = async (param) => {
|
// Get user by ID
|
||||||
|
static async getUserById(id) {
|
||||||
try {
|
try {
|
||||||
const userByUsername = await getUserByUsernameDb(param.userName, param.tenantID);
|
const result = await getUserByIdDb(id);
|
||||||
|
|
||||||
if (userByUsername) {
|
if (!result) throw new ErrorHandler(404, 'User not found');
|
||||||
throw new ErrorHandler(401, "username taken already");
|
|
||||||
|
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) {
|
} catch (error) {
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
getUserById = async (id) => {
|
// Approve user
|
||||||
|
static async approveUser(userId, approverId) {
|
||||||
try {
|
try {
|
||||||
const user = await getUserByIdDb(id);
|
if (!userId) {
|
||||||
// user.password = undefined;
|
throw new ErrorHandler(400, 'User ID is required');
|
||||||
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 (Object.keys(errors).length > 0) {
|
const existingUser = await getUserByIdDb(userId);
|
||||||
throw new ErrorHandler(403, errors);
|
if (!existingUser) {
|
||||||
|
throw new ErrorHandler(404, 'User not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return await updateUserDb(param);
|
if (existingUser.is_approve === 2) {
|
||||||
} catch (error) {
|
throw new ErrorHandler(400, 'User is already approved');
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteUser = async (id, userID) => {
|
if (existingUser.is_approve === 0) {
|
||||||
try {
|
throw new ErrorHandler(400, 'User is already rejected');
|
||||||
return await deleteUserDb(id, userID);
|
}
|
||||||
} catch (error) {
|
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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 {
|
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) {
|
} catch (error) {
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
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