Stand: SMTP-Test, Admin-Mail-Tab, Notifiable-Fix, Lazy-Quill
- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er) - Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link - Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest - Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen) - Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
83
app/Services/GeocodingService.php
Executable file
83
app/Services/GeocodingService.php
Executable file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class GeocodingService
|
||||
{
|
||||
/** Erlaubte Geocoding-Hosts (SSRF-Schutz) */
|
||||
private const ALLOWED_HOSTS = [
|
||||
'nominatim.openstreetmap.org',
|
||||
'photon.komoot.io',
|
||||
];
|
||||
|
||||
public function search(string $query): array
|
||||
{
|
||||
$baseUrl = config('nominatim.base_url');
|
||||
|
||||
// SSRF-Schutz: Nur erlaubte Hosts und HTTPS
|
||||
$parsedHost = parse_url($baseUrl, PHP_URL_HOST);
|
||||
if (!$parsedHost || !in_array($parsedHost, self::ALLOWED_HOSTS)) {
|
||||
return [];
|
||||
}
|
||||
if (parse_url($baseUrl, PHP_URL_SCHEME) !== 'https') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheKey = 'geocode:' . hash('sha256', mb_strtolower(trim($query)));
|
||||
|
||||
$cached = Cache::get($cacheKey);
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'User-Agent' => config('nominatim.user_agent'),
|
||||
])->timeout(5)->get($baseUrl . '/search', [
|
||||
'q' => $query,
|
||||
'format' => 'json',
|
||||
'addressdetails' => 1,
|
||||
'namedetails' => 1,
|
||||
'limit' => 5,
|
||||
'countrycodes' => 'de,at,ch',
|
||||
'accept-language' => 'de',
|
||||
]);
|
||||
|
||||
// Fehlerhafte Responses nicht cachen (V20)
|
||||
if ($response->failed()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$results = collect($response->json())->map(function ($item) {
|
||||
$addr = $item['address'] ?? [];
|
||||
|
||||
// Structured address from components
|
||||
$street = trim(($addr['road'] ?? '') . ' ' . ($addr['house_number'] ?? ''));
|
||||
$postcode = $addr['postcode'] ?? '';
|
||||
$city = $addr['city'] ?? $addr['town'] ?? $addr['village'] ?? $addr['municipality'] ?? '';
|
||||
$name = $item['namedetails']['name'] ?? '';
|
||||
|
||||
// Build formatted address line
|
||||
$parts = array_filter([$street, implode(' ', array_filter([$postcode, $city]))]);
|
||||
$formatted = implode(', ', $parts);
|
||||
|
||||
return [
|
||||
'display_name' => $item['display_name'],
|
||||
'formatted_address' => $formatted ?: $item['display_name'],
|
||||
'name' => $name,
|
||||
'street' => $street,
|
||||
'postcode' => $postcode,
|
||||
'city' => $city,
|
||||
'lat' => $item['lat'],
|
||||
'lon' => $item['lon'],
|
||||
'type' => $item['type'] ?? '',
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
Cache::put($cacheKey, $results, 86400);
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user