Przebudowa - code review aplikaacji, podział na moduły.

This commit is contained in:
2026-09-25 15:33:32 +02:00
parent 45354071bd
commit 9d25de0d89
17 changed files with 2646 additions and 2148 deletions
+35
View File
@@ -47,6 +47,29 @@ function hasPendingGuestAction(PDO $pdo, string $tableId, string $messageType):
return (bool) $stmt->fetchColumn();
}
/**
* Limit: max 1 nowe zgłoszenie danego typu na stolik w oknie czasowym
* (nawet po obsłużeniu poprzedniego w KDS).
*/
function isGuestActionRateLimited(PDO $pdo, string $tableId, string $messageType, int $windowSeconds = 60): bool
{
$windowSeconds = max(1, min(600, $windowSeconds));
$stmt = $pdo->prepare("
SELECT 1
FROM guest_action_queue
WHERE table_id = :table_id
AND message_type = :message_type
AND created_at >= (NOW(3) - INTERVAL {$windowSeconds} SECOND)
LIMIT 1
");
$stmt->execute([
':table_id' => $tableId,
':message_type' => $messageType,
]);
return (bool) $stmt->fetchColumn();
}
function fetchPendingGuestActions(PDO $pdo, string $tableId): array
{
$stmt = $pdo->prepare("
@@ -316,6 +339,18 @@ try {
exit;
}
if (isGuestActionRateLimited($pdo, $tableId, $messageType, 60)) {
http_response_code(429);
echo json_encode([
'status' => 'error',
'code' => 'rate_limited',
'message' => $messageType === 'waiter_call'
? 'Wezwanie zostało już niedawno wysłane. Spróbuj ponownie za chwilę.'
: 'Prośba o rachunek została już niedawno wysłana. Spróbuj ponownie za chwilę.',
], JSON_UNESCAPED_UNICODE);
exit;
}
$stmt = $pdo->prepare("
INSERT INTO guest_action_queue (
table_id,
+116
View File
@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
if ($_SERVER['REQUEST_METHOD'] !== 'GET' && $_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode([
'status' => 'error',
'message' => 'Method not allowed',
], JSON_UNESCAPED_UNICODE);
exit;
}
$nipRaw = '';
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$nipRaw = isset($_GET['nip']) ? (string) $_GET['nip'] : '';
} else {
$rawBody = file_get_contents('php://input');
$data = json_decode($rawBody ?: '', true);
if (is_array($data) && isset($data['nip'])) {
$nipRaw = (string) $data['nip'];
} elseif (isset($_POST['nip'])) {
$nipRaw = (string) $_POST['nip'];
}
}
$nip = preg_replace('/[\s\-]/', '', $nipRaw) ?? '';
if ($nip === '' || !preg_match('/^\d{10}$/', $nip)) {
http_response_code(422);
echo json_encode([
'status' => false,
'message' => 'Podaj poprawny 10-cyfrowy NIP.',
], JSON_UNESCAPED_UNICODE);
exit;
}
$config = require __DIR__ . '/../config/gus.php';
$apiKey = trim((string) ($config['api_key'] ?? ''));
$baseUrl = rtrim((string) ($config['base_url'] ?? ''), '/');
$timeout = (int) ($config['timeout_seconds'] ?? 12);
if ($apiKey === '' || $baseUrl === '') {
http_response_code(500);
echo json_encode([
'status' => false,
'message' => 'Brak konfiguracji GUS.',
], JSON_UNESCAPED_UNICODE);
exit;
}
$url = $baseUrl . '/' . rawurlencode($nip) . '?api_key=' . rawurlencode($apiKey);
$responseBody = false;
$httpCode = 0;
if (function_exists('curl_init')) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => max(3, $timeout),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
],
]);
$responseBody = curl_exec($ch);
$curlErrno = curl_errno($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($curlErrno !== 0) {
$responseBody = false;
}
} else {
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => max(3, $timeout),
'header' => "Accept: application/json\r\n",
'ignore_errors' => true,
],
]);
$responseBody = @file_get_contents($url, false, $context);
if (isset($http_response_header[0]) && preg_match('/\s(\d{3})\s/', $http_response_header[0], $m)) {
$httpCode = (int) $m[1];
}
}
if ($responseBody === false) {
http_response_code(502);
echo json_encode([
'status' => false,
'message' => 'Błąd połączenia z API GUS.',
], JSON_UNESCAPED_UNICODE);
exit;
}
$decoded = json_decode($responseBody, true);
if (!is_array($decoded)) {
http_response_code(502);
echo json_encode([
'status' => false,
'message' => 'Niepoprawna odpowiedź API GUS.',
], JSON_UNESCAPED_UNICODE);
exit;
}
if ($httpCode < 200 || $httpCode >= 300) {
http_response_code($httpCode >= 400 ? $httpCode : 502);
}
echo json_encode($decoded, JSON_UNESCAPED_UNICODE);
+13
View File
@@ -14,6 +14,19 @@ function getRequestClientIp(): string
return trim((string) ($_SERVER['REMOTE_ADDR'] ?? ''));
}
/**
* Hostnamey, dla których frontend pomija geolokalizację (dev / znany host).
* Wstrzykiwane do window.APP_CONFIG przez app.php.
*/
function getGeoBypassTrustedHosts(): array
{
return [
'82.160.190.247',
'localhost',
'127.0.0.1',
];
}
/**
* Pojedyncze IP z pominięciem geo (zewnętrzne, dev, przykładowe hosty LAN).
*/