First commit / commit pertama
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* activity.php — Helper pencatatan log aktivitas (KF-04).
|
||||
*
|
||||
* logActivity($pdo, 'login');
|
||||
* logActivity($pdo, 'create', 'warga_miskin', 12, 'Tambah warga baru');
|
||||
*/
|
||||
|
||||
/**
|
||||
* Catat satu baris aktivitas. Gagal mencatat tidak boleh memutus alur utama,
|
||||
* jadi error ditangkap diam-diam.
|
||||
*/
|
||||
function logActivity(
|
||||
PDO $pdo,
|
||||
string $aksi,
|
||||
?string $tabel = null,
|
||||
?int $recordId = null,
|
||||
?string $keterangan = null
|
||||
): void {
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO activity_log
|
||||
(user_id, username, aksi, tabel_target, record_id, keterangan, ip_address)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([
|
||||
$_SESSION['user_id'] ?? null,
|
||||
$_SESSION['username'] ?? null,
|
||||
$aksi,
|
||||
$tabel,
|
||||
$recordId,
|
||||
$keterangan,
|
||||
$_SERVER['REMOTE_ADDR'] ?? null,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
// Abaikan — logging tidak boleh menggagalkan request.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Application path helpers.
|
||||
*
|
||||
* Set APP_BASE_PATH to the subdirectory that serves project_final.
|
||||
* In this repository's Coolify image the final app runs at /project_final.
|
||||
*/
|
||||
|
||||
function loadEnvFile(): void {
|
||||
$envFile = __DIR__ . '/../../.env';
|
||||
if (file_exists($envFile)) {
|
||||
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
if (strpos($line, '=') === false || strpos($line, '#') === 0) {
|
||||
continue;
|
||||
}
|
||||
[$key, $value] = explode('=', $line, 2);
|
||||
$key = trim($key);
|
||||
$value = trim($value);
|
||||
if (!getenv($key)) {
|
||||
putenv("$key=$value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadEnvFile();
|
||||
|
||||
function app_base_path(): string {
|
||||
$basePath = getenv('APP_BASE_PATH');
|
||||
if ($basePath === false) {
|
||||
$basePath = '/project/project_final';
|
||||
}
|
||||
|
||||
$basePath = trim($basePath);
|
||||
if ($basePath === '' || $basePath === '/') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '/' . trim($basePath, '/');
|
||||
}
|
||||
|
||||
function app_url(string $path = ''): string {
|
||||
$path = '/' . ltrim($path, '/');
|
||||
return app_base_path() . $path;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* auth_check.php — Middleware RBAC untuk 4 role SKPL.
|
||||
*
|
||||
* Role: publik | enumerator | operator | pimpinan
|
||||
*
|
||||
* Usage:
|
||||
* require_once __DIR__ . '/../config/auth_check.php';
|
||||
* requireAnyRole(['operator']); // hanya operator
|
||||
* requireAnyRole(['operator','pimpinan']); // operator atau pimpinan
|
||||
* requireRole('operator'); // kompat lama (1 role)
|
||||
*/
|
||||
require_once __DIR__ . '/session.php';
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
startAppSession();
|
||||
}
|
||||
|
||||
function isLoggedIn(): bool {
|
||||
return isset($_SESSION['user_id']) && isset($_SESSION['role']);
|
||||
}
|
||||
|
||||
function currentRole(): string {
|
||||
return $_SESSION['role'] ?? 'publik';
|
||||
}
|
||||
|
||||
/**
|
||||
* Halaman beranda (landing) per role setelah login.
|
||||
*/
|
||||
function roleHome(string $role): string {
|
||||
return app_url(match ($role) {
|
||||
'operator' => 'admin/index.php',
|
||||
'pimpinan' => 'pimpinan/index.php',
|
||||
'enumerator' => 'enumerator/index.php',
|
||||
default => 'peta_publik.php',
|
||||
});
|
||||
}
|
||||
|
||||
function requireLogin(?string $redirect = null): void {
|
||||
if (!isLoggedIn()) {
|
||||
$redirect = $redirect ?? app_url('login.php');
|
||||
header("Location: $redirect");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Izinkan akses hanya jika role pengguna ada di daftar $roles.
|
||||
* Jika tidak, dialihkan ke beranda role-nya sendiri.
|
||||
*/
|
||||
function requireAnyRole(array $roles, ?string $redirect = null): void {
|
||||
requireLogin($redirect);
|
||||
if (!in_array(currentRole(), $roles, true)) {
|
||||
header('Location: ' . roleHome(currentRole()));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kompatibilitas dengan kode lama: requireRole('operator').
|
||||
*/
|
||||
function requireRole(string $role, ?string $redirect = null): void {
|
||||
requireAnyRole([$role], $redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cek role tanpa redirect (untuk tampilan kondisional menu).
|
||||
*/
|
||||
function hasAnyRole(array $roles): bool {
|
||||
return isLoggedIn() && in_array(currentRole(), $roles, true);
|
||||
}
|
||||
|
||||
function currentUser(): array {
|
||||
return [
|
||||
'id' => $_SESSION['user_id'] ?? null,
|
||||
'username' => $_SESSION['username'] ?? '',
|
||||
'email' => $_SESSION['email'] ?? '',
|
||||
'role' => $_SESSION['role'] ?? '',
|
||||
'wilayah_tugas'=> $_SESSION['wilayah_tugas'] ?? '',
|
||||
'nama_lengkap' => $_SESSION['nama_lengkap'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Label role yang ramah ditampilkan.
|
||||
*/
|
||||
function roleLabel(string $role): string {
|
||||
return match ($role) {
|
||||
'operator' => 'Operator',
|
||||
'pimpinan' => 'Pimpinan',
|
||||
'enumerator' => 'Enumerator',
|
||||
'publik' => 'Publik',
|
||||
default => ucfirst($role),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Singleton PDO connection.
|
||||
*
|
||||
* Coolify: set DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, and DB_PASSWORD
|
||||
* from the database service credentials.
|
||||
*/
|
||||
class Database {
|
||||
private static $conn = null;
|
||||
|
||||
private static function env(string $key, string $default = ''): string {
|
||||
$value = getenv($key);
|
||||
return ($value === false || $value === '') ? $default : $value;
|
||||
}
|
||||
|
||||
public static function getConnection() {
|
||||
if (self::$conn === null) {
|
||||
$dbUrl = self::env('DATABASE_URL');
|
||||
if ($dbUrl) {
|
||||
$parsed = parse_url($dbUrl);
|
||||
$host = $parsed['host'] ?? '127.0.0.1';
|
||||
$port = $parsed['port'] ?? '3306';
|
||||
$username = $parsed['user'] ?? 'root';
|
||||
$password = $parsed['pass'] ?? '';
|
||||
$dbName = ltrim($parsed['path'], '/');
|
||||
} else {
|
||||
$host = self::env('DB_HOST', '127.0.0.1');
|
||||
$port = self::env('DB_PORT', '3306');
|
||||
$dbName = self::env('DB_DATABASE', 'webgis_db');
|
||||
$username = self::env('DB_USERNAME', 'root');
|
||||
$password = self::env('DB_PASSWORD', '');
|
||||
}
|
||||
|
||||
self::$conn = new PDO(
|
||||
"mysql:host={$host};port={$port};dbname={$dbName};charset=utf8mb4",
|
||||
$username,
|
||||
$password
|
||||
);
|
||||
self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
self::$conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
|
||||
// Disable strict mode for GROUP BY to prevent 1055 errors in Coolify's MariaDB
|
||||
self::$conn->exec("SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''))");
|
||||
|
||||
// Auto-initialize database if empty
|
||||
$stmt = self::$conn->query("SHOW TABLES LIKE 'users'");
|
||||
if ($stmt->rowCount() == 0) {
|
||||
$sqlFile = __DIR__ . '/../../database/webgis_db.sql';
|
||||
if (file_exists($sqlFile)) {
|
||||
$sql = file_get_contents($sqlFile);
|
||||
self::$conn->exec($sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
return self::$conn;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/app.php';
|
||||
|
||||
function startAppSession(): void {
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$secureEnv = strtolower((string)getenv('SESSION_SECURE'));
|
||||
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|
||||
|| (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https')
|
||||
|| $secureEnv === 'true'
|
||||
|| $secureEnv === '1';
|
||||
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => app_base_path() ?: '/',
|
||||
'secure' => $isHttps,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
|
||||
session_start();
|
||||
}
|
||||
Reference in New Issue
Block a user