class Database {
private static $instance = null;
private $pdo;
private function __construct() {
$dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Throw exceptions on errors
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Fetch rows as associative arrays
PDO::ATTR_EMULATE_PREPARES => false, // Disable emulation for better security and performance
];
try {
$this->pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
} catch (PDOException $e) {
// Log the error and terminate
error_log('Database connection failed: ' . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Database connection failed. Please try again later.']);
exit();
}
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Database();
}
return self::$instance;
}
public function getConnection() {
return $this->pdo;
}
}
// Helper function to get PDO connection
function getDbConnection() {
return Database::getInstance()->getConnection();
}
// Start session if not already started
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
class Auth {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
/**
* Authenticates a user with username and password.
* @param string $username
* @param string $password
* @return array|false User data on success, false on failure.
*/
public function loginWithPassword($username, $password) {
$stmt = $this->pdo->prepare("SELECT user_id, username, password_hash, role FROM Users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
$this->createSession($user['user_id'], $user['username'], $user['role']);
return ['user_id' => $user['user_id'], 'username' => $user['username'], 'role' => $user['role']];
}
return false;
}
/**
* Authenticates a user with PIN from an approved IP.
* @param string $pin
* @param string $ip_address
* @return array|false User data on success, false on failure.
*/
public function loginWithPin($pin, $ip_address) {
if (!in_array($ip_address, APPROVED_IPS)) {
return false; // IP not approved
}
$stmt = $this->pdo->prepare("SELECT user_id, username, role FROM Users WHERE pin = ?");
$stmt->execute([$pin]);
$user = $stmt->fetch();
if ($user) {
$this->createSession($user['user_id'], $user['username'], $user['role']);
return ['user_id' => $user['user_id'], 'username' => $user['username'], 'role' => $user['role']];
}
return false;
}
private function createSession($userId, $username, $role) {
session_regenerate_id(true); // Regenerate session ID for security
$_SESSION['user_id'] = $userId;
$_SESSION['username'] = $username;
$_SESSION['role'] = $role;
$_SESSION['logged_in'] = true;
}
/**
* Checks if a user is currently authenticated.
* @return bool
*/
public function isAuthenticated() {
return isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true;
}
/**
* Gets the current authenticated user's data.
* @return array|null User data or null if not authenticated.
*/
public function getCurrentUser() {
if ($this->isAuthenticated()) {
return [
'user_id' => $_SESSION['user_id'],
'username' => $_SESSION['username'],
'role' => $_SESSION['role']
];
}
return null;
}
/**
* Checks if the current user has the required role.
* @param string $requiredRole
* @return bool
*/
public function hasRole($requiredRole) {
return $this->isAuthenticated() && $_SESSION['role'] === $requiredRole;
}
/**
* Logs out the current user.
*/
public function logout() {
$_SESSION = array(); // Clear all session variables
session_destroy(); // Destroy the session
setcookie(session_name(), '', time() - 3600, '/'); // Clear session cookie
}
}
class AuditLogger {
private $pdo;
private $auth;
public function __construct(PDO $pdo, Auth $auth) {
$this->pdo = $pdo;
$this->auth = $auth;
}
/**
* Logs an audit event.
* @param string $actionType e.g., 'INSERT', 'UPDATE', 'DELETE', 'LOGIN'
* @param string $tableName
* @param int|null $recordId ID of the affected record
* @param array|null $oldValue Old data (associative array)
* @param array|null $newValue New data (associative array)
*/
public function logEvent($actionType, $tableName, $recordId = null, $oldValue = null, $newValue = null) {
$user = $this->auth->getCurrentUser();
$userId = $user ? $user['user_id'] : null;
$ipAddress = $_SERVER['REMOTE_ADDR'] ?? 'UNKNOWN';
$stmt = $this->pdo->prepare("
INSERT INTO AuditLog (user_id, action_type, table_name, record_id, old_value, new_value, ip_address)
VALUES (?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$userId,
$actionType,
$tableName,
$recordId,
$oldValue ? json_encode($oldValue) : null,
$newValue ? json_encode($newValue) : null,
$ipAddress
]);
}
}
/**
* Sends a JSON response and exits.
* @param array $data The data to encode as JSON.
* @param int $statusCode HTTP status code.
*/
function sendJsonResponse($data, $statusCode = 200) {
header('Content-Type: application/json');
http_response_code($statusCode);
echo json_encode($data);
exit();
}
/**
* Checks if the request method matches the expected method.
* @param string $method Expected HTTP method (e.g., 'GET', 'POST', 'PUT', 'DELETE').
*/
function requireMethod($method) {
if ($_SERVER['REQUEST_METHOD'] !== $method) {
sendJsonResponse(['error' => 'Method Not Allowed'], 405);
}
}
/**
* Gets JSON input from the request body.
* @return array Decoded JSON data.
*/
function getJsonInput() {
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
sendJsonResponse(['error' => 'Invalid JSON input'], 400);
}
return $data;
}
/**
* Fetches a single record by ID from a table.
* @param PDO $pdo
* @param string $tableName
* @param string $idColumnName
* @param int $id
* @return array|false The fetched record or false if not found.
*/
function getRecordById(PDO $pdo, $tableName, $idColumnName, $id) {
$stmt = $pdo->prepare("SELECT * FROM {$tableName} WHERE {$idColumnName} = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
Fatal error: Uncaught Error: Call to undefined function getDbConnection() in /home/nerhcwjs/public_html/services/index.php:10
Stack trace:
#0 {main}
thrown in /home/nerhcwjs/public_html/services/index.php on line 10