221 lines
6.9 KiB
PHP
221 lines
6.9 KiB
PHP
<?php
|
||
// geocode-cache.php
|
||
|
||
// ==== KONFIGURACJA ====
|
||
const DB_DSN = 'mysql:host=serwer1852487.home.pl;port=3380;dbname=28477142_0000006;charset=utf8mb4';
|
||
const DB_USER = '28477142_0000006';
|
||
const DB_PASS = '2aodCAiQSKThx4la6';
|
||
|
||
// Klucz trzymaj w .env/konfiguracji, tu tylko przykład:
|
||
const GOOGLE_GEOCODE_KEY = 'AIzaSyD-1SOVhJXr6HREtfmMILvlmV-hml3nxUg';
|
||
|
||
// Domyślne opcje zapytań do Google – zawęź region/język wg potrzeb
|
||
const GOOGLE_REGION = 'pl';
|
||
const GOOGLE_LANGUAGE = 'pl';
|
||
|
||
// TTL cache w dniach
|
||
const CACHE_TTL_DAYS_FORWARD = 365;
|
||
const CACHE_TTL_DAYS_REVERSE = 365;
|
||
|
||
$ALLOWED_ORIGINS = [
|
||
'https://twoja-domena.pl',
|
||
'https://www.twoja-domena.pl',
|
||
'http://localhost:3000',
|
||
'https://shoppro.project-dc.pl',
|
||
'https://moodo.pl'
|
||
];
|
||
|
||
// ==== NAGŁÓWKI/CORS ====
|
||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||
if ($origin && in_array($origin, $ALLOWED_ORIGINS, true)) {
|
||
header('Access-Control-Allow-Origin: ' . $origin);
|
||
header('Vary: Origin');
|
||
}
|
||
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||
header('Access-Control-Allow-Headers: Content-Type');
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
|
||
|
||
// ==== POMOCNICZE ====
|
||
function normalize_query(string $q): string {
|
||
$q = trim($q);
|
||
$q = preg_replace('/\s+/u', ' ', $q);
|
||
return mb_strtolower($q, 'UTF-8');
|
||
}
|
||
function now_mysql(): string { return date('Y-m-d H:i:s'); }
|
||
function add_days(string $date, int $days): string { return date('Y-m-d H:i:s', strtotime("$date +$days days")); }
|
||
|
||
// Budowa stabilnego klucza cache
|
||
function build_cache_key(?string $q, ?float $lat, ?float $lng): array {
|
||
if ($q !== null && $q !== '') {
|
||
$raw = $q;
|
||
$norm = normalize_query($q);
|
||
$mode = 'forward';
|
||
} else {
|
||
// reverse: lat,lng
|
||
$latR = round((float)$lat, 6);
|
||
$lngR = round((float)$lng, 6);
|
||
$raw = sprintf('%F,%F', $lat, $lng);
|
||
$norm = sprintf('latlng:%0.6f,%0.6f', $latR, $lngR);
|
||
$mode = 'reverse';
|
||
}
|
||
return [
|
||
'raw' => $raw,
|
||
'norm' => $norm,
|
||
'hash' => hash('sha256', $norm),
|
||
'mode' => $mode,
|
||
];
|
||
}
|
||
|
||
// DB
|
||
try {
|
||
$pdo = new PDO(DB_DSN, DB_USER, DB_PASS, [
|
||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||
]);
|
||
} catch (Throwable $e) {
|
||
http_response_code(500);
|
||
echo json_encode(['error' => 'DB connection failed']);
|
||
exit;
|
||
}
|
||
|
||
// Wejście
|
||
$q = isset($_GET['q']) ? trim((string)$_GET['q']) : '';
|
||
$lat = isset($_GET['lat']) ? (float)$_GET['lat'] : null;
|
||
$lng = isset($_GET['lng']) ? (float)$_GET['lng'] : null;
|
||
|
||
if ($q === '' && ($lat === null || $lng === null)) {
|
||
http_response_code(400);
|
||
echo json_encode(['error' => 'Provide q=address or lat & lng']);
|
||
exit;
|
||
}
|
||
|
||
$key = build_cache_key($q, $lat, $lng);
|
||
$now = now_mysql();
|
||
|
||
// ===== 1) CACHE LOOKUP (forward i reverse) =====
|
||
$stmt = $pdo->prepare("SELECT * FROM geocode_cache WHERE query_hash = :h AND (expires_at IS NULL OR expires_at > NOW()) LIMIT 1");
|
||
$stmt->execute([':h' => $key['hash']]);
|
||
if ($row = $stmt->fetch()) {
|
||
$pdo->prepare("UPDATE geocode_cache SET hits = hits + 1, updated_at = NOW() WHERE id = :id")->execute([':id' => $row['id']]);
|
||
echo json_encode([
|
||
'from_cache' => true,
|
||
'source' => 'cache',
|
||
'stale' => false,
|
||
'lat' => (float)$row['lat'],
|
||
'lng' => (float)$row['lng'],
|
||
'formatted_address' => $row['formatted_address'],
|
||
'place_id' => $row['place_id'],
|
||
'provider' => $row['provider'],
|
||
], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
// ===== 2) PYTANIE DO GOOGLE =====
|
||
function google_request(array $params): array {
|
||
$base = 'https://maps.googleapis.com/maps/api/geocode/json';
|
||
$params['key'] = GOOGLE_GEOCODE_KEY;
|
||
$params += ['language' => GOOGLE_LANGUAGE, 'region' => GOOGLE_REGION];
|
||
$url = $base . '?' . http_build_query($params);
|
||
|
||
$ch = curl_init($url);
|
||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3]);
|
||
$res = curl_exec($ch); $err = curl_error($ch); curl_close($ch);
|
||
if ($res === false) throw new RuntimeException('cURL error: ' . $err);
|
||
$data = json_decode($res, true);
|
||
if (!is_array($data)) throw new RuntimeException('Invalid JSON from Google');
|
||
if (($data['status'] ?? '') !== 'OK' || empty($data['results'][0])) {
|
||
$status = $data['status'] ?? 'UNKNOWN';
|
||
throw new RuntimeException('Geocoding failed: ' . $status);
|
||
}
|
||
return $data;
|
||
}
|
||
|
||
try {
|
||
$data = ($key['mode'] === 'forward')
|
||
? google_request(['address' => $q])
|
||
: google_request(['latlng' => $lat . ',' . $lng]);
|
||
|
||
$top = $data['results'][0];
|
||
$latV = (float)$top['geometry']['location']['lat'];
|
||
$lngV = (float)$top['geometry']['location']['lng'];
|
||
$fmt = (string)($top['formatted_address'] ?? '');
|
||
$pid = (string)($top['place_id'] ?? '');
|
||
|
||
// ===== 3) ZAPIS DO CACHE (forward i reverse) =====
|
||
$ttlDays = ($key['mode'] === 'forward') ? CACHE_TTL_DAYS_FORWARD : CACHE_TTL_DAYS_REVERSE;
|
||
$expires = add_days($now, $ttlDays);
|
||
|
||
$stmt = $pdo->prepare("INSERT INTO geocode_cache
|
||
(query_hash, query_raw, query_norm, lat, lng, formatted_address, place_id, provider, hits, created_at, updated_at, expires_at, raw_json)
|
||
VALUES (:h, :raw, :norm, :lat, :lng, :fmt, :pid, 'google', 1, :now, :now, :exp, :json)
|
||
ON DUPLICATE KEY UPDATE
|
||
lat = VALUES(lat),
|
||
lng = VALUES(lng),
|
||
formatted_address = VALUES(formatted_address),
|
||
place_id = VALUES(place_id),
|
||
updated_at = VALUES(updated_at),
|
||
expires_at = VALUES(expires_at),
|
||
raw_json = VALUES(raw_json),
|
||
hits = hits + 1");
|
||
|
||
$stmt->execute([
|
||
':h' => $key['hash'],
|
||
':raw' => $key['raw'],
|
||
':norm'=> $key['norm'],
|
||
':lat' => $latV,
|
||
':lng' => $lngV,
|
||
':fmt' => $fmt,
|
||
':pid' => $pid,
|
||
':now' => $now,
|
||
':exp' => $expires,
|
||
':json'=> json_encode($data, JSON_UNESCAPED_UNICODE),
|
||
]);
|
||
|
||
echo json_encode([
|
||
'from_cache' => false,
|
||
'source' => 'google',
|
||
'stale' => false,
|
||
'lat' => $latV,
|
||
'lng' => $lngV,
|
||
'formatted_address' => $fmt,
|
||
'place_id' => $pid,
|
||
'provider' => 'google',
|
||
], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
|
||
} catch (Throwable $e) {
|
||
try {
|
||
$stmt = $pdo->prepare("SELECT * FROM geocode_cache WHERE query_hash = :h LIMIT 1");
|
||
$stmt->execute([':h' => $key['hash']]);
|
||
if ($row = $stmt->fetch()) {
|
||
http_response_code(200);
|
||
echo json_encode([
|
||
'from_cache' => true,
|
||
'source' => 'cache',
|
||
'stale' => true,
|
||
'lat' => (float)$row['lat'],
|
||
'lng' => (float)$row['lng'],
|
||
'formatted_address' => $row['formatted_address'],
|
||
'place_id' => $row['place_id'],
|
||
'provider' => $row['provider'],
|
||
], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
} catch (Throwable $ignored) {}
|
||
|
||
// *** fallback „pusty” ***
|
||
http_response_code(200);
|
||
echo json_encode([
|
||
'from_cache' => false,
|
||
'source' => 'error',
|
||
'stale' => false,
|
||
'lat' => null,
|
||
'lng' => null,
|
||
'formatted_address' => '',
|
||
'place_id' => '',
|
||
'provider' => '',
|
||
'error' => $e->getMessage(),
|
||
], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
} |