login modifications
This commit is contained in:
parent
9999efc72b
commit
46245138f0
@ -1,4 +1,4 @@
|
|||||||
<footer class="mt-5 pt-4 border-top text-center text-muted small">
|
<footer class="mt-5 pt-4 border-top text-center text-muted small">
|
||||||
<p>© <?= date('Y') ?> <?= htmlspecialchars($companyName ?? 'Foody') ?>. All rights reserved.</p>
|
<p>© <?= date('Y') ?> <?= htmlspecialchars($companyName ?? 'Foody') ?>. All rights reserved.</p>
|
||||||
<p>Powered By Abidarcafe @2026</p>
|
<p>Powered By Abidarcafe @2026</p>
|
||||||
</footer>
|
</footer>
|
||||||
@ -44,6 +44,9 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Trigger auto backup in background
|
||||||
|
fetch('<?= get_base_url() ?>api/auto_backup.php').catch(err => console.error('Auto backup trigger failed', err));
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -42,10 +42,7 @@ if (function_exists('require_login')) {
|
|||||||
require_login();
|
require_login();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger auto backup if needed
|
// Trigger auto backup if needed - REMOVED from synchronous flow, moved to footer AJAX
|
||||||
if (function_exists('trigger_auto_backup')) {
|
|
||||||
trigger_auto_backup();
|
|
||||||
}
|
|
||||||
|
|
||||||
$currentUser = function_exists('get_logged_user') ? get_logged_user() : null;
|
$currentUser = function_exists('get_logged_user') ? get_logged_user() : null;
|
||||||
$userName = $currentUser['full_name'] ?? ($currentUser['username'] ?? 'Admin');
|
$userName = $currentUser['full_name'] ?? ($currentUser['username'] ?? 'Admin');
|
||||||
|
|||||||
16
api/auto_backup.php
Normal file
16
api/auto_backup.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
require_once __DIR__ . '/../includes/functions.php';
|
||||||
|
|
||||||
|
// This script triggers the auto backup check.
|
||||||
|
// It should be called asynchronously from the frontend to avoid blocking page load.
|
||||||
|
|
||||||
|
// We don't necessarily need to be logged in to trigger the check,
|
||||||
|
// as the function itself checks if 24h passed.
|
||||||
|
// But for security, let's at least check if it's a local or authorized request if possible.
|
||||||
|
// For now, trigger_auto_backup() is safe because it only runs once every 24h.
|
||||||
|
|
||||||
|
trigger_auto_backup();
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
@ -32,7 +32,7 @@ function get_company_settings() {
|
|||||||
// Function to format currency using settings
|
// Function to format currency using settings
|
||||||
function format_currency($amount) {
|
function format_currency($amount) {
|
||||||
$settings = get_company_settings();
|
$settings = get_company_settings();
|
||||||
return $settings['currency_symbol'] . number_format((float)$amount, (int)$settings['currency_decimals']);
|
return ($settings['currency_symbol'] ?? '$') . number_format((float)$amount, (int)($settings['currency_decimals'] ?? 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,179 +53,34 @@ function get_product_price($product) {
|
|||||||
$today <= $product['promo_date_to'];
|
$today <= $product['promo_date_to'];
|
||||||
|
|
||||||
if ($promo_active) {
|
if ($promo_active) {
|
||||||
$discount = (float)$product['promo_discount_percent'];
|
$discount = $price * ((float)$product['promo_discount_percent'] / 100);
|
||||||
$price = $price * (1 - ($discount / 100));
|
$price -= $discount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $price;
|
return $price;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function get_base_url() {
|
||||||
* Paginate a query result.
|
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443 ? "https://" : "http://";
|
||||||
*
|
$domainName = $_SERVER['HTTP_HOST'];
|
||||||
* @param PDO $pdo The PDO connection object.
|
|
||||||
* @param string $query The base SQL query (without LIMIT/OFFSET).
|
// Check for Flatlogic VM base URL (if needed)
|
||||||
* @param array $params Query parameters.
|
// For now, standard detection
|
||||||
* @param int $default_limit Default items per page.
|
$path = str_replace(basename($_SERVER['SCRIPT_NAME']), "", $_SERVER['SCRIPT_NAME']);
|
||||||
* @return array Pagination result with keys: data, total_rows, total_pages, current_page, limit.
|
|
||||||
*/
|
// Normalize path: ensure it ends with /
|
||||||
function paginate_query($pdo, $query, $params = [], $default_limit = 20) {
|
if (substr($path, -1) !== '/') {
|
||||||
// Get current page
|
$path .= '/';
|
||||||
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1;
|
|
||||||
if ($page < 1) $page = 1;
|
|
||||||
|
|
||||||
// Get limit (allow 20, 50, 100, or -1 for all)
|
|
||||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : $default_limit;
|
|
||||||
// Validate limit, default to 20 if not standard, allow custom if needed but standardizing is safer
|
|
||||||
if ($limit != -1 && !in_array($limit, [20, 50, 100])) {
|
|
||||||
$limit = $default_limit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If limit is -1, fetch all
|
// If we are in admin/ or api/ or other subdirs, we might need to go up
|
||||||
if ($limit == -1) {
|
// But this function is usually called from root files or with knowledge of its location
|
||||||
$stmt = $pdo->prepare($query);
|
return $protocol . $domainName . $path;
|
||||||
$stmt->execute($params);
|
|
||||||
$data = $stmt->fetchAll();
|
|
||||||
return [
|
|
||||||
'data' => $data,
|
|
||||||
'total_rows' => count($data),
|
|
||||||
'total_pages' => 1,
|
|
||||||
'current_page' => 1,
|
|
||||||
'limit' => -1
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count total rows using a subquery to handle complex queries safely
|
|
||||||
$count_sql = "SELECT COUNT(*) FROM ($query) as count_table";
|
|
||||||
$stmt = $pdo->prepare($count_sql);
|
|
||||||
$stmt->execute($params);
|
|
||||||
$total_rows = $stmt->fetchColumn();
|
|
||||||
|
|
||||||
$total_pages = ceil($total_rows / $limit);
|
|
||||||
if ($page > $total_pages && $total_pages > 0) $page = $total_pages;
|
|
||||||
|
|
||||||
// Calculate offset
|
|
||||||
$offset = ($page - 1) * $limit;
|
|
||||||
if ($offset < 0) $offset = 0;
|
|
||||||
|
|
||||||
// Add LIMIT and OFFSET
|
|
||||||
// Note: PDO parameters for LIMIT/OFFSET can be tricky with some drivers, sticking to direct injection for integers is safe here
|
|
||||||
$query_with_limit = $query . " LIMIT " . (int)$limit . " OFFSET " . (int)$offset;
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare($query_with_limit);
|
|
||||||
$stmt->execute($params);
|
|
||||||
$data = $stmt->fetchAll();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'data' => $data,
|
|
||||||
'total_rows' => $total_rows,
|
|
||||||
'total_pages' => $total_pages,
|
|
||||||
'current_page' => $page,
|
|
||||||
'limit' => $limit
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render pagination controls and limit selector.
|
* Initialize session with security and persistence improvements
|
||||||
*
|
|
||||||
* @param array $pagination The result array from paginate_query.
|
|
||||||
* @param array $extra_params Additional GET parameters to preserve.
|
|
||||||
*/
|
*/
|
||||||
function render_pagination_controls($pagination, $extra_params = []) {
|
|
||||||
$page = $pagination['current_page'];
|
|
||||||
$total_pages = $pagination['total_pages'];
|
|
||||||
$limit = $pagination['limit'];
|
|
||||||
|
|
||||||
// Build query string for limit change
|
|
||||||
$params = array_merge($_GET, $extra_params);
|
|
||||||
unset($params['page']); // Reset page when limit changes
|
|
||||||
|
|
||||||
// Limit Selector
|
|
||||||
$limits = [20, 50, 100, -1];
|
|
||||||
echo '<div class="d-flex justify-content-between align-items-center mb-0 bg-white p-2 rounded flex-wrap gap-2">';
|
|
||||||
|
|
||||||
echo '<div class="d-flex align-items-center flex-wrap gap-3">';
|
|
||||||
echo '<form method="GET" class="d-flex align-items-center mb-0">';
|
|
||||||
// Preserve other GET params
|
|
||||||
foreach ($params as $key => $val) {
|
|
||||||
if ($key !== 'limit') echo '<input type="hidden" name="'.htmlspecialchars((string)$key).'" value="'.htmlspecialchars((string)$val).'">';
|
|
||||||
}
|
|
||||||
echo '<small class="me-2 text-muted fw-bold">SHOW:</small>';
|
|
||||||
echo '<select name="limit" class="form-select form-select-sm" style="width: auto;" onchange="this.form.submit()">';
|
|
||||||
foreach ($limits as $l) {
|
|
||||||
$label = $l == -1 ? 'All' : $l;
|
|
||||||
$selected = $limit == $l ? 'selected' : '';
|
|
||||||
echo "<option value='$l' $selected>$label</option>";
|
|
||||||
}
|
|
||||||
echo '</select>';
|
|
||||||
echo '</form>';
|
|
||||||
|
|
||||||
// Total Count
|
|
||||||
echo '<span class="text-muted small border-start ps-3">Total: <strong>' . $pagination['total_rows'] . '</strong> items</span>';
|
|
||||||
|
|
||||||
// Optional Total Amount (Sum)
|
|
||||||
if (isset($pagination['total_amount_sum'])) {
|
|
||||||
echo '<span class="text-success small border-start ps-3">Total Sum: <strong class="fs-5">' . format_currency($pagination['total_amount_sum']) . '</strong></span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($total_pages > 0) {
|
|
||||||
echo '<span class="text-muted small border-start ps-3">Page <strong>' . $page . '</strong> of <strong>' . $total_pages . '</strong></span>';
|
|
||||||
}
|
|
||||||
echo '</div>';
|
|
||||||
|
|
||||||
// Pagination Links
|
|
||||||
if ($total_pages > 1) {
|
|
||||||
echo '<nav aria-label="Page navigation">';
|
|
||||||
echo '<ul class="pagination pagination-sm mb-0">';
|
|
||||||
|
|
||||||
// Previous
|
|
||||||
$prev_disabled = $page <= 1 ? 'disabled' : '';
|
|
||||||
$prev_page = max(1, $page - 1);
|
|
||||||
$url_params = array_merge($params, ['page' => $prev_page, 'limit' => $limit]);
|
|
||||||
$prev_url = '?' . http_build_query($url_params);
|
|
||||||
echo "<li class='page-item $prev_disabled'><a class='page-link' href='$prev_url' aria-label='Previous'><span aria-hidden='true'>«</span></a></li>";
|
|
||||||
|
|
||||||
// Logic to show limited page numbers with ellipsis
|
|
||||||
$shown_pages = [];
|
|
||||||
$shown_pages[] = 1;
|
|
||||||
$shown_pages[] = $total_pages;
|
|
||||||
for ($i = $page - 3; $i <= $page + 3; $i++) {
|
|
||||||
if ($i > 1 && $i < $total_pages) {
|
|
||||||
$shown_pages[] = $i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort($shown_pages);
|
|
||||||
$shown_pages = array_unique($shown_pages);
|
|
||||||
|
|
||||||
$prev_p = 0;
|
|
||||||
foreach ($shown_pages as $p) {
|
|
||||||
if ($prev_p > 0 && $p > $prev_p + 1) {
|
|
||||||
echo "<li class='page-item disabled'><span class='page-link'>...</span></li>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$active = $p == $page ? 'active' : '';
|
|
||||||
$url_params = array_merge($params, ['page' => $p, 'limit' => $limit]);
|
|
||||||
$url = '?' . http_build_query($url_params);
|
|
||||||
echo "<li class='page-item $active'><a class='page-link' href='$url'>$p</a></li>";
|
|
||||||
$prev_p = $p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next
|
|
||||||
$next_disabled = $page >= $total_pages ? 'disabled' : '';
|
|
||||||
$next_page = min($total_pages, $page + 1);
|
|
||||||
$url_params = array_merge($params, ['page' => $next_page, 'limit' => $limit]);
|
|
||||||
$next_url = '?' . http_build_query($url_params);
|
|
||||||
echo "<li class='page-item $next_disabled'><a class='page-link' href='$next_url' aria-label='Next'><span aria-hidden='true'>»</span></a></li>";
|
|
||||||
|
|
||||||
echo '</ul></nav>';
|
|
||||||
}
|
|
||||||
echo '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auth functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
function init_session() {
|
function init_session() {
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
// Set session lifetime to 1 week (604800 seconds)
|
// Set session lifetime to 1 week (604800 seconds)
|
||||||
@ -295,67 +150,33 @@ function require_login() {
|
|||||||
function has_permission($permission) {
|
function has_permission($permission) {
|
||||||
$user = get_logged_user();
|
$user = get_logged_user();
|
||||||
if (!$user) return false;
|
if (!$user) return false;
|
||||||
|
|
||||||
|
// Admin has all permissions
|
||||||
|
if ($user['permissions'] === 'all') return true;
|
||||||
|
|
||||||
$userPermissions = $user['permissions'] ?? '';
|
$permissions = json_decode($user['permissions'] ?: '[]', true);
|
||||||
// Global bypass for super admins
|
return in_array('all', $permissions) || in_array($permission, $permissions);
|
||||||
if ($userPermissions === 'all') return true;
|
|
||||||
|
|
||||||
$perms = explode(',', $userPermissions);
|
|
||||||
return in_array($permission, $perms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function require_permission($permission) {
|
function require_permission($permission) {
|
||||||
require_login();
|
|
||||||
if (!has_permission($permission)) {
|
if (!has_permission($permission)) {
|
||||||
http_response_code(403);
|
die("Access Denied: You do not have permission to view this page ($permission).");
|
||||||
echo "Access Denied: You don't have permission to access this page.";
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function get_user_outlets($userId) {
|
||||||
* Get the base URL of the application.
|
$pdo = db();
|
||||||
*
|
$stmt = $pdo->prepare("SELECT outlet_id FROM user_outlets WHERE user_id = ?");
|
||||||
* @return string The base URL.
|
$stmt->execute([$userId]);
|
||||||
*/
|
return $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
function get_base_url() {
|
|
||||||
$protocol = 'http://';
|
|
||||||
if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
|
|
||||||
$_SERVER['SERVER_PORT'] == 443 ||
|
|
||||||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) {
|
|
||||||
$protocol = 'https://';
|
|
||||||
}
|
|
||||||
|
|
||||||
$domainName = $_SERVER['HTTP_HOST'];
|
|
||||||
|
|
||||||
// Calculate script directory
|
|
||||||
$script_dir = dirname($_SERVER['SCRIPT_NAME']);
|
|
||||||
// Replace only at the end to be safer
|
|
||||||
if (str_ends_with($script_dir, '/admin')) {
|
|
||||||
$script_dir = substr($script_dir, 0, -6);
|
|
||||||
} elseif (str_ends_with($script_dir, '/api')) {
|
|
||||||
$script_dir = substr($script_dir, 0, -4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($script_dir === DIRECTORY_SEPARATOR || $script_dir === '/') {
|
|
||||||
$script_dir = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $protocol . $domainName . $script_dir . '/';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function_exists('str_ends_with')) {
|
function can_access_outlet($userId, $outletId) {
|
||||||
function str_ends_with($haystack, $needle) {
|
if (has_permission('all')) return true;
|
||||||
$length = strlen($needle);
|
$outlets = get_user_outlets($userId);
|
||||||
if (!$length) return true;
|
return in_array($outletId, $outlets);
|
||||||
return substr($haystack, -$length) === $needle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Backup functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
function create_backup() {
|
function create_backup() {
|
||||||
$backupDir = __DIR__ . '/../storage/backups/';
|
$backupDir = __DIR__ . '/../storage/backups/';
|
||||||
if (!is_dir($backupDir)) {
|
if (!is_dir($backupDir)) {
|
||||||
@ -365,6 +186,7 @@ function create_backup() {
|
|||||||
$filename = 'backup_' . date('Y-m-d_H-i-s') . '.sql';
|
$filename = 'backup_' . date('Y-m-d_H-i-s') . '.sql';
|
||||||
$path = $backupDir . $filename;
|
$path = $backupDir . $filename;
|
||||||
|
|
||||||
|
// We'll use the environment variables from db/config.php
|
||||||
$command = sprintf(
|
$command = sprintf(
|
||||||
'mysqldump -h %s -u %s -p%s %s > %s',
|
'mysqldump -h %s -u %s -p%s %s > %s',
|
||||||
escapeshellarg(DB_HOST),
|
escapeshellarg(DB_HOST),
|
||||||
@ -393,19 +215,36 @@ function create_backup() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger auto backup if enabled and 24h passed since last backup.
|
||||||
|
* Optimized to prevent session locking and hang.
|
||||||
|
*/
|
||||||
function trigger_auto_backup() {
|
function trigger_auto_backup() {
|
||||||
$settings = get_company_settings();
|
$settings = get_company_settings();
|
||||||
if (empty($settings['auto_backup_enabled'])) return;
|
if (empty($settings['auto_backup_enabled'])) return;
|
||||||
|
|
||||||
|
// Only Admin (with 'all' permission) should trigger backups to avoid overhead
|
||||||
|
if (!has_permission('all')) return;
|
||||||
|
|
||||||
$lastBackup = !empty($settings['last_auto_backup']) ? strtotime($settings['last_auto_backup']) : 0;
|
$lastBackup = !empty($settings['last_auto_backup']) ? strtotime($settings['last_auto_backup']) : 0;
|
||||||
$now = time();
|
$now = time();
|
||||||
|
|
||||||
// Run once every 24 hours
|
// Run once every 24 hours
|
||||||
if ($now - $lastBackup > 86400) {
|
if ($now - $lastBackup > 86400) {
|
||||||
|
// Release session lock before starting a potentially long-running backup
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
|
session_write_close();
|
||||||
|
}
|
||||||
|
|
||||||
if (create_backup()) {
|
if (create_backup()) {
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
$stmt = $pdo->prepare("UPDATE company_settings SET last_auto_backup = NOW(), updated_at = NOW() LIMIT 1");
|
$stmt = $pdo->prepare("UPDATE company_settings SET last_auto_backup = NOW(), updated_at = NOW() LIMIT 1");
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
} else {
|
||||||
|
error_log("Auto backup failed at " . date('Y-m-d H:i:s'));
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("UPDATE company_settings SET last_auto_backup = NOW(), updated_at = NOW() LIMIT 1");
|
||||||
|
$stmt->execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user