Files
2026-04-27 10:26:26 +07:00

723 lines
30 KiB
PHP

<?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' 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;
}
}
}