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 remove_user() { if (!$this->isLogin) { $this->sys_error("Invalid Token"); return; } $username = $this->sanitize_username($this->get_input('username')); if ($username === '') { $this->sys_error("username wajib diisi"); return; } $userQuery = $this->db_dashboard->query( "SELECT User_ID FROM cpone_dashboard.dashboard_user WHERE User_Username = ? LIMIT 1", array($username) ); if (!$userQuery || $userQuery->num_rows() === 0) { $this->log_activity("REMOVE_USER", $username, $this->sys_input, "N", "user tidak ditemukan"); $this->sys_error("user tidak ditemukan"); return; } $userID = intval($userQuery->row()->User_ID); $projectRows = $this->db_dashboard->query( "SELECT UserProj_McuID FROM cpone_dashboard.dashboard_user_project WHERE UserProj_UserID = ? AND UserProj_IsActive = 'Y'", array($userID) ); if (!$projectRows) { $this->log_activity("REMOVE_USER", $username, $this->sys_input, "N", "gagal ambil project user"); $this->sys_error_db("gagal ambil project user", $this->db_dashboard); return; } $mcuIDs = array(); foreach ($projectRows->result_array() as $row) { $mcuID = isset($row['UserProj_McuID']) ? intval($row['UserProj_McuID']) : 0; if ($mcuID > 0) { $mcuIDs[] = $mcuID; } } $this->db_dashboard->trans_begin(); foreach ($mcuIDs as $mcuID) { $removeProjectQuery = $this->db_dashboard->query( "CALL sp_remove_user_project(?, ?)", array($username, $mcuID) ); $this->clean_mysqli_connection($this->db_dashboard->conn_id); if (!$removeProjectQuery) { $error = $this->db_dashboard->error(); $this->db_dashboard->trans_rollback(); $this->log_activity("REMOVE_USER", $username, $this->sys_input, "N", $error['message']); $this->sys_error_db("gagal remove project user", $this->db_dashboard); return; } } $updateUserQuery = $this->db_dashboard->query( "UPDATE cpone_dashboard.dashboard_user SET User_IsActive = 'N', User_UpdatedAt = NOW() WHERE User_ID = ?", array($userID) ); if (!$updateUserQuery) { $this->db_dashboard->trans_rollback(); $this->log_activity("REMOVE_USER", $username, $this->sys_input, "N", "gagal nonaktifkan user"); $this->sys_error_db("gagal nonaktifkan user", $this->db_dashboard); return; } if ($this->db_dashboard->trans_status() === FALSE) { $this->db_dashboard->trans_rollback(); $this->log_activity("REMOVE_USER", $username, $this->sys_input, "N", "transaction failed"); $this->sys_error("gagal remove user"); return; } $this->db_dashboard->trans_commit(); $this->log_activity("REMOVE_USER", $username, $this->sys_input, "Y", "success"); $this->sys_ok(array( "message" => "user berhasil dinonaktifkan", "username" => $username, "removed_project_count" => count($mcuIDs) )); } 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; } }