481 lines
18 KiB
PHP
481 lines
18 KiB
PHP
<?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;
|
|
}
|
|
}
|
|
|
|
}
|