Initial import
This commit is contained in:
343
application/controllers/website/Branch.php
Normal file
343
application/controllers/website/Branch.php
Normal file
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
class Branch extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db_onedev = $this->load->database("onedev", true);
|
||||
$this->db_smartone = $this->load->database("onedev", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API handler untuk mendapatkan semua data harga
|
||||
* Implementasi dengan fitur keamanan yang baik
|
||||
*
|
||||
* @return void Output JSON response
|
||||
*/
|
||||
|
||||
function get_branches() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'branches:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT SELECT M_BranchCode as branch_code,
|
||||
M_BranchCodeLab as branch_code_lab,
|
||||
M_BranchName as branch_name,
|
||||
'' as branch_address,
|
||||
M_BranchLat as branch_latitude,
|
||||
M_BranchLong as branch_longitude
|
||||
FROM m_branch
|
||||
WHERE BranchIsActive = 'Y' AND M_BranchIsOuter = 'N'
|
||||
ORDER BY BranchCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_BRANCHES', 'branch/get_branches', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function insert_log_error($sql, $params){
|
||||
$sql = "INSERT INTO error_log_website(
|
||||
ErrorLogWebsiteCode,
|
||||
ErrorLogWebsiteFnName,
|
||||
ErrorLogWebsiteDescription,
|
||||
ErrorLogWebsiteCreated
|
||||
)
|
||||
VALUES(
|
||||
?,?,?,NOW()
|
||||
)";
|
||||
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan API key dari header Authorization
|
||||
*
|
||||
* @return string|null API key atau null jika tidak ada
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
$headers = null;
|
||||
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER['Authorization']);
|
||||
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$headers = trim($_SERVER['HTTP_AUTHORIZATION']);
|
||||
} else if (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
$requestHeaders = array_combine(
|
||||
array_map('ucwords', array_keys($requestHeaders)),
|
||||
array_values($requestHeaders)
|
||||
);
|
||||
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
|
||||
// Format header biasanya "Bearer {token}"
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi API key
|
||||
*
|
||||
* @param string $apiKey API key untuk divalidasi
|
||||
* @return bool True jika valid, false jika tidak
|
||||
*/
|
||||
private function validateApiKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contoh implementasi validasi:
|
||||
// 1. Cek token di database
|
||||
// 2. Validasi JWT token
|
||||
// 3. Cek api key statis (hanya untuk contoh sederhana)
|
||||
|
||||
try {
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1 AND expired_at > NOW()";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitasi input untuk mencegah SQL injection dan XSS
|
||||
*
|
||||
* @param string $input Input yang akan disanitasi
|
||||
* @return string Input yang sudah disanitasi
|
||||
*/
|
||||
private function sanitizeInput($input) {
|
||||
if (is_string($input)) {
|
||||
// Hapus karakter tidak perlu
|
||||
$input = trim($input);
|
||||
|
||||
// Hilangkan HTML tags
|
||||
$input = strip_tags($input);
|
||||
|
||||
// Konversi special characters ke HTML entities
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim HTTP response dengan JSON
|
||||
*
|
||||
* @param int $statusCode HTTP status code
|
||||
* @param array $data Data yang akan dikirim sebagai JSON
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($statusCode, $data) {
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Tambahkan header keamanan
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header('Content-Security-Policy: default-src \'self\'');
|
||||
|
||||
// Set cache control untuk kontrol caching
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
// Konversi data ke JSON dan kirim
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fungsi untuk menghasilkan API key baru
|
||||
function generateApiKey($userId, $name, $description, $rateLimit = 100, $expiryDays = 365) {
|
||||
try {
|
||||
|
||||
$permissions = 'prices:read';
|
||||
// Generate random token
|
||||
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
|
||||
|
||||
// Set tanggal kadaluarsa (jika ada)
|
||||
$expiredAt = $expiryDays ? date('Y-m-d H:i:s', strtotime("+{$expiryDays} days")) : null;
|
||||
|
||||
// Convert permissions to JSON if it's an array
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Insert ke database
|
||||
$sql = "INSERT INTO api_keys (
|
||||
user_id, api_key, name, description, permissions,
|
||||
rate_limit, is_active, created_by, expired_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
?, 1, ?, ?
|
||||
)";
|
||||
$prm = [$userId, $apiKey, $name, $description, $permissions, $rateLimit, $userId, $expiredAt];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return [
|
||||
'id' => $db->lastInsertId(),
|
||||
'api_key' => $apiKey,
|
||||
'expired_at' => $expiredAt
|
||||
];
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error generating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menonaktifkan API key
|
||||
function deactivateApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_active = 0, updated_at = NOW(), updated_by = ? WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deactivating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menghapus API key (soft delete)
|
||||
function deleteApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_deleted = 1, deleted_at = NOW(), deleted_by = ?, is_active = 0 WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deleting API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fungsi untuk memvalidasi API key dan cek izin
|
||||
function validateApiKeyWithPermission($apiKey, $requiredPermission) {
|
||||
try {
|
||||
$sql = "SELECT id, user_id, permissions, rate_limit, last_used_at FROM api_keys WHERE api_key = ? AND is_active = 1 AND is_deleted = 0 AND (expired_at IS NULL OR expired_at > NOW())";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$apiKeyData = $query->row_array();
|
||||
|
||||
if (!$apiKeyData) {
|
||||
return false; // API key tidak valid atau tidak aktif
|
||||
}
|
||||
|
||||
// Update last_used_at
|
||||
$sql = "UPDATE api_keys SET last_used_at = NOW() WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$apiKeyData['id']];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['UPDATE_API_KEY_LAST_USED', 'price/validateApiKeyWithPermission', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Cek rate limiting (implementasi sederhana)
|
||||
if ($apiKeyData['last_used_at']) {
|
||||
$lastUsed = new DateTime($apiKeyData['last_used_at']);
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $lastUsed->getTimestamp();
|
||||
|
||||
// Jika terakhir digunakan kurang dari 1 detik yang lalu, dan sudah melebihi rate limit
|
||||
// Ini implementasi sederhana, idealnya gunakan sistem rate limiting yang lebih kompleks
|
||||
if ($diff < 1 && $this->countRecentRequests($apiKeyData['id']) >= $apiKeyData['rate_limit']) {
|
||||
return ['error' => 'rate_limit_exceeded'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cek permissions jika ada
|
||||
if ($requiredPermission) {
|
||||
$permissions = json_decode($apiKeyData['permissions'], true);
|
||||
|
||||
// Format permission: {"resource": "action1,action2"}
|
||||
// contoh: {"prices": "read,write"}
|
||||
list($resource, $action) = explode(':', $requiredPermission);
|
||||
|
||||
if (!isset($permissions[$resource]) ||
|
||||
!in_array($action, explode(',', $permissions[$resource]))) {
|
||||
return ['error' => 'permission_denied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $apiKeyData;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
343
application/controllers/website/Branch.php--010825
Normal file
343
application/controllers/website/Branch.php--010825
Normal file
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
class Branch extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db_onedev = $this->load->database("onedev", true);
|
||||
$this->db_smartone = $this->load->database("onedev", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API handler untuk mendapatkan semua data harga
|
||||
* Implementasi dengan fitur keamanan yang baik
|
||||
*
|
||||
* @return void Output JSON response
|
||||
*/
|
||||
|
||||
function get_branches() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'branches:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT SELECT M_BranchCode as branch_code,
|
||||
M_BranchCodeLab as branch_code_lab,
|
||||
M_BranchName as branch_name,
|
||||
M_BranchAddress as branch_address,
|
||||
M_BranchLat as branch_latitude,
|
||||
M_BranchLong as branch_longitude
|
||||
FROM m_branch
|
||||
WHERE BranchIsActive = 'Y'
|
||||
ORDER BY BranchCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_BRANCHES', 'branch/get_branches', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function insert_log_error($sql, $params){
|
||||
$sql = "INSERT INTO error_log_website(
|
||||
ErrorLogWebsiteCode,
|
||||
ErrorLogWebsiteFnName,
|
||||
ErrorLogWebsiteDescription,
|
||||
ErrorLogWebsiteCreated
|
||||
)
|
||||
VALUES(
|
||||
?,?,?,NOW()
|
||||
)";
|
||||
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan API key dari header Authorization
|
||||
*
|
||||
* @return string|null API key atau null jika tidak ada
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
$headers = null;
|
||||
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER['Authorization']);
|
||||
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$headers = trim($_SERVER['HTTP_AUTHORIZATION']);
|
||||
} else if (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
$requestHeaders = array_combine(
|
||||
array_map('ucwords', array_keys($requestHeaders)),
|
||||
array_values($requestHeaders)
|
||||
);
|
||||
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
|
||||
// Format header biasanya "Bearer {token}"
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi API key
|
||||
*
|
||||
* @param string $apiKey API key untuk divalidasi
|
||||
* @return bool True jika valid, false jika tidak
|
||||
*/
|
||||
private function validateApiKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contoh implementasi validasi:
|
||||
// 1. Cek token di database
|
||||
// 2. Validasi JWT token
|
||||
// 3. Cek api key statis (hanya untuk contoh sederhana)
|
||||
|
||||
try {
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1 AND expired_at > NOW()";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitasi input untuk mencegah SQL injection dan XSS
|
||||
*
|
||||
* @param string $input Input yang akan disanitasi
|
||||
* @return string Input yang sudah disanitasi
|
||||
*/
|
||||
private function sanitizeInput($input) {
|
||||
if (is_string($input)) {
|
||||
// Hapus karakter tidak perlu
|
||||
$input = trim($input);
|
||||
|
||||
// Hilangkan HTML tags
|
||||
$input = strip_tags($input);
|
||||
|
||||
// Konversi special characters ke HTML entities
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim HTTP response dengan JSON
|
||||
*
|
||||
* @param int $statusCode HTTP status code
|
||||
* @param array $data Data yang akan dikirim sebagai JSON
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($statusCode, $data) {
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Tambahkan header keamanan
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header('Content-Security-Policy: default-src \'self\'');
|
||||
|
||||
// Set cache control untuk kontrol caching
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
// Konversi data ke JSON dan kirim
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fungsi untuk menghasilkan API key baru
|
||||
function generateApiKey($userId, $name, $description, $rateLimit = 100, $expiryDays = 365) {
|
||||
try {
|
||||
|
||||
$permissions = 'prices:read';
|
||||
// Generate random token
|
||||
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
|
||||
|
||||
// Set tanggal kadaluarsa (jika ada)
|
||||
$expiredAt = $expiryDays ? date('Y-m-d H:i:s', strtotime("+{$expiryDays} days")) : null;
|
||||
|
||||
// Convert permissions to JSON if it's an array
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Insert ke database
|
||||
$sql = "INSERT INTO api_keys (
|
||||
user_id, api_key, name, description, permissions,
|
||||
rate_limit, is_active, created_by, expired_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
?, 1, ?, ?
|
||||
)";
|
||||
$prm = [$userId, $apiKey, $name, $description, $permissions, $rateLimit, $userId, $expiredAt];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return [
|
||||
'id' => $db->lastInsertId(),
|
||||
'api_key' => $apiKey,
|
||||
'expired_at' => $expiredAt
|
||||
];
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error generating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menonaktifkan API key
|
||||
function deactivateApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_active = 0, updated_at = NOW(), updated_by = ? WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deactivating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menghapus API key (soft delete)
|
||||
function deleteApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_deleted = 1, deleted_at = NOW(), deleted_by = ?, is_active = 0 WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deleting API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fungsi untuk memvalidasi API key dan cek izin
|
||||
function validateApiKeyWithPermission($apiKey, $requiredPermission) {
|
||||
try {
|
||||
$sql = "SELECT id, user_id, permissions, rate_limit, last_used_at FROM api_keys WHERE api_key = ? AND is_active = 1 AND is_deleted = 0 AND (expired_at IS NULL OR expired_at > NOW())";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$apiKeyData = $query->row_array();
|
||||
|
||||
if (!$apiKeyData) {
|
||||
return false; // API key tidak valid atau tidak aktif
|
||||
}
|
||||
|
||||
// Update last_used_at
|
||||
$sql = "UPDATE api_keys SET last_used_at = NOW() WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$apiKeyData['id']];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['UPDATE_API_KEY_LAST_USED', 'price/validateApiKeyWithPermission', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Cek rate limiting (implementasi sederhana)
|
||||
if ($apiKeyData['last_used_at']) {
|
||||
$lastUsed = new DateTime($apiKeyData['last_used_at']);
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $lastUsed->getTimestamp();
|
||||
|
||||
// Jika terakhir digunakan kurang dari 1 detik yang lalu, dan sudah melebihi rate limit
|
||||
// Ini implementasi sederhana, idealnya gunakan sistem rate limiting yang lebih kompleks
|
||||
if ($diff < 1 && $this->countRecentRequests($apiKeyData['id']) >= $apiKeyData['rate_limit']) {
|
||||
return ['error' => 'rate_limit_exceeded'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cek permissions jika ada
|
||||
if ($requiredPermission) {
|
||||
$permissions = json_decode($apiKeyData['permissions'], true);
|
||||
|
||||
// Format permission: {"resource": "action1,action2"}
|
||||
// contoh: {"prices": "read,write"}
|
||||
list($resource, $action) = explode(':', $requiredPermission);
|
||||
|
||||
if (!isset($permissions[$resource]) ||
|
||||
!in_array($action, explode(',', $permissions[$resource]))) {
|
||||
return ['error' => 'permission_denied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $apiKeyData;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
341
application/controllers/website/Branch.php--210525
Normal file
341
application/controllers/website/Branch.php--210525
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
class Branch extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db_onedev = $this->load->database("onedev", true);
|
||||
$this->db_smartone = $this->load->database("onedev", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API handler untuk mendapatkan semua data harga
|
||||
* Implementasi dengan fitur keamanan yang baik
|
||||
*
|
||||
* @return void Output JSON response
|
||||
*/
|
||||
|
||||
function get_branches() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'branches:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT BranchCode as branch_code,
|
||||
M_BranchCodeLab as branch_code_lab,
|
||||
BranchName as branch_name,
|
||||
M_BranchAddress as branch_address
|
||||
FROM m_branch
|
||||
WHERE BranchIsActive = 'Y'
|
||||
ORDER BY BranchCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_BRANCHES', 'branch/get_branches', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function insert_log_error($sql, $params){
|
||||
$sql = "INSERT INTO error_log_website(
|
||||
ErrorLogWebsiteCode,
|
||||
ErrorLogWebsiteFnName,
|
||||
ErrorLogWebsiteDescription,
|
||||
ErrorLogWebsiteCreated
|
||||
)
|
||||
VALUES(
|
||||
?,?,?,NOW()
|
||||
)";
|
||||
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan API key dari header Authorization
|
||||
*
|
||||
* @return string|null API key atau null jika tidak ada
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
$headers = null;
|
||||
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER['Authorization']);
|
||||
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$headers = trim($_SERVER['HTTP_AUTHORIZATION']);
|
||||
} else if (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
$requestHeaders = array_combine(
|
||||
array_map('ucwords', array_keys($requestHeaders)),
|
||||
array_values($requestHeaders)
|
||||
);
|
||||
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
|
||||
// Format header biasanya "Bearer {token}"
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi API key
|
||||
*
|
||||
* @param string $apiKey API key untuk divalidasi
|
||||
* @return bool True jika valid, false jika tidak
|
||||
*/
|
||||
private function validateApiKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contoh implementasi validasi:
|
||||
// 1. Cek token di database
|
||||
// 2. Validasi JWT token
|
||||
// 3. Cek api key statis (hanya untuk contoh sederhana)
|
||||
|
||||
try {
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1 AND expired_at > NOW()";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitasi input untuk mencegah SQL injection dan XSS
|
||||
*
|
||||
* @param string $input Input yang akan disanitasi
|
||||
* @return string Input yang sudah disanitasi
|
||||
*/
|
||||
private function sanitizeInput($input) {
|
||||
if (is_string($input)) {
|
||||
// Hapus karakter tidak perlu
|
||||
$input = trim($input);
|
||||
|
||||
// Hilangkan HTML tags
|
||||
$input = strip_tags($input);
|
||||
|
||||
// Konversi special characters ke HTML entities
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim HTTP response dengan JSON
|
||||
*
|
||||
* @param int $statusCode HTTP status code
|
||||
* @param array $data Data yang akan dikirim sebagai JSON
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($statusCode, $data) {
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Tambahkan header keamanan
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header('Content-Security-Policy: default-src \'self\'');
|
||||
|
||||
// Set cache control untuk kontrol caching
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
// Konversi data ke JSON dan kirim
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fungsi untuk menghasilkan API key baru
|
||||
function generateApiKey($userId, $name, $description, $rateLimit = 100, $expiryDays = 365) {
|
||||
try {
|
||||
|
||||
$permissions = 'prices:read';
|
||||
// Generate random token
|
||||
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
|
||||
|
||||
// Set tanggal kadaluarsa (jika ada)
|
||||
$expiredAt = $expiryDays ? date('Y-m-d H:i:s', strtotime("+{$expiryDays} days")) : null;
|
||||
|
||||
// Convert permissions to JSON if it's an array
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Insert ke database
|
||||
$sql = "INSERT INTO api_keys (
|
||||
user_id, api_key, name, description, permissions,
|
||||
rate_limit, is_active, created_by, expired_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
?, 1, ?, ?
|
||||
)";
|
||||
$prm = [$userId, $apiKey, $name, $description, $permissions, $rateLimit, $userId, $expiredAt];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return [
|
||||
'id' => $db->lastInsertId(),
|
||||
'api_key' => $apiKey,
|
||||
'expired_at' => $expiredAt
|
||||
];
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error generating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menonaktifkan API key
|
||||
function deactivateApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_active = 0, updated_at = NOW(), updated_by = ? WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deactivating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menghapus API key (soft delete)
|
||||
function deleteApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_deleted = 1, deleted_at = NOW(), deleted_by = ?, is_active = 0 WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deleting API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fungsi untuk memvalidasi API key dan cek izin
|
||||
function validateApiKeyWithPermission($apiKey, $requiredPermission) {
|
||||
try {
|
||||
$sql = "SELECT id, user_id, permissions, rate_limit, last_used_at FROM api_keys WHERE api_key = ? AND is_active = 1 AND is_deleted = 0 AND (expired_at IS NULL OR expired_at > NOW())";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$apiKeyData = $query->row_array();
|
||||
|
||||
if (!$apiKeyData) {
|
||||
return false; // API key tidak valid atau tidak aktif
|
||||
}
|
||||
|
||||
// Update last_used_at
|
||||
$sql = "UPDATE api_keys SET last_used_at = NOW() WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$apiKeyData['id']];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['UPDATE_API_KEY_LAST_USED', 'price/validateApiKeyWithPermission', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Cek rate limiting (implementasi sederhana)
|
||||
if ($apiKeyData['last_used_at']) {
|
||||
$lastUsed = new DateTime($apiKeyData['last_used_at']);
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $lastUsed->getTimestamp();
|
||||
|
||||
// Jika terakhir digunakan kurang dari 1 detik yang lalu, dan sudah melebihi rate limit
|
||||
// Ini implementasi sederhana, idealnya gunakan sistem rate limiting yang lebih kompleks
|
||||
if ($diff < 1 && $this->countRecentRequests($apiKeyData['id']) >= $apiKeyData['rate_limit']) {
|
||||
return ['error' => 'rate_limit_exceeded'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cek permissions jika ada
|
||||
if ($requiredPermission) {
|
||||
$permissions = json_decode($apiKeyData['permissions'], true);
|
||||
|
||||
// Format permission: {"resource": "action1,action2"}
|
||||
// contoh: {"prices": "read,write"}
|
||||
list($resource, $action) = explode(':', $requiredPermission);
|
||||
|
||||
if (!isset($permissions[$resource]) ||
|
||||
!in_array($action, explode(',', $permissions[$resource]))) {
|
||||
return ['error' => 'permission_denied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $apiKeyData;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2267
application/controllers/website/Order.php
Normal file
2267
application/controllers/website/Order.php
Normal file
File diff suppressed because it is too large
Load Diff
2222
application/controllers/website/Order_bkp170625.php
Normal file
2222
application/controllers/website/Order_bkp170625.php
Normal file
File diff suppressed because it is too large
Load Diff
480
application/controllers/website/Patient.php
Normal file
480
application/controllers/website/Patient.php
Normal file
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
class Patient extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db_onedev = $this->load->database("onedev", true);
|
||||
$this->db_smartone = $this->load->database("onedev", true);
|
||||
|
||||
// Daftar domain yang diizinkan mengakses API
|
||||
$allowedOrigins = [
|
||||
'https://devone.aplikasi.web.id',
|
||||
'https://westerindo.com'
|
||||
// Tambahkan domain lain yang diizinkan di sini
|
||||
];
|
||||
|
||||
// Ambil Origin dari request
|
||||
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
|
||||
|
||||
// Handle preflight request khusus
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
// Cek apakah origin ada dalam daftar yang diizinkan
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
// Izinkan origin spesifik
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
|
||||
// Ambil header yang diminta dari request preflight
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
||||
header('Access-Control-Allow-Headers: ' . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||
} else {
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept, X-Requested-With');
|
||||
}
|
||||
|
||||
header('Access-Control-Max-Age: 86400'); // Cache preflight selama 24 jam
|
||||
header('Content-Length: 0');
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(200);
|
||||
} else {
|
||||
// Origin tidak diizinkan - berikan respons 403
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(403);
|
||||
echo 'Origin not allowed';
|
||||
}
|
||||
exit(0); // Penting untuk menghentikan eksekusi lebih lanjut
|
||||
}
|
||||
|
||||
// Header untuk request normal (non-OPTIONS)
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function get_regional() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'patient:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ambil dan validasi parameter filtering (jika ada)
|
||||
$filters = [];
|
||||
$allowedFilters = ['type','search', 'province_id','kabupaten_id','kecamatan_id','kelurahan_id'];
|
||||
|
||||
foreach ($allowedFilters as $filter) {
|
||||
if (isset($_GET[$filter])) {
|
||||
// Sanitasi input
|
||||
$filters[$filter] = $this->sanitizeInput($_GET[$filter]);
|
||||
}
|
||||
}
|
||||
|
||||
$search = isset($filters['search']) ? $filters['search'] : '';
|
||||
$type = isset($filters['type']) ? $filters['type'] : '';
|
||||
$province_id = isset($filters['province_id']) ? $filters['province_id'] : '';
|
||||
$kabupaten_id = isset($filters['kabupaten_id']) ? $filters['kabupaten_id'] : '';
|
||||
$kecamatan_id = isset($filters['kecamatan_id']) ? $filters['kecamatan_id'] : '';
|
||||
$kelurahan_id = isset($filters['kelurahan_id']) ? $filters['kelurahan_id'] : '';
|
||||
|
||||
|
||||
$regional_cd = '';
|
||||
$params = [];
|
||||
$params[] = $type;
|
||||
$params[] = $type;
|
||||
$params[] = $type;
|
||||
$params[] = $type;
|
||||
$filters = 'regional_nm LIKE ?';
|
||||
$params[] = '%'.$search.'%';
|
||||
|
||||
if($type == 'province'){
|
||||
if($province_id != ''){
|
||||
$filters .= " AND pro_cd = ?";
|
||||
$params[] = $province_id;
|
||||
}else{
|
||||
$filters .= " AND kab_cd = '00' AND kec_cd = '000' AND kel_cd = '000'";
|
||||
}
|
||||
}
|
||||
|
||||
if($type == 'kabupaten'){
|
||||
if($province_id == ''){
|
||||
$this->sendResponse(401, ['error' => 'Data tidak lengkap', 'message' => 'Data provinsi tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
if($kabupaten_id != ''){
|
||||
$filters .= " AND pro_cd = ? AND kab_cd = ? AND kec_cd = '000' AND kel_cd = '000'";
|
||||
$params[] = $province_id;
|
||||
$params[] = $kabupaten_id;
|
||||
}else{
|
||||
$filters .= " AND pro_cd = ? AND kab_cd <> '00' AND kec_cd = '000' AND kel_cd = '000'";
|
||||
$params[] = $province_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($type == 'kecamatan'){
|
||||
if($province_id == '' || $kabupaten_id == ''){
|
||||
$this->sendResponse(401, ['error' => 'Data tidak lengkap', 'message' => 'Data provinsi atau kabupaten tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
if($kecamatan_id != ''){
|
||||
$filters .= " AND pro_cd = ? AND kab_cd = ? AND kec_cd = ? AND kel_cd = '000'";
|
||||
$params[] = $province_id;
|
||||
$params[] = $kabupaten_id;
|
||||
$params[] = $kecamatan_id;
|
||||
}else{
|
||||
$filters .= " AND pro_cd = ? AND kab_cd = ? AND kec_cd <> '000' AND kel_cd = '000'";
|
||||
$params[] = $province_id;
|
||||
$params[] = $kabupaten_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($type == 'kelurahan'){
|
||||
if($province_id == '' || $kabupaten_id == '' || $kecamatan_id == ''){
|
||||
$this->sendResponse(401, ['error' => 'Data tidak lengkap', 'message' => 'Data provinsi, kabupaten, atau kecamatan tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
if($kelurahan_id != ''){
|
||||
$filters .= " AND pro_cd = ? AND kab_cd = ? AND kec_cd = ? AND kel_cd = ?";
|
||||
$params[] = $province_id;
|
||||
$params[] = $kabupaten_id;
|
||||
$params[] = $kecamatan_id;
|
||||
$params[] = $kelurahan_id;
|
||||
}else{
|
||||
$filters .= " AND pro_cd = ? AND kab_cd = ? AND kec_cd = ? AND kel_cd <> '000'";
|
||||
$params[] = $province_id;
|
||||
$params[] = $kabupaten_id;
|
||||
$params[] = $kecamatan_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT
|
||||
case
|
||||
when ? = 'province' then pro_cd
|
||||
when ? = 'kabupaten' then kab_cd
|
||||
when ? = 'kecamatan' then kec_cd
|
||||
when ? = 'kelurahan' then kel_cd
|
||||
end as id,
|
||||
regional_nm as name
|
||||
FROM regional
|
||||
WHERE
|
||||
$filters
|
||||
LIMIT 100";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
//echo $this->db_onedev->last_query();
|
||||
if(!$query){
|
||||
$prm_log = ['GET_REGIONAL', 'patient/get_regional', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function insert_log_error($sql, $params){
|
||||
$sql = "INSERT INTO error_log_website(
|
||||
ErrorLogWebsiteCode,
|
||||
ErrorLogWebsiteFnName,
|
||||
ErrorLogWebsiteDescription,
|
||||
ErrorLogWebsiteCreated
|
||||
)
|
||||
VALUES(
|
||||
?,?,?,NOW()
|
||||
)";
|
||||
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan API key dari header Authorization
|
||||
*
|
||||
* @return string|null API key atau null jika tidak ada
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
$headers = null;
|
||||
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER['Authorization']);
|
||||
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$headers = trim($_SERVER['HTTP_AUTHORIZATION']);
|
||||
} else if (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
$requestHeaders = array_combine(
|
||||
array_map('ucwords', array_keys($requestHeaders)),
|
||||
array_values($requestHeaders)
|
||||
);
|
||||
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
|
||||
// Format header biasanya "Bearer {token}"
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi API key
|
||||
*
|
||||
* @param string $apiKey API key untuk divalidasi
|
||||
* @return bool True jika valid, false jika tidak
|
||||
*/
|
||||
private function validateApiKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contoh implementasi validasi:
|
||||
// 1. Cek token di database
|
||||
// 2. Validasi JWT token
|
||||
// 3. Cek api key statis (hanya untuk contoh sederhana)
|
||||
|
||||
try {
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1 AND expired_at > NOW()";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitasi input untuk mencegah SQL injection dan XSS
|
||||
*
|
||||
* @param string $input Input yang akan disanitasi
|
||||
* @return string Input yang sudah disanitasi
|
||||
*/
|
||||
private function sanitizeInput($input) {
|
||||
if (is_string($input)) {
|
||||
// Hapus karakter tidak perlu
|
||||
$input = trim($input);
|
||||
|
||||
// Hilangkan HTML tags
|
||||
$input = strip_tags($input);
|
||||
|
||||
// Konversi special characters ke HTML entities
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim HTTP response dengan JSON
|
||||
*
|
||||
* @param int $statusCode HTTP status code
|
||||
* @param array $data Data yang akan dikirim sebagai JSON
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($statusCode, $data) {
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Tambahkan header keamanan
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header('Content-Security-Policy: default-src \'self\'');
|
||||
|
||||
// Set cache control untuk kontrol caching
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
// Konversi data ke JSON dan kirim
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fungsi untuk menghasilkan API key baru
|
||||
function generateApiKey($userId, $name, $description, $rateLimit = 100, $expiryDays = 365) {
|
||||
try {
|
||||
|
||||
$permissions = 'prices:read';
|
||||
// Generate random token
|
||||
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
|
||||
|
||||
// Set tanggal kadaluarsa (jika ada)
|
||||
$expiredAt = $expiryDays ? date('Y-m-d H:i:s', strtotime("+{$expiryDays} days")) : null;
|
||||
|
||||
// Convert permissions to JSON if it's an array
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Insert ke database
|
||||
$sql = "INSERT INTO api_keys (
|
||||
user_id, api_key, name, description, permissions,
|
||||
rate_limit, is_active, created_by, expired_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
?, 1, ?, ?
|
||||
)";
|
||||
$prm = [$userId, $apiKey, $name, $description, $permissions, $rateLimit, $userId, $expiredAt];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return [
|
||||
'id' => $db->lastInsertId(),
|
||||
'api_key' => $apiKey,
|
||||
'expired_at' => $expiredAt
|
||||
];
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error generating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menonaktifkan API key
|
||||
function deactivateApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_active = 0, updated_at = NOW(), updated_by = ? WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deactivating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menghapus API key (soft delete)
|
||||
function deleteApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_deleted = 1, deleted_at = NOW(), deleted_by = ?, is_active = 0 WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deleting API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fungsi untuk memvalidasi API key dan cek izin
|
||||
function validateApiKeyWithPermission($apiKey, $requiredPermission) {
|
||||
try {
|
||||
$sql = "SELECT id, user_id, permissions, rate_limit, last_used_at FROM api_keys WHERE api_key = ? AND is_active = 1 AND is_deleted = 0 AND (expired_at IS NULL OR expired_at > NOW())";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$apiKeyData = $query->row_array();
|
||||
|
||||
if (!$apiKeyData) {
|
||||
return false; // API key tidak valid atau tidak aktif
|
||||
}
|
||||
|
||||
// Update last_used_at
|
||||
$sql = "UPDATE api_keys SET last_used_at = NOW() WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$apiKeyData['id']];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['UPDATE_API_KEY_LAST_USED', 'price/validateApiKeyWithPermission', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Cek rate limiting (implementasi sederhana)
|
||||
if ($apiKeyData['last_used_at']) {
|
||||
$lastUsed = new DateTime($apiKeyData['last_used_at']);
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $lastUsed->getTimestamp();
|
||||
|
||||
// Jika terakhir digunakan kurang dari 1 detik yang lalu, dan sudah melebihi rate limit
|
||||
// Ini implementasi sederhana, idealnya gunakan sistem rate limiting yang lebih kompleks
|
||||
if ($diff < 1 && $this->countRecentRequests($apiKeyData['id']) >= $apiKeyData['rate_limit']) {
|
||||
return ['error' => 'rate_limit_exceeded'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cek permissions jika ada
|
||||
if ($requiredPermission) {
|
||||
$permissions = json_decode($apiKeyData['permissions'], true);
|
||||
|
||||
// Format permission: {"resource": "action1,action2"}
|
||||
// contoh: {"prices": "read,write"}
|
||||
list($resource, $action) = explode(':', $requiredPermission);
|
||||
|
||||
if (!isset($permissions[$resource]) ||
|
||||
!in_array($action, explode(',', $permissions[$resource]))) {
|
||||
return ['error' => 'permission_denied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $apiKeyData;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
722
application/controllers/website/Price.php
Normal file
722
application/controllers/website/Price.php
Normal file
@@ -0,0 +1,722 @@
|
||||
<?php
|
||||
class Price extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db_onedev = $this->load->database("onedev", true);
|
||||
$this->db_smartone = $this->load->database("onedev", true);
|
||||
|
||||
// Daftar domain yang diizinkan mengakses API
|
||||
$allowedOrigins = [
|
||||
'https://devone.aplikasi.web.id',
|
||||
'https://westerindo.com'
|
||||
// Tambahkan domain lain yang diizinkan di sini
|
||||
];
|
||||
|
||||
// Ambil Origin dari request
|
||||
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
|
||||
|
||||
// Handle preflight request khusus
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
// Cek apakah origin ada dalam daftar yang diizinkan
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
// Izinkan origin spesifik
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
|
||||
// Ambil header yang diminta dari request preflight
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
||||
header('Access-Control-Allow-Headers: ' . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||
} else {
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept, X-Requested-With');
|
||||
}
|
||||
|
||||
header('Access-Control-Max-Age: 86400'); // Cache preflight selama 24 jam
|
||||
header('Content-Length: 0');
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(200);
|
||||
} else {
|
||||
// Origin tidak diizinkan - berikan respons 403
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(403);
|
||||
echo 'Origin not allowed';
|
||||
}
|
||||
exit(0); // Penting untuk menghentikan eksekusi lebih lanjut
|
||||
}
|
||||
|
||||
// Header untuk request normal (non-OPTIONS)
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API handler untuk mendapatkan semua data harga
|
||||
* Implementasi dengan fitur keamanan yang baik
|
||||
*
|
||||
* @return void Output JSON response
|
||||
*/
|
||||
|
||||
function get_branches() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'branches:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT M_BranchCode as branch_code,
|
||||
M_BranchCodeLab as branch_code_lab,
|
||||
M_BranchName as branch_name,
|
||||
'' as branch_address,
|
||||
M_BranchLat as branch_latitude,
|
||||
M_BranchLong as branch_longitude
|
||||
FROM m_branch
|
||||
WHERE M_BranchIsActive = 'Y' AND M_BranchIsOuter = 'N'
|
||||
ORDER BY M_BranchCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_BRANCHES', 'branch/get_branches', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_category() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT Nat_SubGroupSasCode as category,
|
||||
Nat_SubGroupFormalName as category_name
|
||||
FROM nat_subgroup
|
||||
WHERE Nat_SubGroupIsActive = 'Y'
|
||||
ORDER BY Nat_SubGroupSasCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_CATEGORY', 'price/get_category', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_single_price() {
|
||||
// --- 1. VALIDASI REQUEST DAN KEAMANAN ---
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 2. PARAMETER FILTERING & PAGINATION ---
|
||||
|
||||
// Ambil dan validasi parameter filtering (jika ada)
|
||||
$filters = [];
|
||||
$allowedFilters = ['search','category'];
|
||||
|
||||
foreach ($allowedFilters as $filter) {
|
||||
if (isset($_GET[$filter])) {
|
||||
// Sanitasi input
|
||||
$filters[$filter] = $this->sanitizeInput($_GET[$filter]);
|
||||
}
|
||||
}
|
||||
|
||||
//parameter
|
||||
$search = isset($filters['search']) ? $filters['search'] : '';
|
||||
$category = isset($filters['category']) ? $filters['category'] : '';
|
||||
|
||||
// Pagination
|
||||
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
|
||||
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20; // Default 20, max 100
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// --- 3. QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
$params = [];
|
||||
$params[] = '%'.$search.'%';
|
||||
$filter_category = '';
|
||||
if($category != ''){
|
||||
$filter_category = " AND Nat_SubGroupSasCode = ?";
|
||||
$params[] = $category;
|
||||
}
|
||||
|
||||
$sql = "SELECT COUNT(*) as total
|
||||
FROM `ss_price_mou`
|
||||
JOIN `t_test` test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN `nat_test` ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN `nat_subgroup` ON `Nat_TestNat_SubGroupID` = `Nat_SubGroupID` $filter_category
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'N'";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_SINGLE', 'price/get_single_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$totalCount = $query->row_array()['total'];
|
||||
$totalPages = ceil($totalCount / $limit);
|
||||
|
||||
$params[] = $offset;
|
||||
$params[] = $limit;
|
||||
$sql = "SELECT Mgm_McuNumber as project_number,
|
||||
ss_price_mou.Ss_PriceMouID as x_id,
|
||||
test.T_TestID as test_id,
|
||||
nat_test.Nat_TestID as test_nat_id,
|
||||
test.T_TestName as test_name,
|
||||
test.T_TestSasCode as test_sas_code,
|
||||
Nat_SubGroupName as category,
|
||||
ss_price_mou.T_TestRequirement as test_requirement,
|
||||
ss_price_mou.T_PriceAmount as price,
|
||||
ss_price_mou.T_PriceDisc as disc,
|
||||
ss_price_mou.T_PriceDiscRp as disc_rp,
|
||||
ss_price_mou.T_PriceSubTotal as subtotal,
|
||||
ss_price_mou.T_PriceTotal as total,
|
||||
ss_price_mou.px_type as px_type,
|
||||
ss_price_mou.nat_test as nat_tests,
|
||||
ss_price_mou.child_test as child_test,
|
||||
ss_price_mou.T_PriceT_PriceHeaderID as price_header_id,
|
||||
IFNULL(nat_test_desc.Nat_TestDescNote, '') as test_desc
|
||||
FROM ss_price_mou
|
||||
JOIN t_test test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN nat_test ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN nat_subgroup ON Nat_TestNat_SubGroupID = Nat_SubGroupID $filter_category
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
LEFT JOIN nat_test_desc ON nat_test_desc.Nat_TestDescNat_TestID = nat_test.Nat_TestID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'N'
|
||||
ORDER BY ss_price_mou.T_TestName ASC
|
||||
LIMIT ?, ?";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_SINGLE', 'price/get_single_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
if(count($rows) > 0){
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]['total'] = $row['total'];
|
||||
$child_test = json_decode($row['child_test']);
|
||||
$rows[$key]['child_test'] = $child_test;
|
||||
|
||||
|
||||
if($row['px_type'] == 'PXR' && count($child_test) > 0){
|
||||
$total = 0;
|
||||
|
||||
foreach($child_test as $key_child => $child){
|
||||
//echo $child->T_PriceTotal;
|
||||
$total += $child->T_PriceTotal;
|
||||
}
|
||||
$rows[$key]['total'] = $total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total_records' => $totalCount,
|
||||
'total_pages' => $totalPages
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_packet_price() {
|
||||
// --- 1. VALIDASI REQUEST DAN KEAMANAN ---
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 2. PARAMETER FILTERING & PAGINATION ---
|
||||
|
||||
// Ambil dan validasi parameter filtering (jika ada)
|
||||
$filters = [];
|
||||
$allowedFilters = ['search'];
|
||||
|
||||
foreach ($allowedFilters as $filter) {
|
||||
if (isset($_GET[$filter])) {
|
||||
// Sanitasi input
|
||||
$filters[$filter] = $this->sanitizeInput($_GET[$filter]);
|
||||
}
|
||||
}
|
||||
|
||||
//parameter
|
||||
$search = isset($filters['search']) ? $filters['search'] : '';
|
||||
|
||||
// Pagination
|
||||
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
|
||||
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20; // Default 20, max 100
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// --- 3. QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
$params = [];
|
||||
$params[] = '%'.$search.'%';
|
||||
|
||||
$sql = "SELECT COUNT(*) as total
|
||||
FROM `ss_price_mou`
|
||||
JOIN `t_test` test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN `nat_test` ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN `nat_subgroup` ON `Nat_TestNat_SubGroupID` = `Nat_SubGroupID`
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'Y'";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_PACKET', 'price/get_packet_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$totalCount = $query->row_array()['total'];
|
||||
$totalPages = ceil($totalCount / $limit);
|
||||
|
||||
$params[] = $offset;
|
||||
$params[] = $limit;
|
||||
$sql = "SELECT
|
||||
Mgm_McuNumber as project_number,
|
||||
ss_price_mou.Ss_PriceMouID as x_id,
|
||||
t_packet.T_PacketID as test_id,
|
||||
0 as test_nat_id,
|
||||
ss_price_mou.T_TestName as test_name,
|
||||
t_packet.T_PacketSasCode as test_sas_code,
|
||||
'packet' as category,
|
||||
ss_price_mou.T_TestRequirement as test_requirement,
|
||||
ss_price_mou.T_PriceAmount as price,
|
||||
ss_price_mou.T_PriceDisc as disc,
|
||||
ss_price_mou.T_PriceDiscRp as disc_rp,
|
||||
ss_price_mou.T_PriceSubTotal as subtotal,
|
||||
ss_price_mou.T_PriceTotal as total,
|
||||
ss_price_mou.px_type as px_type,
|
||||
ss_price_mou.nat_test as nat_tests,
|
||||
ss_price_mou.child_test as child_test,
|
||||
ss_price_mou.T_PriceT_PriceHeaderID as price_header_id,
|
||||
IFNULL(T_PacketDescNote, '') as test_desc
|
||||
FROM ss_price_mou
|
||||
JOIN t_packet ON t_packet.T_PacketID = ss_price_mou.T_TestID AND t_packet.T_PacketName LIKE ?
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
LEFT JOIN t_packet_desc_map ON T_PacketDescMapT_PacketID = ss_price_mou.T_TestID AND T_PacketDescMapIsActive = 'Y'
|
||||
LEFT JOIN t_packet_desc ON T_PacketDescMapT_PacketDescID = t_packet_desc.T_PacketDescID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'Y'
|
||||
ORDER BY ss_price_mou.T_TestName ASC
|
||||
LIMIT ?, ?";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_PACKET', 'price/get_packet_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
if(count($rows) > 0){
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]['child_test'] = json_decode($row['child_test']);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total_records' => $totalCount,
|
||||
'total_pages' => $totalPages
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function insert_log_error($sql, $params){
|
||||
$sql = "INSERT INTO error_log_website(
|
||||
ErrorLogWebsiteCode,
|
||||
ErrorLogWebsiteFnName,
|
||||
ErrorLogWebsiteDescription,
|
||||
ErrorLogWebsiteCreated
|
||||
)
|
||||
VALUES(
|
||||
?,?,?,NOW()
|
||||
)";
|
||||
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan API key dari header Authorization
|
||||
*
|
||||
* @return string|null API key atau null jika tidak ada
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
$headers = null;
|
||||
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER['Authorization']);
|
||||
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$headers = trim($_SERVER['HTTP_AUTHORIZATION']);
|
||||
} else if (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
$requestHeaders = array_combine(
|
||||
array_map('ucwords', array_keys($requestHeaders)),
|
||||
array_values($requestHeaders)
|
||||
);
|
||||
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
|
||||
// Format header biasanya "Bearer {token}"
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi API key
|
||||
*
|
||||
* @param string $apiKey API key untuk divalidasi
|
||||
* @return bool True jika valid, false jika tidak
|
||||
*/
|
||||
private function validateApiKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contoh implementasi validasi:
|
||||
// 1. Cek token di database
|
||||
// 2. Validasi JWT token
|
||||
// 3. Cek api key statis (hanya untuk contoh sederhana)
|
||||
|
||||
try {
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1 AND expired_at > NOW()";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitasi input untuk mencegah SQL injection dan XSS
|
||||
*
|
||||
* @param string $input Input yang akan disanitasi
|
||||
* @return string Input yang sudah disanitasi
|
||||
*/
|
||||
private function sanitizeInput($input) {
|
||||
if (is_string($input)) {
|
||||
// Hapus karakter tidak perlu
|
||||
$input = trim($input);
|
||||
|
||||
// Hilangkan HTML tags
|
||||
$input = strip_tags($input);
|
||||
|
||||
// Konversi special characters ke HTML entities
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim HTTP response dengan JSON
|
||||
*
|
||||
* @param int $statusCode HTTP status code
|
||||
* @param array $data Data yang akan dikirim sebagai JSON
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($statusCode, $data) {
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Tambahkan header keamanan
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header('Content-Security-Policy: default-src \'self\'');
|
||||
|
||||
// Set cache control untuk kontrol caching
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
// Konversi data ke JSON dan kirim
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fungsi untuk menghasilkan API key baru
|
||||
function generateApiKey($userId, $name, $description, $rateLimit = 100, $expiryDays = 365) {
|
||||
try {
|
||||
|
||||
$permissions = 'prices:read';
|
||||
// Generate random token
|
||||
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
|
||||
|
||||
// Set tanggal kadaluarsa (jika ada)
|
||||
$expiredAt = $expiryDays ? date('Y-m-d H:i:s', strtotime("+{$expiryDays} days")) : null;
|
||||
|
||||
// Convert permissions to JSON if it's an array
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Insert ke database
|
||||
$sql = "INSERT INTO api_keys (
|
||||
user_id, api_key, name, description, permissions,
|
||||
rate_limit, is_active, created_by, expired_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
?, 1, ?, ?
|
||||
)";
|
||||
$prm = [$userId, $apiKey, $name, $description, $permissions, $rateLimit, $userId, $expiredAt];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return [
|
||||
'id' => $db->lastInsertId(),
|
||||
'api_key' => $apiKey,
|
||||
'expired_at' => $expiredAt
|
||||
];
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error generating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menonaktifkan API key
|
||||
function deactivateApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_active = 0, updated_at = NOW(), updated_by = ? WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deactivating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menghapus API key (soft delete)
|
||||
function deleteApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_deleted = 1, deleted_at = NOW(), deleted_by = ?, is_active = 0 WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deleting API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fungsi untuk memvalidasi API key dan cek izin
|
||||
function validateApiKeyWithPermission($apiKey, $requiredPermission) {
|
||||
try {
|
||||
$sql = "SELECT id, user_id, permissions, rate_limit, last_used_at FROM api_keys WHERE api_key = ? AND is_active = 1 AND is_deleted = 0 AND (expired_at IS NULL OR expired_at > NOW())";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$apiKeyData = $query->row_array();
|
||||
|
||||
if (!$apiKeyData) {
|
||||
return false; // API key tidak valid atau tidak aktif
|
||||
}
|
||||
|
||||
// Update last_used_at
|
||||
$sql = "UPDATE api_keys SET last_used_at = NOW() WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$apiKeyData['id']];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['UPDATE_API_KEY_LAST_USED', 'price/validateApiKeyWithPermission', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Cek rate limiting (implementasi sederhana)
|
||||
if ($apiKeyData['last_used_at']) {
|
||||
$lastUsed = new DateTime($apiKeyData['last_used_at']);
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $lastUsed->getTimestamp();
|
||||
|
||||
// Jika terakhir digunakan kurang dari 1 detik yang lalu, dan sudah melebihi rate limit
|
||||
// Ini implementasi sederhana, idealnya gunakan sistem rate limiting yang lebih kompleks
|
||||
if ($diff < 1 && $this->countRecentRequests($apiKeyData['id']) >= $apiKeyData['rate_limit']) {
|
||||
return ['error' => 'rate_limit_exceeded'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cek permissions jika ada
|
||||
if ($requiredPermission) {
|
||||
$permissions = json_decode($apiKeyData['permissions'], true);
|
||||
|
||||
// Format permission: {"resource": "action1,action2"}
|
||||
// contoh: {"prices": "read,write"}
|
||||
list($resource, $action) = explode(':', $requiredPermission);
|
||||
|
||||
if (!isset($permissions[$resource]) ||
|
||||
!in_array($action, explode(',', $permissions[$resource]))) {
|
||||
return ['error' => 'permission_denied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $apiKeyData;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
722
application/controllers/website/Price.php--010825
Normal file
722
application/controllers/website/Price.php--010825
Normal file
@@ -0,0 +1,722 @@
|
||||
<?php
|
||||
class Price extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db_onedev = $this->load->database("onedev", true);
|
||||
$this->db_smartone = $this->load->database("onedev", true);
|
||||
|
||||
// Daftar domain yang diizinkan mengakses API
|
||||
$allowedOrigins = [
|
||||
'https://devone.aplikasi.web.id',
|
||||
'https://westerindo.com'
|
||||
// Tambahkan domain lain yang diizinkan di sini
|
||||
];
|
||||
|
||||
// Ambil Origin dari request
|
||||
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
|
||||
|
||||
// Handle preflight request khusus
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
// Cek apakah origin ada dalam daftar yang diizinkan
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
// Izinkan origin spesifik
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
|
||||
// Ambil header yang diminta dari request preflight
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
||||
header('Access-Control-Allow-Headers: ' . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||
} else {
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept, X-Requested-With');
|
||||
}
|
||||
|
||||
header('Access-Control-Max-Age: 86400'); // Cache preflight selama 24 jam
|
||||
header('Content-Length: 0');
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(200);
|
||||
} else {
|
||||
// Origin tidak diizinkan - berikan respons 403
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(403);
|
||||
echo 'Origin not allowed';
|
||||
}
|
||||
exit(0); // Penting untuk menghentikan eksekusi lebih lanjut
|
||||
}
|
||||
|
||||
// Header untuk request normal (non-OPTIONS)
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API handler untuk mendapatkan semua data harga
|
||||
* Implementasi dengan fitur keamanan yang baik
|
||||
*
|
||||
* @return void Output JSON response
|
||||
*/
|
||||
|
||||
function get_branches() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'branches:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT M_BranchCode as branch_code,
|
||||
M_BranchCodeLab as branch_code_lab,
|
||||
M_BranchName as branch_name,
|
||||
M_BranchAddress as branch_address,
|
||||
M_BranchLat as branch_latitude,
|
||||
M_BranchLong as branch_longitude
|
||||
FROM m_branch
|
||||
WHERE M_BranchIsActive = 'Y'
|
||||
ORDER BY M_BranchCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_BRANCHES', 'branch/get_branches', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_category() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT Nat_SubGroupSasCode as category,
|
||||
Nat_SubGroupFormalName as category_name
|
||||
FROM nat_subgroup
|
||||
WHERE Nat_SubGroupIsActive = 'Y'
|
||||
ORDER BY Nat_SubGroupSasCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_CATEGORY', 'price/get_category', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_single_price() {
|
||||
// --- 1. VALIDASI REQUEST DAN KEAMANAN ---
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 2. PARAMETER FILTERING & PAGINATION ---
|
||||
|
||||
// Ambil dan validasi parameter filtering (jika ada)
|
||||
$filters = [];
|
||||
$allowedFilters = ['search','category'];
|
||||
|
||||
foreach ($allowedFilters as $filter) {
|
||||
if (isset($_GET[$filter])) {
|
||||
// Sanitasi input
|
||||
$filters[$filter] = $this->sanitizeInput($_GET[$filter]);
|
||||
}
|
||||
}
|
||||
|
||||
//parameter
|
||||
$search = isset($filters['search']) ? $filters['search'] : '';
|
||||
$category = isset($filters['category']) ? $filters['category'] : '';
|
||||
|
||||
// Pagination
|
||||
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
|
||||
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20; // Default 20, max 100
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// --- 3. QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
$params = [];
|
||||
$params[] = '%'.$search.'%';
|
||||
$filter_category = '';
|
||||
if($category != ''){
|
||||
$filter_category = " AND Nat_SubGroupSasCode = ?";
|
||||
$params[] = $category;
|
||||
}
|
||||
|
||||
$sql = "SELECT COUNT(*) as total
|
||||
FROM `ss_price_mou`
|
||||
JOIN `t_test` test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN `nat_test` ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN `nat_subgroup` ON `Nat_TestNat_SubGroupID` = `Nat_SubGroupID` $filter_category
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'N'";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_SINGLE', 'price/get_single_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$totalCount = $query->row_array()['total'];
|
||||
$totalPages = ceil($totalCount / $limit);
|
||||
|
||||
$params[] = $offset;
|
||||
$params[] = $limit;
|
||||
$sql = "SELECT Mgm_McuNumber as project_number,
|
||||
ss_price_mou.Ss_PriceMouID as x_id,
|
||||
test.T_TestID as test_id,
|
||||
nat_test.Nat_TestID as test_nat_id,
|
||||
test.T_TestName as test_name,
|
||||
test.T_TestSasCode as test_sas_code,
|
||||
Nat_SubGroupName as category,
|
||||
ss_price_mou.T_TestRequirement as test_requirement,
|
||||
ss_price_mou.T_PriceAmount as price,
|
||||
ss_price_mou.T_PriceDisc as disc,
|
||||
ss_price_mou.T_PriceDiscRp as disc_rp,
|
||||
ss_price_mou.T_PriceSubTotal as subtotal,
|
||||
ss_price_mou.T_PriceTotal as total,
|
||||
ss_price_mou.px_type as px_type,
|
||||
ss_price_mou.nat_test as nat_tests,
|
||||
ss_price_mou.child_test as child_test,
|
||||
ss_price_mou.T_PriceT_PriceHeaderID as price_header_id,
|
||||
IFNULL(nat_test_desc.Nat_TestDescNote, '') as test_desc
|
||||
FROM ss_price_mou
|
||||
JOIN t_test test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN nat_test ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN nat_subgroup ON Nat_TestNat_SubGroupID = Nat_SubGroupID $filter_category
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
LEFT JOIN nat_test_desc ON nat_test_desc.Nat_TestDescNat_TestID = nat_test.Nat_TestID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'N'
|
||||
ORDER BY ss_price_mou.T_TestName ASC
|
||||
LIMIT ?, ?";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_SINGLE', 'price/get_single_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
if(count($rows) > 0){
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]['T_PriceTotal'] = $row['total'];
|
||||
$child_test = json_decode($row['child_test']);
|
||||
$rows[$key]['child_test'] = $child_test;
|
||||
|
||||
|
||||
if($row['px_type'] == 'PXR' && count($child_test) > 0){
|
||||
$total = 0;
|
||||
|
||||
foreach($child_test as $key_child => $child){
|
||||
|
||||
$total += $child->T_PriceTotal;
|
||||
}
|
||||
$rows[$key]['T_PriceTotal'] = $total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total_records' => $totalCount,
|
||||
'total_pages' => $totalPages
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_packet_price() {
|
||||
// --- 1. VALIDASI REQUEST DAN KEAMANAN ---
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 2. PARAMETER FILTERING & PAGINATION ---
|
||||
|
||||
// Ambil dan validasi parameter filtering (jika ada)
|
||||
$filters = [];
|
||||
$allowedFilters = ['search'];
|
||||
|
||||
foreach ($allowedFilters as $filter) {
|
||||
if (isset($_GET[$filter])) {
|
||||
// Sanitasi input
|
||||
$filters[$filter] = $this->sanitizeInput($_GET[$filter]);
|
||||
}
|
||||
}
|
||||
|
||||
//parameter
|
||||
$search = isset($filters['search']) ? $filters['search'] : '';
|
||||
|
||||
// Pagination
|
||||
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
|
||||
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20; // Default 20, max 100
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// --- 3. QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
$params = [];
|
||||
$params[] = '%'.$search.'%';
|
||||
|
||||
$sql = "SELECT COUNT(*) as total
|
||||
FROM `ss_price_mou`
|
||||
JOIN `t_test` test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN `nat_test` ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN `nat_subgroup` ON `Nat_TestNat_SubGroupID` = `Nat_SubGroupID`
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'Y'";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_PACKET', 'price/get_packet_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$totalCount = $query->row_array()['total'];
|
||||
$totalPages = ceil($totalCount / $limit);
|
||||
|
||||
$params[] = $offset;
|
||||
$params[] = $limit;
|
||||
$sql = "SELECT
|
||||
Mgm_McuNumber as project_number,
|
||||
ss_price_mou.Ss_PriceMouID as x_id,
|
||||
t_packet.T_PacketID as test_id,
|
||||
0 as test_nat_id,
|
||||
ss_price_mou.T_TestName as test_name,
|
||||
t_packet.T_PacketSasCode as test_sas_code,
|
||||
'packet' as category,
|
||||
ss_price_mou.T_TestRequirement as test_requirement,
|
||||
ss_price_mou.T_PriceAmount as price,
|
||||
ss_price_mou.T_PriceDisc as disc,
|
||||
ss_price_mou.T_PriceDiscRp as disc_rp,
|
||||
ss_price_mou.T_PriceSubTotal as subtotal,
|
||||
ss_price_mou.T_PriceTotal as total,
|
||||
ss_price_mou.px_type as px_type,
|
||||
ss_price_mou.nat_test as nat_tests,
|
||||
ss_price_mou.child_test as child_test,
|
||||
ss_price_mou.T_PriceT_PriceHeaderID as price_header_id,
|
||||
IFNULL(T_PacketDescNote, '') as test_desc
|
||||
FROM ss_price_mou
|
||||
JOIN t_packet ON t_packet.T_PacketID = ss_price_mou.T_TestID AND t_packet.T_PacketName LIKE ?
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
LEFT JOIN t_packet_desc_map ON T_PacketDescMapT_PacketID = ss_price_mou.T_TestID AND T_PacketDescMapIsActive = 'Y'
|
||||
LEFT JOIN t_packet_desc ON T_PacketDescMapT_PacketDescID = t_packet_desc.T_PacketDescID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'Y'
|
||||
ORDER BY ss_price_mou.T_TestName ASC
|
||||
LIMIT ?, ?";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_PACKET', 'price/get_packet_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
if(count($rows) > 0){
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]['child_test'] = json_decode($row['child_test']);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total_records' => $totalCount,
|
||||
'total_pages' => $totalPages
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function insert_log_error($sql, $params){
|
||||
$sql = "INSERT INTO error_log_website(
|
||||
ErrorLogWebsiteCode,
|
||||
ErrorLogWebsiteFnName,
|
||||
ErrorLogWebsiteDescription,
|
||||
ErrorLogWebsiteCreated
|
||||
)
|
||||
VALUES(
|
||||
?,?,?,NOW()
|
||||
)";
|
||||
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan API key dari header Authorization
|
||||
*
|
||||
* @return string|null API key atau null jika tidak ada
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
$headers = null;
|
||||
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER['Authorization']);
|
||||
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$headers = trim($_SERVER['HTTP_AUTHORIZATION']);
|
||||
} else if (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
$requestHeaders = array_combine(
|
||||
array_map('ucwords', array_keys($requestHeaders)),
|
||||
array_values($requestHeaders)
|
||||
);
|
||||
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
|
||||
// Format header biasanya "Bearer {token}"
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi API key
|
||||
*
|
||||
* @param string $apiKey API key untuk divalidasi
|
||||
* @return bool True jika valid, false jika tidak
|
||||
*/
|
||||
private function validateApiKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contoh implementasi validasi:
|
||||
// 1. Cek token di database
|
||||
// 2. Validasi JWT token
|
||||
// 3. Cek api key statis (hanya untuk contoh sederhana)
|
||||
|
||||
try {
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1 AND expired_at > NOW()";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitasi input untuk mencegah SQL injection dan XSS
|
||||
*
|
||||
* @param string $input Input yang akan disanitasi
|
||||
* @return string Input yang sudah disanitasi
|
||||
*/
|
||||
private function sanitizeInput($input) {
|
||||
if (is_string($input)) {
|
||||
// Hapus karakter tidak perlu
|
||||
$input = trim($input);
|
||||
|
||||
// Hilangkan HTML tags
|
||||
$input = strip_tags($input);
|
||||
|
||||
// Konversi special characters ke HTML entities
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim HTTP response dengan JSON
|
||||
*
|
||||
* @param int $statusCode HTTP status code
|
||||
* @param array $data Data yang akan dikirim sebagai JSON
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($statusCode, $data) {
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Tambahkan header keamanan
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header('Content-Security-Policy: default-src \'self\'');
|
||||
|
||||
// Set cache control untuk kontrol caching
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
// Konversi data ke JSON dan kirim
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fungsi untuk menghasilkan API key baru
|
||||
function generateApiKey($userId, $name, $description, $rateLimit = 100, $expiryDays = 365) {
|
||||
try {
|
||||
|
||||
$permissions = 'prices:read';
|
||||
// Generate random token
|
||||
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
|
||||
|
||||
// Set tanggal kadaluarsa (jika ada)
|
||||
$expiredAt = $expiryDays ? date('Y-m-d H:i:s', strtotime("+{$expiryDays} days")) : null;
|
||||
|
||||
// Convert permissions to JSON if it's an array
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Insert ke database
|
||||
$sql = "INSERT INTO api_keys (
|
||||
user_id, api_key, name, description, permissions,
|
||||
rate_limit, is_active, created_by, expired_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
?, 1, ?, ?
|
||||
)";
|
||||
$prm = [$userId, $apiKey, $name, $description, $permissions, $rateLimit, $userId, $expiredAt];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return [
|
||||
'id' => $db->lastInsertId(),
|
||||
'api_key' => $apiKey,
|
||||
'expired_at' => $expiredAt
|
||||
];
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error generating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menonaktifkan API key
|
||||
function deactivateApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_active = 0, updated_at = NOW(), updated_by = ? WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deactivating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menghapus API key (soft delete)
|
||||
function deleteApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_deleted = 1, deleted_at = NOW(), deleted_by = ?, is_active = 0 WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deleting API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fungsi untuk memvalidasi API key dan cek izin
|
||||
function validateApiKeyWithPermission($apiKey, $requiredPermission) {
|
||||
try {
|
||||
$sql = "SELECT id, user_id, permissions, rate_limit, last_used_at FROM api_keys WHERE api_key = ? AND is_active = 1 AND is_deleted = 0 AND (expired_at IS NULL OR expired_at > NOW())";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$apiKeyData = $query->row_array();
|
||||
|
||||
if (!$apiKeyData) {
|
||||
return false; // API key tidak valid atau tidak aktif
|
||||
}
|
||||
|
||||
// Update last_used_at
|
||||
$sql = "UPDATE api_keys SET last_used_at = NOW() WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$apiKeyData['id']];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['UPDATE_API_KEY_LAST_USED', 'price/validateApiKeyWithPermission', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Cek rate limiting (implementasi sederhana)
|
||||
if ($apiKeyData['last_used_at']) {
|
||||
$lastUsed = new DateTime($apiKeyData['last_used_at']);
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $lastUsed->getTimestamp();
|
||||
|
||||
// Jika terakhir digunakan kurang dari 1 detik yang lalu, dan sudah melebihi rate limit
|
||||
// Ini implementasi sederhana, idealnya gunakan sistem rate limiting yang lebih kompleks
|
||||
if ($diff < 1 && $this->countRecentRequests($apiKeyData['id']) >= $apiKeyData['rate_limit']) {
|
||||
return ['error' => 'rate_limit_exceeded'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cek permissions jika ada
|
||||
if ($requiredPermission) {
|
||||
$permissions = json_decode($apiKeyData['permissions'], true);
|
||||
|
||||
// Format permission: {"resource": "action1,action2"}
|
||||
// contoh: {"prices": "read,write"}
|
||||
list($resource, $action) = explode(':', $requiredPermission);
|
||||
|
||||
if (!isset($permissions[$resource]) ||
|
||||
!in_array($action, explode(',', $permissions[$resource]))) {
|
||||
return ['error' => 'permission_denied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $apiKeyData;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
707
application/controllers/website/Price.php--210525
Normal file
707
application/controllers/website/Price.php--210525
Normal file
@@ -0,0 +1,707 @@
|
||||
<?php
|
||||
class Price extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db_onedev = $this->load->database("onedev", true);
|
||||
$this->db_smartone = $this->load->database("onedev", true);
|
||||
|
||||
// Daftar domain yang diizinkan mengakses API
|
||||
$allowedOrigins = [
|
||||
'https://devone.aplikasi.web.id',
|
||||
'https://westerindo.com'
|
||||
// Tambahkan domain lain yang diizinkan di sini
|
||||
];
|
||||
|
||||
// Ambil Origin dari request
|
||||
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
|
||||
|
||||
// Handle preflight request khusus
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
// Cek apakah origin ada dalam daftar yang diizinkan
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
// Izinkan origin spesifik
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
|
||||
// Ambil header yang diminta dari request preflight
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
||||
header('Access-Control-Allow-Headers: ' . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||
} else {
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept, X-Requested-With');
|
||||
}
|
||||
|
||||
header('Access-Control-Max-Age: 86400'); // Cache preflight selama 24 jam
|
||||
header('Content-Length: 0');
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(200);
|
||||
} else {
|
||||
// Origin tidak diizinkan - berikan respons 403
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(403);
|
||||
echo 'Origin not allowed';
|
||||
}
|
||||
exit(0); // Penting untuk menghentikan eksekusi lebih lanjut
|
||||
}
|
||||
|
||||
// Header untuk request normal (non-OPTIONS)
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Methods: GET, POST');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, authorization, Accept');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API handler untuk mendapatkan semua data harga
|
||||
* Implementasi dengan fitur keamanan yang baik
|
||||
*
|
||||
* @return void Output JSON response
|
||||
*/
|
||||
|
||||
function get_branches() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'branches:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT M_BranchCode as branch_code,
|
||||
M_BranchCodeLab as branch_code_lab,
|
||||
M_BranchName as branch_name,
|
||||
M_BranchAddress as branch_address
|
||||
FROM m_branch
|
||||
WHERE M_BranchIsActive = 'Y'
|
||||
ORDER BY M_BranchCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_BRANCHES', 'branch/get_branches', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_category() {
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
|
||||
$sql = "SELECT Nat_SubGroupSasCode as category,
|
||||
Nat_SubGroupFormalName as category_name
|
||||
FROM nat_subgroup
|
||||
WHERE Nat_SubGroupIsActive = 'Y'
|
||||
ORDER BY Nat_SubGroupSasCode ASC";
|
||||
$query = $this->db_onedev->query($sql);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_CATEGORY', 'price/get_category', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'total_records' => count($rows)
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_single_price() {
|
||||
// --- 1. VALIDASI REQUEST DAN KEAMANAN ---
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 2. PARAMETER FILTERING & PAGINATION ---
|
||||
|
||||
// Ambil dan validasi parameter filtering (jika ada)
|
||||
$filters = [];
|
||||
$allowedFilters = ['search','category'];
|
||||
|
||||
foreach ($allowedFilters as $filter) {
|
||||
if (isset($_GET[$filter])) {
|
||||
// Sanitasi input
|
||||
$filters[$filter] = $this->sanitizeInput($_GET[$filter]);
|
||||
}
|
||||
}
|
||||
|
||||
//parameter
|
||||
$search = isset($filters['search']) ? $filters['search'] : '';
|
||||
$category = isset($filters['category']) ? $filters['category'] : '';
|
||||
|
||||
// Pagination
|
||||
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
|
||||
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20; // Default 20, max 100
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// --- 3. QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
$params = [];
|
||||
$params[] = '%'.$search.'%';
|
||||
$filter_category = '';
|
||||
if($category != ''){
|
||||
$filter_category = " AND Nat_SubGroupSasCode = ?";
|
||||
$params[] = $category;
|
||||
}
|
||||
|
||||
$sql = "SELECT COUNT(*) as total
|
||||
FROM `ss_price_mou`
|
||||
JOIN `t_test` test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN `nat_test` ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN `nat_subgroup` ON `Nat_TestNat_SubGroupID` = `Nat_SubGroupID` $filter_category
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'N'";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_SINGLE', 'price/get_single_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$totalCount = $query->row_array()['total'];
|
||||
$totalPages = ceil($totalCount / $limit);
|
||||
|
||||
$params[] = $offset;
|
||||
$params[] = $limit;
|
||||
$sql = "SELECT Mgm_McuNumber as project_number,
|
||||
ss_price_mou.Ss_PriceMouID as x_id,
|
||||
test.T_TestID as test_id,
|
||||
nat_test.Nat_TestID as test_nat_id,
|
||||
test.T_TestName as test_name,
|
||||
test.T_TestSasCode as test_sas_code,
|
||||
Nat_SubGroupName as category,
|
||||
ss_price_mou.T_TestRequirement as test_requirement,
|
||||
ss_price_mou.T_PriceAmount as price,
|
||||
ss_price_mou.T_PriceDisc as disc,
|
||||
ss_price_mou.T_PriceDiscRp as disc_rp,
|
||||
ss_price_mou.T_PriceSubTotal as subtotal,
|
||||
ss_price_mou.T_PriceTotal as total,
|
||||
ss_price_mou.px_type as px_type,
|
||||
ss_price_mou.nat_test as nat_tests,
|
||||
ss_price_mou.child_test as child_test,
|
||||
ss_price_mou.T_PriceT_PriceHeaderID as price_header_id,
|
||||
IFNULL(nat_test_desc.Nat_TestDescNote, '') as test_desc
|
||||
FROM ss_price_mou
|
||||
JOIN t_test test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN nat_test ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN nat_subgroup ON Nat_TestNat_SubGroupID = Nat_SubGroupID $filter_category
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
LEFT JOIN nat_test_desc ON nat_test_desc.Nat_TestDescNat_TestID = nat_test.Nat_TestID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'N'
|
||||
ORDER BY ss_price_mou.T_TestName ASC
|
||||
LIMIT ?, ?";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_SINGLE', 'price/get_single_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
if(count($rows) > 0){
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]['child_test'] = json_decode($row['child_test']);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total_records' => $totalCount,
|
||||
'total_pages' => $totalPages
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_packet_price() {
|
||||
// --- 1. VALIDASI REQUEST DAN KEAMANAN ---
|
||||
|
||||
// Pastikan hanya menerima request GET
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
$this->sendResponse(405, ['error' => 'Method Not Allowed', 'message' => 'Hanya metode GET yang diizinkan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validasi API key/token
|
||||
$apiKey = $this->getAuthorizationHeader();
|
||||
if (!$this->validateApiKeyWithPermission($apiKey, 'prices:read')) {
|
||||
$this->sendResponse(401, ['error' => 'Unauthorized', 'message' => 'API key tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 2. PARAMETER FILTERING & PAGINATION ---
|
||||
|
||||
// Ambil dan validasi parameter filtering (jika ada)
|
||||
$filters = [];
|
||||
$allowedFilters = ['search'];
|
||||
|
||||
foreach ($allowedFilters as $filter) {
|
||||
if (isset($_GET[$filter])) {
|
||||
// Sanitasi input
|
||||
$filters[$filter] = $this->sanitizeInput($_GET[$filter]);
|
||||
}
|
||||
}
|
||||
|
||||
//parameter
|
||||
$search = isset($filters['search']) ? $filters['search'] : '';
|
||||
|
||||
// Pagination
|
||||
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
|
||||
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20; // Default 20, max 100
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// --- 3. QUERY DATABASE DENGAN AMAN ---
|
||||
try {
|
||||
$params = [];
|
||||
$params[] = '%'.$search.'%';
|
||||
|
||||
$sql = "SELECT COUNT(*) as total
|
||||
FROM `ss_price_mou`
|
||||
JOIN `t_test` test ON test.T_TestID = ss_price_mou.T_TestID AND test.T_TestName LIKE ?
|
||||
JOIN `nat_test` ON nat_test.Nat_TestID = test.T_TestNat_TestID
|
||||
JOIN `nat_subgroup` ON `Nat_TestNat_SubGroupID` = `Nat_SubGroupID`
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'Y'";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_PACKET', 'price/get_packet_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$totalCount = $query->row_array()['total'];
|
||||
$totalPages = ceil($totalCount / $limit);
|
||||
|
||||
$params[] = $offset;
|
||||
$params[] = $limit;
|
||||
$sql = "SELECT
|
||||
Mgm_McuNumber as project_number,
|
||||
ss_price_mou.Ss_PriceMouID as x_id,
|
||||
t_packet.T_PacketID as test_id,
|
||||
0 as test_nat_id,
|
||||
ss_price_mou.T_TestName as test_name,
|
||||
t_packet.T_PacketSasCode as test_sas_code,
|
||||
'packet' as category,
|
||||
ss_price_mou.T_TestRequirement as test_requirement,
|
||||
ss_price_mou.T_PriceAmount as price,
|
||||
ss_price_mou.T_PriceDisc as disc,
|
||||
ss_price_mou.T_PriceDiscRp as disc_rp,
|
||||
ss_price_mou.T_PriceSubTotal as subtotal,
|
||||
ss_price_mou.T_PriceTotal as total,
|
||||
ss_price_mou.px_type as px_type,
|
||||
ss_price_mou.nat_test as nat_tests,
|
||||
ss_price_mou.child_test as child_test,
|
||||
ss_price_mou.T_PriceT_PriceHeaderID as price_header_id,
|
||||
IFNULL(T_PacketDescNote, '') as test_desc
|
||||
FROM ss_price_mou
|
||||
JOIN t_packet ON t_packet.T_PacketID = ss_price_mou.T_TestID AND t_packet.T_PacketName LIKE ?
|
||||
JOIN mgm_mcu ON mgm_mcu.Mgm_McuT_PriceHeaderID = ss_price_mou.T_PriceT_PriceHeaderID
|
||||
JOIN config_website ON config_website.configWebsiteMgm_McuID = mgm_mcu.Mgm_McuID
|
||||
LEFT JOIN t_packet_desc_map ON T_PacketDescMapT_PacketID = ss_price_mou.T_TestID AND T_PacketDescMapIsActive = 'Y'
|
||||
LEFT JOIN t_packet_desc ON T_PacketDescMapT_PacketDescID = t_packet_desc.T_PacketDescID
|
||||
WHERE
|
||||
ss_price_mou.is_packet = 'Y'
|
||||
ORDER BY ss_price_mou.T_TestName ASC
|
||||
LIMIT ?, ?";
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['GET_PRICE_PACKET', 'price/get_packet_price', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
return;
|
||||
}
|
||||
$rows = $query->result_array();
|
||||
if(count($rows) > 0){
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]['child_test'] = json_decode($row['child_test']);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'data' => $rows,
|
||||
'meta' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total_records' => $totalCount,
|
||||
'total_pages' => $totalPages
|
||||
]
|
||||
];
|
||||
|
||||
// --- 5. KIRIM RESPONSE ---
|
||||
$this->sendResponse(200, $response);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log error tapi jangan tampilkan detail ke user
|
||||
error_log('Database error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan saat mengambil data']);
|
||||
} catch (Exception $e) {
|
||||
error_log('API error: ' . $e->getMessage());
|
||||
$this->sendResponse(500, ['error' => 'Internal Server Error', 'message' => 'Terjadi kesalahan dalam pemrosesan']);
|
||||
}
|
||||
}
|
||||
|
||||
function insert_log_error($sql, $params){
|
||||
$sql = "INSERT INTO error_log_website(
|
||||
ErrorLogWebsiteCode,
|
||||
ErrorLogWebsiteFnName,
|
||||
ErrorLogWebsiteDescription,
|
||||
ErrorLogWebsiteCreated
|
||||
)
|
||||
VALUES(
|
||||
?,?,?,NOW()
|
||||
)";
|
||||
|
||||
$query = $this->db_onedev->query($sql, $params);
|
||||
if(!$query){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan API key dari header Authorization
|
||||
*
|
||||
* @return string|null API key atau null jika tidak ada
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
$headers = null;
|
||||
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER['Authorization']);
|
||||
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$headers = trim($_SERVER['HTTP_AUTHORIZATION']);
|
||||
} else if (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
$requestHeaders = array_combine(
|
||||
array_map('ucwords', array_keys($requestHeaders)),
|
||||
array_values($requestHeaders)
|
||||
);
|
||||
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
|
||||
// Format header biasanya "Bearer {token}"
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi API key
|
||||
*
|
||||
* @param string $apiKey API key untuk divalidasi
|
||||
* @return bool True jika valid, false jika tidak
|
||||
*/
|
||||
private function validateApiKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contoh implementasi validasi:
|
||||
// 1. Cek token di database
|
||||
// 2. Validasi JWT token
|
||||
// 3. Cek api key statis (hanya untuk contoh sederhana)
|
||||
|
||||
try {
|
||||
$sql = "SELECT * FROM api_keys WHERE api_key = ? AND is_active = 1 AND expired_at > NOW()";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitasi input untuk mencegah SQL injection dan XSS
|
||||
*
|
||||
* @param string $input Input yang akan disanitasi
|
||||
* @return string Input yang sudah disanitasi
|
||||
*/
|
||||
private function sanitizeInput($input) {
|
||||
if (is_string($input)) {
|
||||
// Hapus karakter tidak perlu
|
||||
$input = trim($input);
|
||||
|
||||
// Hilangkan HTML tags
|
||||
$input = strip_tags($input);
|
||||
|
||||
// Konversi special characters ke HTML entities
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim HTTP response dengan JSON
|
||||
*
|
||||
* @param int $statusCode HTTP status code
|
||||
* @param array $data Data yang akan dikirim sebagai JSON
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($statusCode, $data) {
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Tambahkan header keamanan
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
header('Content-Security-Policy: default-src \'self\'');
|
||||
|
||||
// Set cache control untuk kontrol caching
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
// Konversi data ke JSON dan kirim
|
||||
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fungsi untuk menghasilkan API key baru
|
||||
function generateApiKey($userId, $name, $description, $rateLimit = 100, $expiryDays = 365) {
|
||||
try {
|
||||
|
||||
$permissions = 'prices:read';
|
||||
// Generate random token
|
||||
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
|
||||
|
||||
// Set tanggal kadaluarsa (jika ada)
|
||||
$expiredAt = $expiryDays ? date('Y-m-d H:i:s', strtotime("+{$expiryDays} days")) : null;
|
||||
|
||||
// Convert permissions to JSON if it's an array
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Insert ke database
|
||||
$sql = "INSERT INTO api_keys (
|
||||
user_id, api_key, name, description, permissions,
|
||||
rate_limit, is_active, created_by, expired_at
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?,
|
||||
?, 1, ?, ?
|
||||
)";
|
||||
$prm = [$userId, $apiKey, $name, $description, $permissions, $rateLimit, $userId, $expiredAt];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return [
|
||||
'id' => $db->lastInsertId(),
|
||||
'api_key' => $apiKey,
|
||||
'expired_at' => $expiredAt
|
||||
];
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error generating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menonaktifkan API key
|
||||
function deactivateApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_active = 0, updated_at = NOW(), updated_by = ? WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deactivating API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menghapus API key (soft delete)
|
||||
function deleteApiKey($apiKeyId, $userId) {
|
||||
try {
|
||||
$sql = "UPDATE api_keys SET is_deleted = 1, deleted_at = NOW(), deleted_by = ?, is_active = 0 WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$userId, $apiKeyId];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$data = $query->row_array();
|
||||
if ($data) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('Error deleting API key: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fungsi untuk memvalidasi API key dan cek izin
|
||||
function validateApiKeyWithPermission($apiKey, $requiredPermission) {
|
||||
try {
|
||||
$sql = "SELECT id, user_id, permissions, rate_limit, last_used_at FROM api_keys WHERE api_key = ? AND is_active = 1 AND is_deleted = 0 AND (expired_at IS NULL OR expired_at > NOW())";
|
||||
$prm = [$apiKey];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
$apiKeyData = $query->row_array();
|
||||
|
||||
if (!$apiKeyData) {
|
||||
return false; // API key tidak valid atau tidak aktif
|
||||
}
|
||||
|
||||
// Update last_used_at
|
||||
$sql = "UPDATE api_keys SET last_used_at = NOW() WHERE id = ? AND is_deleted = 0";
|
||||
$prm = [$apiKeyData['id']];
|
||||
$query = $this->db_onedev->query($sql, $prm);
|
||||
|
||||
if(!$query){
|
||||
$prm_log = ['UPDATE_API_KEY_LAST_USED', 'price/validateApiKeyWithPermission', $this->db_onedev->last_query()];
|
||||
$log_error = $this->insert_log_error($sql, $prm_log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Cek rate limiting (implementasi sederhana)
|
||||
if ($apiKeyData['last_used_at']) {
|
||||
$lastUsed = new DateTime($apiKeyData['last_used_at']);
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $lastUsed->getTimestamp();
|
||||
|
||||
// Jika terakhir digunakan kurang dari 1 detik yang lalu, dan sudah melebihi rate limit
|
||||
// Ini implementasi sederhana, idealnya gunakan sistem rate limiting yang lebih kompleks
|
||||
if ($diff < 1 && $this->countRecentRequests($apiKeyData['id']) >= $apiKeyData['rate_limit']) {
|
||||
return ['error' => 'rate_limit_exceeded'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cek permissions jika ada
|
||||
if ($requiredPermission) {
|
||||
$permissions = json_decode($apiKeyData['permissions'], true);
|
||||
|
||||
// Format permission: {"resource": "action1,action2"}
|
||||
// contoh: {"prices": "read,write"}
|
||||
list($resource, $action) = explode(':', $requiredPermission);
|
||||
|
||||
if (!isset($permissions[$resource]) ||
|
||||
!in_array($action, explode(',', $permissions[$resource]))) {
|
||||
return ['error' => 'permission_denied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $apiKeyData;
|
||||
} catch (Exception $e) {
|
||||
error_log('API key validation error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
317
application/controllers/website/order.http
Normal file
317
application/controllers/website/order.http
Normal file
@@ -0,0 +1,317 @@
|
||||
|
||||
### Variabel Global
|
||||
@baseUrlOrder = https://cpone.aplikasi.web.id/one-api/website/order/
|
||||
@apiKey = 7a9b8c7d6e5f4g3h2i1j0k9l8m7n6o5p4q3r2s1t0u9v8w7x6y5z4a3b2c1d0
|
||||
|
||||
### Save Order Single - POST Request
|
||||
#Complete data
|
||||
# Permission yang dibutuhkan: order:save
|
||||
POST {{baseUrlOrder}}save
|
||||
Authorization: Bearer {{apiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
{
|
||||
"trx_date": "2025-06-01",
|
||||
"trx_time": "10:30:00",
|
||||
"type_order": "HOME_SERVICE",
|
||||
"branch_code_lab": "R",
|
||||
"diagnose": "",
|
||||
"fo_note": "",
|
||||
"patient": {
|
||||
"name": "Nina Nani",
|
||||
"nik": "1114512191123456",
|
||||
"gender": "FEMALE",
|
||||
"birthdate": "1989-01-01",
|
||||
"phone": "081234527890",
|
||||
"email": "nina.nani@example.com",
|
||||
"job": "",
|
||||
"nip": "",
|
||||
"job_departement": "",
|
||||
"job_division": "",
|
||||
"job_position": "",
|
||||
"job_location": "",
|
||||
"address": "Jl. Semeru No. 123",
|
||||
"pro_cd": "31",
|
||||
"kab_cd": "71",
|
||||
"kec_cd": "020",
|
||||
"kel_cd": "002",
|
||||
"rt": "001",
|
||||
"rw": "002"
|
||||
},
|
||||
"details": [
|
||||
{
|
||||
"project_number": "MGM240700009",
|
||||
"x_id": "804",
|
||||
"test_id": "952",
|
||||
"test_nat_id": "4669",
|
||||
"test_name": "HDL Cholesterol",
|
||||
"test_sas_code": "10520500",
|
||||
"category": "Kimia Klinik",
|
||||
"test_requirement": null,
|
||||
"price": "30000",
|
||||
"disc": "0",
|
||||
"disc_rp": "0",
|
||||
"subtotal": "30000",
|
||||
"total": "30000",
|
||||
"px_type": "PX",
|
||||
"nat_tests": "[4669]",
|
||||
"child_test": [],
|
||||
"price_header_id": "57",
|
||||
"test_desc": ""
|
||||
},
|
||||
{
|
||||
"project_number": "MGM240700009",
|
||||
"x_id": "784",
|
||||
"test_id": "1",
|
||||
"test_nat_id": "4095",
|
||||
"test_name": "Hematologi Lengkap",
|
||||
"test_sas_code": "10110100",
|
||||
"category": "",
|
||||
"test_requirement": null,
|
||||
"price": "65000",
|
||||
"disc": "0",
|
||||
"disc_rp": "0",
|
||||
"subtotal": "65000",
|
||||
"total": "65000",
|
||||
"px_type": "PX",
|
||||
"nat_tests": "[4095,4098,4099,4101,4102,4103,4105,4106,4107,4108,4109,4110,4111,4112,4113,4114,4117,4116]",
|
||||
"child_test": [],
|
||||
"price_header_id": "57",
|
||||
"test_desc": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
### Save Order Paket - POST Request
|
||||
# minimal data
|
||||
# Permission yang dibutuhkan: order:save
|
||||
POST {{baseUrlOrder}}save
|
||||
Authorization: Bearer {{apiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"trx_date": "2025-08-26",
|
||||
"trx_time": "10:00:00",
|
||||
"branch_code_lab": "R",
|
||||
"patient": {
|
||||
"name": "Nina Nani",
|
||||
"nik": "1114512191123456",
|
||||
"gender": "FEMALE",
|
||||
"birthdate": "1989-01-01",
|
||||
"phone": "081234527890",
|
||||
"email": "nina.nani@example.com",
|
||||
"address": "Jl. Semeru No. 123",
|
||||
"pro_cd": "31",
|
||||
"kab_cd": "71",
|
||||
"kec_cd": "020",
|
||||
"kel_cd": "002"
|
||||
},
|
||||
"details": [
|
||||
{
|
||||
"x_id": "7614",
|
||||
"test_id": "936",
|
||||
"test_nat_id": "0",
|
||||
"test_name": "PAKET TENSI",
|
||||
"test_sas_code": "PN2500374",
|
||||
"category": "packet",
|
||||
"test_requirement": null,
|
||||
"price": "210000",
|
||||
"disc": "0",
|
||||
"disc_rp": "0",
|
||||
"subtotal": "0",
|
||||
"total": "210000",
|
||||
"px_type": "PN",
|
||||
"nat_tests": "[4634,4633,4095,4098,4099,4100,4101,4102,4103,4105,4106,4107,4108,4109,4110,4111,4112,4113,4114,4116,4117,4331,4334,4335,4336,4337,4338,4339,4340,4341,4342,4344,4345,4346,4347,4348,4349,4350,4351,4352,10997,10998,6236]",
|
||||
"child_test": [
|
||||
{
|
||||
"T_PriceT_PriceHeaderID": "645",
|
||||
"T_TestID": "895",
|
||||
"T_TestName": "SGPT",
|
||||
"IsFromPanel": "N",
|
||||
"Nat_TestID": "4634",
|
||||
"T_PriceT_TestID": "895",
|
||||
"T_PriceIsCito": "N",
|
||||
"T_PricePriority": "0",
|
||||
"T_PriceAmount": "30000",
|
||||
"T_PriceDisc": "0",
|
||||
"T_PriceDiscRp": "0",
|
||||
"T_PriceSubTotal": "30000",
|
||||
"T_PriceOther": "0",
|
||||
"T_PriceTotal": "30000",
|
||||
"T_TestForceSell": "Y",
|
||||
"is_packet": "N",
|
||||
"packet_id": "0",
|
||||
"px_type": "PX",
|
||||
"nat_test": "[4634]",
|
||||
"child_test": "[]",
|
||||
"IsFavourite": "N",
|
||||
"T_TestSasCode": "10510200"
|
||||
},
|
||||
{
|
||||
"T_PriceT_PriceHeaderID": "645",
|
||||
"T_TestID": "894",
|
||||
"T_TestName": "SGOT",
|
||||
"IsFromPanel": "N",
|
||||
"Nat_TestID": "4633",
|
||||
"T_PriceT_TestID": "894",
|
||||
"T_PriceIsCito": "N",
|
||||
"T_PricePriority": "0",
|
||||
"T_PriceAmount": "30000",
|
||||
"T_PriceDisc": "0",
|
||||
"T_PriceDiscRp": "0",
|
||||
"T_PriceSubTotal": "30000",
|
||||
"T_PriceOther": "0",
|
||||
"T_PriceTotal": "30000",
|
||||
"T_TestForceSell": "Y",
|
||||
"is_packet": "N",
|
||||
"packet_id": "0",
|
||||
"px_type": "PX",
|
||||
"nat_test": "[4633]",
|
||||
"child_test": "[]",
|
||||
"IsFavourite": "N",
|
||||
"T_TestSasCode": "10510100"
|
||||
},
|
||||
{
|
||||
"T_PriceT_PriceHeaderID": "645",
|
||||
"T_TestID": "1",
|
||||
"T_TestName": "Hematologi Lengkap",
|
||||
"IsFromPanel": "N",
|
||||
"Nat_TestID": "4095",
|
||||
"T_PriceT_TestID": "1",
|
||||
"T_PriceIsCito": "N",
|
||||
"T_PricePriority": "0",
|
||||
"T_PriceAmount": "65000",
|
||||
"T_PriceDisc": "0",
|
||||
"T_PriceDiscRp": "0",
|
||||
"T_PriceSubTotal": "65000",
|
||||
"T_PriceOther": "0",
|
||||
"T_PriceTotal": "65000",
|
||||
"T_TestForceSell": "Y",
|
||||
"is_packet": "N",
|
||||
"packet_id": "0",
|
||||
"px_type": "PX",
|
||||
"nat_test": "[4095,4098,4099,4100,4101,4102,4103,4105,4106,4107,4108,4109,4110,4111,4112,4113,4114,4116,4117]",
|
||||
"child_test": "[]",
|
||||
"IsFavourite": "N",
|
||||
"T_TestSasCode": "10110100"
|
||||
},
|
||||
{
|
||||
"T_PriceT_PriceHeaderID": "645",
|
||||
"T_TestID": "448",
|
||||
"T_TestName": "Urine Lengkap",
|
||||
"IsFromPanel": "N",
|
||||
"Nat_TestID": "4331",
|
||||
"T_PriceT_TestID": "448",
|
||||
"T_PriceIsCito": "N",
|
||||
"T_PricePriority": "0",
|
||||
"T_PriceAmount": "35000",
|
||||
"T_PriceDisc": "0",
|
||||
"T_PriceDiscRp": "0",
|
||||
"T_PriceSubTotal": "35000",
|
||||
"T_PriceOther": "0",
|
||||
"T_PriceTotal": "35000",
|
||||
"T_TestForceSell": "Y",
|
||||
"is_packet": "N",
|
||||
"packet_id": "0",
|
||||
"px_type": "PX",
|
||||
"nat_test": "[4331,4334,4335,4336,4337,4338,4339,4340,4341,4342,4344,4345,4346,4347,4348,4349,4350,4351,4352,10997,10998]",
|
||||
"child_test": "[]",
|
||||
"IsFavourite": "N",
|
||||
"T_TestSasCode": "10210100"
|
||||
},
|
||||
{
|
||||
"T_PriceT_PriceHeaderID": "645",
|
||||
"T_TestID": "2562",
|
||||
"T_TestName": "Pemeriksaan Fisik",
|
||||
"IsFromPanel": "N",
|
||||
"Nat_TestID": "6236",
|
||||
"T_PriceT_TestID": "2562",
|
||||
"T_PriceIsCito": "N",
|
||||
"T_PricePriority": "0",
|
||||
"T_PriceAmount": "50000",
|
||||
"T_PriceDisc": "0",
|
||||
"T_PriceDiscRp": "0",
|
||||
"T_PriceSubTotal": "50000",
|
||||
"T_PriceOther": "0",
|
||||
"T_PriceTotal": "50000",
|
||||
"T_TestForceSell": "Y",
|
||||
"is_packet": "N",
|
||||
"packet_id": "0",
|
||||
"px_type": "PX",
|
||||
"nat_test": "[6236]",
|
||||
"child_test": "[]",
|
||||
"IsFavourite": "N",
|
||||
"T_TestSasCode": "40110100"
|
||||
}
|
||||
],
|
||||
"price_header_id": "645",
|
||||
"test_desc": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
### Save Order - Invalid Request (Missing Required Fields)
|
||||
# Testing validation error handling
|
||||
POST {{baseUrlOrder}}save
|
||||
Authorization: Bearer {{apiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"trx_date": "2023-09-25",
|
||||
"patient": {
|
||||
"name": "Invalid Patient"
|
||||
},
|
||||
"details": []
|
||||
}
|
||||
|
||||
### Save Order with Invalid Authorization
|
||||
# Testing unauthorized access
|
||||
POST {{baseUrlOrder}}save
|
||||
Authorization: Bearer {{apiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"trx_date": "2025-08-26",
|
||||
"trx_time": "10:30:00",
|
||||
"type_order": "WALK_IN",
|
||||
"branch_code_lab": "R",
|
||||
"diagnose": "",
|
||||
"fo_note": "",
|
||||
"patient": {
|
||||
"name": "Nina Nani Ninu",
|
||||
"nik": "1114512191123456",
|
||||
"gender": "FEMALE",
|
||||
"birthdate": "1989-01-01",
|
||||
"phone": "081234527890",
|
||||
"email": "nina.nani@example.com",
|
||||
"address": "Jl. Sudirman No. 123",
|
||||
"pro_cd": "01",
|
||||
"kab_cd": "01",
|
||||
"kec_cd": "001",
|
||||
"kel_cd": "001"
|
||||
},
|
||||
"details": [
|
||||
{
|
||||
"x_id": "7593",
|
||||
"test_id": "952",
|
||||
"test_nat_id": "4669",
|
||||
"test_name": "HDL Cholesterol",
|
||||
"test_sas_code": "10520500",
|
||||
"category": "Kimia Klinik",
|
||||
"test_requirement": null,
|
||||
"price": "30000",
|
||||
"disc": "0",
|
||||
"disc_rp": "0",
|
||||
"subtotal": "30000",
|
||||
"total": "30000",
|
||||
"px_type": "PX",
|
||||
"nat_tests": "[4669]",
|
||||
"child_test": [],
|
||||
"price_header_id": "645",
|
||||
"test_desc": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
39
application/controllers/website/patient.http
Normal file
39
application/controllers/website/patient.http
Normal file
@@ -0,0 +1,39 @@
|
||||
### Variabel Global
|
||||
@baseUrl = https://cpone.aplikasi.web.id/one-api/website/patient/
|
||||
@adminApiKey = 7a9b8c7d6e5f4g3h2i1j0k9l8m7n6o5p4q3r2s1t0u9v8w7x6y5z4a3b2c1d0
|
||||
@userApiKey = 7a9b8c7d6e5f4g3h2i1j0k9l8m7n6o5p4q3r2s1t0u9v8w7x6y5z4a3b2c1d0
|
||||
|
||||
### 1. Get Province - Contoh mencari "JAWA"
|
||||
GET {{baseUrl}}get_regional?type=province&search=JAWA
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 1. Get Province
|
||||
GET {{baseUrl}}get_regional?type=province&search=
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 2. Get Kabupaten - Contoh untuk DKI Jakarta (31)
|
||||
GET {{baseUrl}}get_regional?type=kabupaten&province_id=31
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 2.1. Get Kecamatan - Contoh untuk DKI Jakarta, Kota Jakarta Selatan
|
||||
GET {{baseUrl}}get_regional?type=kabupaten&province_id=31&kabupaten_id=71
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 3. Get Kecamatan - Contoh untuk DKI Jakarta, Kota Jakarta Selatan
|
||||
GET {{baseUrl}}get_regional?type=kecamatan&province_id=31&kabupaten_id=71
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 3.1. Get Kecamatan - Contoh untuk DKI Jakarta, Kota Jakarta Selatan, Pasar Minggu
|
||||
GET {{baseUrl}}get_regional?type=kecamatan&province_id=31&kabupaten_id=71&kecamatan_id=020
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 4. Get Kelurahan - Contoh untuk DKI Jakarta, Kota Jakarta Selatan, Pasar Minggu
|
||||
GET {{baseUrl}}get_regional?type=kelurahan&province_id=31&kabupaten_id=71&kecamatan_id=020
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 4.1. Get Kelurahan - Contoh untuk DKI Jakarta, Kota Jakarta Selatan, Pasar Minggu, Ragunan
|
||||
GET {{baseUrl}}get_regional?type=kelurahan&province_id=31&kabupaten_id=71&kecamatan_id=020&kelurahan_id=002
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
|
||||
|
||||
371
application/controllers/website/prices.http
Normal file
371
application/controllers/website/prices.http
Normal file
@@ -0,0 +1,371 @@
|
||||
### Variabel Global
|
||||
@baseUrl = https://cpone.aplikasi.web.id/one-api/website/price/
|
||||
@adminApiKey = 7a9b8c7d6e5f4g3h2i1j0k9l8m7n6o5p4q3r2s1t0u9v8w7x6y5z4a3b2c1d0
|
||||
@userApiKey = 7a9b8c7d6e5f4g3h2i1j0k9l8m7n6o5p4q3r2s1t0u9v8w7x6y5z4a3b2c1d0
|
||||
|
||||
### 1. GET Semua Data Cabang
|
||||
GET {{baseUrl}}/get_branches
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 1. GET Semua Data Kategori
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/get_category
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 2. GET Data Harga Single
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/get_single_price?limit=20&page=1
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 2.1. GET Data Harga Single dengan Filter Kategori
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/get_single_price?limit=20&page=1&category=A05
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 2.2. GET Data Harga Single dengan Filter Kategori dan Search
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/get_single_price?limit=20&page=1&category=01&search=hema
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 3. GET Data Harga Packet
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/get_packet_price?limit=20&page=1
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 4. POST Menambahkan Data Harga Baru
|
||||
# Permission yang dibutuhkan: prices:write
|
||||
POST {{baseUrl}}/prices
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"name": "Smartphone XYZ",
|
||||
"price": 2500000,
|
||||
"category": "elektronik"
|
||||
}
|
||||
|
||||
### 5. PUT Memperbarui Data Harga
|
||||
# Permission yang dibutuhkan: prices:write
|
||||
PUT {{baseUrl}}/prices/1
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"name": "Smartphone XYZ Updated",
|
||||
"price": 2750000,
|
||||
"category": "elektronik"
|
||||
}
|
||||
|
||||
### 6. PATCH Memperbarui Sebagian Data Harga
|
||||
# Permission yang dibutuhkan: prices:write
|
||||
PATCH {{baseUrl}}/prices/1
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"price": 2650000
|
||||
}
|
||||
|
||||
### 7. DELETE Menghapus Data Harga
|
||||
# Permission yang dibutuhkan: prices:delete
|
||||
DELETE {{baseUrl}}/prices/1
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 8. Mencoba Akses dengan API Key Tidak Valid
|
||||
GET {{baseUrl}}/prices
|
||||
Authorization: Bearer invalid_api_key
|
||||
Accept: application/json
|
||||
|
||||
### 9. Mencoba Akses Tanpa API Key
|
||||
GET {{baseUrl}}/prices
|
||||
Accept: application/json
|
||||
|
||||
### 10. Mencoba Akses dengan API Key yang Tidak Memiliki Permission
|
||||
# API key ini diasumsikan tidak memiliki permission prices:delete
|
||||
DELETE {{baseUrl}}/prices/1
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 11. Testing Rate Limit
|
||||
# Mengirim beberapa request berturut-turut dengan cepat untuk menguji rate limiting
|
||||
GET {{baseUrl}}/prices
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
GET {{baseUrl}}/prices
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
GET {{baseUrl}}/prices
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 12. Mencoba Mencari Data dengan Query Search
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/prices?search=smartphone
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 13. Mencoba Sorting Data
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/prices?sort=price&order=desc
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 14. Testing API Key dengan Permission Produk
|
||||
# Permission yang dibutuhkan: products:read
|
||||
GET {{baseUrl}}/products?limit=10&offset=0
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 15. Testing API Key dengan Permission Batch (Bulk)
|
||||
# Permission yang dibutuhkan: prices:write
|
||||
POST {{baseUrl}}/prices/batch
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"name": "Produk 1",
|
||||
"price": 100000,
|
||||
"category": "elektronik"
|
||||
},
|
||||
{
|
||||
"name": "Produk 2",
|
||||
"price": 200000,
|
||||
"category": "elektronik"
|
||||
},
|
||||
{
|
||||
"name": "Produk 3",
|
||||
"price": 300000,
|
||||
"category": "elektronik"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
### 16. Testing API Key dengan Filtering Lebih Kompleks
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/prices?category=elektronik&min_price=100000&max_price=5000000&sort=price&order=asc
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 17. Testing Export Data
|
||||
# Permission yang dibutuhkan: prices:export
|
||||
GET {{baseUrl}}/prices/export?format=csv
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/octet-stream
|
||||
|
||||
### 18. Testing dengan Format XML (Jika API Mendukung)
|
||||
# Permission yang dibutuhkan: prices:read
|
||||
GET {{baseUrl}}/prices?limit=10&offset=0
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/xml
|
||||
|
||||
### 19. Testing API Key dengan Permission Batch (Bulk)
|
||||
# Permission yang dibutuhkan: prices:write
|
||||
POST {{baseUrl}}/prices/batch
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
|
||||
### 1. Generate API Key Baru (Admin Permission)
|
||||
# Permission yang dibutuhkan: api_keys:create
|
||||
POST {{baseUrl}}/api-keys/generate
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"name": "API Key untuk Mobile App",
|
||||
"user_id": 123,
|
||||
"permissions": {
|
||||
"prices": "read",
|
||||
"products": "read",
|
||||
"cart": "read,write",
|
||||
"orders": "read,write"
|
||||
},
|
||||
"expires_in_days": 365,
|
||||
"rate_limit": 100,
|
||||
"description": "API Key untuk aplikasi mobile dengan akses terbatas"
|
||||
}
|
||||
|
||||
### 2. Generate API Key dengan Full Access (Super Admin)
|
||||
# Permission yang dibutuhkan: api_keys:create
|
||||
POST {{baseUrl}}/api-keys/generate
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"name": "Full Access API Key",
|
||||
"user_id": 1,
|
||||
"permissions": {
|
||||
"*": "*"
|
||||
},
|
||||
"expires_in_days": null,
|
||||
"rate_limit": 1000,
|
||||
"description": "API Key dengan akses penuh untuk administrator sistem"
|
||||
}
|
||||
|
||||
### 3. Generate API Key dengan Permission Spesifik
|
||||
# Permission yang dibutuhkan: api_keys:create
|
||||
POST {{baseUrl}}/api-keys/generate
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"name": "Analytics API Key",
|
||||
"user_id": 456,
|
||||
"permissions": {
|
||||
"prices": "read",
|
||||
"products": "read",
|
||||
"reports": "read",
|
||||
"statistics": "read"
|
||||
},
|
||||
"expires_in_days": 180,
|
||||
"rate_limit": 500,
|
||||
"description": "API Key untuk keperluan analisis data"
|
||||
}
|
||||
|
||||
### 4. Generate API Key untuk Partner External
|
||||
# Permission yang dibutuhkan: api_keys:create
|
||||
POST {{baseUrl}}/api-keys/generate
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"name": "Partner Integration API Key",
|
||||
"user_id": 789,
|
||||
"permissions": {
|
||||
"prices": {
|
||||
"actions": ["read"],
|
||||
"conditions": {
|
||||
"categories": [1, 2, 3],
|
||||
"visibility": "public"
|
||||
}
|
||||
},
|
||||
"products": {
|
||||
"actions": ["read"],
|
||||
"conditions": {
|
||||
"visibility": "public"
|
||||
}
|
||||
},
|
||||
"orders": {
|
||||
"actions": ["read-limited", "create"],
|
||||
"conditions": {
|
||||
"partner_id": 789
|
||||
}
|
||||
}
|
||||
},
|
||||
"expires_in_days": 90,
|
||||
"rate_limit": 200,
|
||||
"description": "API Key untuk integrasi dengan partner dengan akses terbatas"
|
||||
}
|
||||
|
||||
### 5. Dapatkan Daftar API Key yang Aktif (Admin Only)
|
||||
# Permission yang dibutuhkan: api_keys:read
|
||||
GET {{baseUrl}}/api-keys
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 6. Dapatkan Detail API Key Tertentu
|
||||
# Permission yang dibutuhkan: api_keys:read
|
||||
GET {{baseUrl}}/api-keys/123
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 7. Menonaktifkan API Key
|
||||
# Permission yang dibutuhkan: api_keys:write
|
||||
PUT {{baseUrl}}/api-keys/123/deactivate
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 8. Memperbarui Permission API Key
|
||||
# Permission yang dibutuhkan: api_keys:write
|
||||
PUT {{baseUrl}}/api-keys/123/permissions
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"permissions": {
|
||||
"prices": "read,write",
|
||||
"products": "read",
|
||||
"orders": "read,write,delete"
|
||||
}
|
||||
}
|
||||
|
||||
### 9. Memperpanjang Masa Berlaku API Key
|
||||
# Permission yang dibutuhkan: api_keys:write
|
||||
PUT {{baseUrl}}/api-keys/123/extend
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"additional_days": 180
|
||||
}
|
||||
|
||||
### 10. Menghapus API Key (Soft Delete)
|
||||
# Permission yang dibutuhkan: api_keys:delete
|
||||
DELETE {{baseUrl}}/api-keys/123
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 11. Mendapatkan API Key Milik User Tertentu
|
||||
# Permission yang dibutuhkan: api_keys:read
|
||||
GET {{baseUrl}}/api-keys/user/456
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 12. Mendapatkan API Key Milik User Saat Ini
|
||||
# User dapat melihat API key miliknya sendiri
|
||||
GET {{baseUrl}}/api-keys/me
|
||||
Authorization: Bearer {{userApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 13. Membuat API Key untuk Self (User Membuat untuk Dirinya Sendiri)
|
||||
# Permission yang diperlukan: api_keys:create-self
|
||||
POST {{baseUrl}}/api-keys/generate-self
|
||||
Authorization: Bearer {{userApiKey}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"name": "My Personal API Key",
|
||||
"permissions": {
|
||||
"prices": "read",
|
||||
"products": "read",
|
||||
"cart": "read,write",
|
||||
"orders": "read,write"
|
||||
},
|
||||
"expires_in_days": 365,
|
||||
"description": "API Key untuk penggunaan pribadi"
|
||||
}
|
||||
|
||||
### 14. Validasi API Key
|
||||
# Endpoint untuk mengecek apakah API key valid dan mengambil informasi permission
|
||||
GET {{baseUrl}}/api-keys/validate
|
||||
Authorization: Bearer {{userApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
### 15. Dapatkan Statistik Penggunaan API Key
|
||||
# Permission yang dibutuhkan: api_keys:read, statistics:read
|
||||
GET {{baseUrl}}/api-keys/123/usage
|
||||
Authorization: Bearer {{adminApiKey}}
|
||||
Accept: application/json
|
||||
|
||||
Reference in New Issue
Block a user