package handlers import ( "encoding/json" "io" "net/http" "net/url" "strings" "time" ) const nominatimURL = "https://nominatim.openstreetmap.org/reverse" var geocodeClient = &http.Client{Timeout: 5 * time.Second} type nominatimResponse struct { DisplayName string `json:"display_name"` } func GeocodeReverse(w http.ResponseWriter, r *http.Request) { lat := r.URL.Query().Get("lat") lon := r.URL.Query().Get("lon") if lat == "" || lon == "" { writeError(w, http.StatusBadRequest, "lat and lon are required", nil) return } u, _ := url.Parse(nominatimURL) q := u.Query() q.Set("lat", lat) q.Set("lon", lon) q.Set("format", "json") q.Set("accept-language", "ru") u.RawQuery = q.Encode() req, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, u.String(), nil) req.Header.Set("User-Agent", "Photoplaces/1.0") resp, err := geocodeClient.Do(req) if err != nil { writeError(w, http.StatusBadGateway, "geocoding service unavailable", err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { // Тело ответа вышестоящего сервиса наружу не отдаём, только логируем. io.Copy(io.Discard, resp.Body) writeJSON(w, http.StatusOK, map[string]interface{}{ "display_name": nil, }) return } var data nominatimResponse if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { writeError(w, http.StatusInternalServerError, "failed to parse geocoding response", err) return } writeJSON(w, http.StatusOK, map[string]interface{}{ "display_name": reverseAddress(data.DisplayName), }) } func reverseAddress(addr string) string { if addr == "" { return "" } parts := strings.Split(addr, ", ") for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 { parts[i], parts[j] = parts[j], parts[i] } return strings.Join(parts, ", ") }