Add dashboard MCU user controller and activity log table

This commit is contained in:
sas.fajri
2026-05-07 16:02:52 +07:00
parent 3cde1b3cdb
commit 1957038c10
2 changed files with 464 additions and 0 deletions

View File

@@ -0,0 +1,447 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class User extends MY_Controller
{
private $db_dashboard;
private $db_log;
public function __construct()
{
parent::__construct();
$this->db_dashboard = $this->load->database("cpone_dashboard", true);
$this->db_log = $this->load->database("log", true);
}
public function save()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$username = $this->sanitize_username($this->get_input('username'));
$password = (string) $this->get_input('password');
$displayName = $this->sanitize_text($this->get_input('display_name'), 100);
if ($username === '') {
$this->sys_error("username wajib diisi");
return;
}
if (!$this->is_valid_password_policy($password)) {
$this->sys_error("password harus minimal 8 karakter dan mengandung huruf besar, huruf kecil, angka, serta simbol");
return;
}
$query = $this->db_dashboard->query(
"CALL sp_insert_dashboard_user(?, ?, ?)",
array($username, $password, $displayName)
);
$this->clean_mysqli_connection($this->db_dashboard->conn_id);
if (!$query) {
$error = $this->db_dashboard->error();
$this->log_activity("SAVE_USER", $username, $this->sys_input, "N", $error['message']);
$this->sys_error_db("gagal insert dashboard user", $this->db_dashboard);
return;
}
$this->log_activity("SAVE_USER", $username, $this->sys_input, "Y", "success");
$this->sys_ok(array(
"message" => "user berhasil disimpan",
"username" => $username
));
}
public function reset_password()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$username = $this->sanitize_username($this->get_input('username'));
$newPassword = (string) $this->get_input('password');
if ($username === '') {
$this->sys_error("username wajib diisi");
return;
}
if (!$this->is_valid_password_policy($newPassword)) {
$this->sys_error("password harus minimal 8 karakter dan mengandung huruf besar, huruf kecil, angka, serta simbol");
return;
}
$query = $this->db_dashboard->query(
"CALL sp_reset_dashboard_user_password(?, ?)",
array($username, $newPassword)
);
$this->clean_mysqli_connection($this->db_dashboard->conn_id);
if (!$query) {
$error = $this->db_dashboard->error();
$this->log_activity("RESET_PASSWORD", $username, $this->sys_input, "N", $error['message']);
$this->sys_error_db("gagal reset password", $this->db_dashboard);
return;
}
$this->log_activity("RESET_PASSWORD", $username, $this->sys_input, "Y", "success");
$this->sys_ok(array(
"message" => "password berhasil direset",
"username" => $username
));
}
public function assign_project()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$username = $this->sanitize_username($this->get_input('username'));
$mcuID = intval($this->get_input('mcu_id'));
if ($username === '') {
$this->sys_error("username wajib diisi");
return;
}
if ($mcuID <= 0) {
$this->sys_error("mcu_id tidak valid");
return;
}
$query = $this->db_dashboard->query(
"CALL sp_assign_user_project(?, ?)",
array($username, $mcuID)
);
$this->clean_mysqli_connection($this->db_dashboard->conn_id);
if (!$query) {
$error = $this->db_dashboard->error();
$this->log_activity("ASSIGN_PROJECT", $username, $this->sys_input, "N", $error['message']);
$this->sys_error_db("gagal assign project", $this->db_dashboard);
return;
}
$this->log_activity("ASSIGN_PROJECT", $username, $this->sys_input, "Y", "success");
$this->sys_ok(array(
"message" => "project berhasil di-assign",
"username" => $username,
"mcu_id" => $mcuID
));
}
public function remove_project()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$username = $this->sanitize_username($this->get_input('username'));
$mcuID = intval($this->get_input('mcu_id'));
if ($username === '') {
$this->sys_error("username wajib diisi");
return;
}
if ($mcuID <= 0) {
$this->sys_error("mcu_id tidak valid");
return;
}
$query = $this->db_dashboard->query(
"CALL sp_remove_user_project(?, ?)",
array($username, $mcuID)
);
$this->clean_mysqli_connection($this->db_dashboard->conn_id);
if (!$query) {
$error = $this->db_dashboard->error();
$this->log_activity("REMOVE_PROJECT", $username, $this->sys_input, "N", $error['message']);
$this->sys_error_db("gagal remove project", $this->db_dashboard);
return;
}
$this->log_activity("REMOVE_PROJECT", $username, $this->sys_input, "Y", "success");
$this->sys_ok(array(
"message" => "project berhasil dihapus dari user",
"username" => $username,
"mcu_id" => $mcuID
));
}
public function search_project()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$keyword = $this->sanitize_text($this->get_input('search'), 100);
$keywordLike = "%" . $keyword . "%";
$sql = "SELECT
Mgm_McuID AS mcu_id,
Mgm_McuNumber AS project_number,
Mgm_McuLabel AS project_name
FROM cpone.mgm_mcu
WHERE Mgm_McuIsActive = 'Y'
AND (
Mgm_McuNumber LIKE ?
OR Mgm_McuLabel LIKE ?
)
ORDER BY Mgm_McuID DESC
LIMIT 20";
$query = $this->db_dashboard->query($sql, array($keywordLike, $keywordLike));
if (!$query) {
$this->sys_error_db("gagal search project", $this->db_dashboard);
return;
}
$this->sys_ok(array(
"records" => $query->result_array()
));
}
public function search()
{
if (!$this->isLogin) {
$this->sys_error("Invalid Token");
return;
}
$username = $this->sanitize_text($this->get_input('username'), 50);
$project = $this->sanitize_text($this->get_input('project'), 20);
$page = intval($this->get_input('page'));
$limit = intval($this->get_input('limit'));
if ($page <= 0) {
$page = 1;
}
if ($limit <= 0) {
$limit = 20;
}
if ($limit > 100) {
$limit = 100;
}
$offset = ($page - 1) * $limit;
$usernameLike = "%" . $username . "%";
$projectID = 0;
if ($project !== '' && strtolower($project) !== 'all') {
$projectID = intval($project);
}
$whereProject = "";
$paramsCount = array($usernameLike);
$paramsData = array($usernameLike);
if ($projectID > 0) {
$whereProject = " AND up.UserProj_McuID = ? ";
$paramsCount[] = $projectID;
$paramsData[] = $projectID;
}
$sqlCount = "SELECT COUNT(DISTINCT u.User_ID) AS total_rows
FROM cpone_dashboard.dashboard_user u
LEFT JOIN cpone_dashboard.dashboard_user_project up
ON up.UserProj_UserID = u.User_ID
AND up.UserProj_IsActive = 'Y'
WHERE u.User_IsActive = 'Y'
AND u.User_Username LIKE ? " . $whereProject;
$countQuery = $this->db_dashboard->query($sqlCount, $paramsCount);
if (!$countQuery) {
$this->sys_error_db("gagal hitung data user", $this->db_dashboard);
return;
}
$totalRows = intval($countQuery->row()->total_rows);
$totalPages = $totalRows > 0 ? intval(ceil($totalRows / $limit)) : 0;
$sqlData = "SELECT
u.User_ID,
u.User_Username,
u.User_DisplayName,
u.User_IsActive,
u.User_CreatedAt,
u.User_UpdatedAt,
GROUP_CONCAT(
DISTINCT CONCAT(
up.UserProj_McuID, '|',
IFNULL(m.Mgm_McuNumber, ''), '|',
IFNULL(m.Mgm_McuLabel, '')
)
ORDER BY up.UserProj_McuID DESC
SEPARATOR '||'
) AS project_list
FROM cpone_dashboard.dashboard_user u
LEFT JOIN cpone_dashboard.dashboard_user_project up
ON up.UserProj_UserID = u.User_ID
AND up.UserProj_IsActive = 'Y'
LEFT JOIN cpone.mgm_mcu m
ON m.Mgm_McuID = up.UserProj_McuID
WHERE u.User_IsActive = 'Y'
AND u.User_Username LIKE ? " . $whereProject . "
GROUP BY
u.User_ID,
u.User_Username,
u.User_DisplayName,
u.User_IsActive,
u.User_CreatedAt,
u.User_UpdatedAt
ORDER BY u.User_ID DESC
LIMIT ? OFFSET ?";
$paramsData[] = $limit;
$paramsData[] = $offset;
$dataQuery = $this->db_dashboard->query($sqlData, $paramsData);
if (!$dataQuery) {
$this->sys_error_db("gagal ambil data user", $this->db_dashboard);
return;
}
$rows = $dataQuery->result_array();
foreach ($rows as &$row) {
$row['projects'] = array();
if (!empty($row['project_list'])) {
$items = explode("||", $row['project_list']);
foreach ($items as $item) {
$parts = explode("|", $item);
$row['projects'][] = array(
"mcu_id" => isset($parts[0]) ? intval($parts[0]) : 0,
"project_number" => isset($parts[1]) ? $parts[1] : "",
"project_name" => isset($parts[2]) ? $parts[2] : ""
);
}
}
unset($row['project_list']);
}
unset($row);
$this->sys_ok(array(
"pagination" => array(
"page" => $page,
"limit" => $limit,
"total_rows" => $totalRows,
"total_pages" => $totalPages
),
"filters" => array(
"username" => $username,
"project" => $project === '' ? 'all' : $project
),
"records" => $rows
));
}
private function log_activity($action, $target, $payload, $isSuccess, $message)
{
$actorUserID = isset($this->sys_user["M_UserID"]) ? intval($this->sys_user["M_UserID"]) : 0;
$actorUsername = isset($this->sys_user["M_UserUsername"]) ? $this->sys_user["M_UserUsername"] : "";
$actorEmail = isset($this->sys_user["M_UserEmail"]) ? $this->sys_user["M_UserEmail"] : "";
$sql = "INSERT INTO cpone_log.dashboard_user_activity_log (
DashboardUserActivityLogAction,
DashboardUserActivityLogTarget,
DashboardUserActivityLogPayloadJson,
DashboardUserActivityLogIsSuccess,
DashboardUserActivityLogMessage,
DashboardUserActivityLogActorUserID,
DashboardUserActivityLogActorUsername,
DashboardUserActivityLogActorEmail,
DashboardUserActivityLogCreatedAt
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())";
$safePayload = $this->mask_sensitive_payload($payload);
$payloadJson = json_encode($safePayload);
if ($payloadJson === false) {
$payloadJson = "{}";
}
$this->db_log->query($sql, array(
$this->sanitize_text($action, 50),
$this->sanitize_text($target, 100),
$payloadJson,
$isSuccess === "Y" ? "Y" : "N",
$this->sanitize_text($message, 255),
$actorUserID,
$this->sanitize_text($actorUsername, 100),
$this->sanitize_text($actorEmail, 150)
));
}
private function mask_sensitive_payload($payload)
{
if (!is_array($payload)) {
return $payload;
}
$sensitiveKeys = array(
'password',
'new_password',
'old_password',
'pass',
'passwd'
);
foreach ($payload as $key => $value) {
$lowerKey = strtolower((string) $key);
if (in_array($lowerKey, $sensitiveKeys, true)) {
$payload[$key] = hash('sha256', (string) $value);
continue;
}
if (is_array($value)) {
$payload[$key] = $this->mask_sensitive_payload($value);
}
}
return $payload;
}
private function get_input($key)
{
return isset($this->sys_input[$key]) ? $this->sys_input[$key] : "";
}
private function sanitize_username($value)
{
$value = trim((string) $value);
$value = preg_replace('/[^a-zA-Z0-9._-]/', '', $value);
return substr($value, 0, 50);
}
private function sanitize_text($value, $maxLen = 255)
{
$value = trim((string) $value);
$value = strip_tags($value);
if (strlen($value) > $maxLen) {
$value = substr($value, 0, $maxLen);
}
return $value;
}
private function is_valid_password_policy($password)
{
if (!is_string($password)) {
return false;
}
if (strlen($password) < 8) {
return false;
}
$hasUpper = preg_match('/[A-Z]/', $password) === 1;
$hasLower = preg_match('/[a-z]/', $password) === 1;
$hasNumber = preg_match('/[0-9]/', $password) === 1;
$hasSymbol = preg_match('/[^a-zA-Z0-9]/', $password) === 1;
return $hasUpper && $hasLower && $hasNumber && $hasSymbol;
}
}

View File

@@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS `cpone_log`.`dashboard_user_activity_log` (
`DashboardUserActivityLogID` BIGINT NOT NULL AUTO_INCREMENT,
`DashboardUserActivityLogAction` VARCHAR(50) NOT NULL,
`DashboardUserActivityLogTarget` VARCHAR(100) DEFAULT NULL,
`DashboardUserActivityLogPayloadJson` LONGTEXT,
`DashboardUserActivityLogIsSuccess` CHAR(1) NOT NULL DEFAULT 'Y',
`DashboardUserActivityLogMessage` VARCHAR(255) DEFAULT NULL,
`DashboardUserActivityLogActorUserID` INT NOT NULL DEFAULT 0,
`DashboardUserActivityLogActorUsername` VARCHAR(100) DEFAULT NULL,
`DashboardUserActivityLogActorEmail` VARCHAR(150) DEFAULT NULL,
`DashboardUserActivityLogCreatedAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`DashboardUserActivityLogID`),
KEY `idx_dashboard_user_activity_log_created` (`DashboardUserActivityLogCreatedAt`),
KEY `idx_dashboard_user_activity_log_action` (`DashboardUserActivityLogAction`),
KEY `idx_dashboard_user_activity_log_actor` (`DashboardUserActivityLogActorUserID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;