package api import ( "context" "encoding/json" "net/http" cf "gitea.dooplex.hu/admin/felhom-controller/internal/cloudflare" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" ) func (r *Router) geoStatus(w http.ResponseWriter, _ *http.Request) { geo := r.sett.GetGeoRestriction() data := map[string]interface{}{ "cf_configured": r.cfg.Infrastructure.CFAPIToken != "", "geo_available": r.geoSync != nil, } if geo != nil { data["enabled"] = geo.Enabled data["allowed_countries"] = geo.AllowedCountries data["app_overrides"] = geo.AppOverrides data["last_sync"] = geo.LastSync data["last_sync_error"] = geo.LastSyncError data["syncing"] = r.geoSync != nil && r.geoSync.IsRunning() } else { data["enabled"] = false data["allowed_countries"] = []string{"HU"} } writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: data}) } func (r *Router) geoUpdateSettings(w http.ResponseWriter, req *http.Request) { limitBody(w, req) r.dbg("geoUpdateSettings: contentLength=%d", req.ContentLength) var body struct { Enabled bool `json:"enabled"` AllowedCountries []string `json:"allowed_countries"` } if err := json.NewDecoder(req.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "invalid request body"}) return } // Validate country codes for _, code := range body.AllowedCountries { if !cf.ValidCountryCode(code) { writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "invalid country code: " + code}) return } } // Get existing settings to preserve app overrides and sync state existing := r.sett.GetGeoRestriction() geo := &settings.GeoRestriction{ Enabled: body.Enabled, AllowedCountries: body.AllowedCountries, } if existing != nil { geo.AppOverrides = existing.AppOverrides geo.ZoneID = existing.ZoneID geo.RulesetID = existing.RulesetID } if err := r.sett.SetGeoRestriction(geo); err != nil { r.logger.Printf("[ERROR] [api] Failed to save geo settings: %v", err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } r.logger.Printf("[INFO] [api] Geo settings updated: enabled=%v, countries=%v", body.Enabled, body.AllowedCountries) // Trigger async CF sync if r.geoSync != nil { go func() { if err := r.geoSync.Sync(context.Background()); err != nil { r.logger.Printf("[ERROR] [api] Geo sync after settings update failed: %v", err) } }() } writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Geo-korlátozás beállítva"}) } func (r *Router) geoTriggerSync(w http.ResponseWriter, _ *http.Request) { if r.geoSync == nil { writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "Cloudflare API nincs konfigurálva"}) return } go func() { if err := r.geoSync.Sync(context.Background()); err != nil { r.logger.Printf("[ERROR] [api] Manual geo sync failed: %v", err) } }() writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Szinkronizálás elindítva"}) } func (r *Router) geoCountries(w http.ResponseWriter, _ *http.Request) { writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: cf.AllCountries()}) } func (r *Router) geoSetAppOverride(w http.ResponseWriter, req *http.Request, appName string) { if appName == "" { writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "invalid app name"}) return } limitBody(w, req) var body struct { AllowedCountries []string `json:"allowed_countries"` } if err := json.NewDecoder(req.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "invalid request body"}) return } // Validate country codes for _, code := range body.AllowedCountries { if !cf.ValidCountryCode(code) { writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "invalid country code: " + code}) return } } // Verify app exists if _, ok := r.stackMgr.GetStack(appName); !ok { writeJSON(w, http.StatusNotFound, apiResponse{OK: false, Error: "app not found: " + appName}) return } override := &settings.AppGeoOverride{AllowedCountries: body.AllowedCountries} if err := r.sett.SetGeoAppOverride(appName, override); err != nil { r.logger.Printf("[ERROR] [api] Failed to save geo override for %s: %v", appName, err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } r.logger.Printf("[INFO] [api] Geo override set for %s: countries=%v", appName, body.AllowedCountries) // Trigger async CF sync if r.geoSync != nil { go func() { if err := r.geoSync.Sync(context.Background()); err != nil { r.logger.Printf("[ERROR] [api] Geo sync after app override failed: %v", err) } }() } writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Alkalmazás geo-korlátozás beállítva"}) } func (r *Router) geoRemoveAppOverride(w http.ResponseWriter, _ *http.Request, appName string) { if appName == "" { writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "invalid app name"}) return } if err := r.sett.RemoveGeoAppOverride(appName); err != nil { r.logger.Printf("[ERROR] [api] Failed to remove geo override for %s: %v", appName, err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } r.logger.Printf("[INFO] [api] Geo override removed for %s", appName) // Trigger async CF sync if r.geoSync != nil { go func() { if err := r.geoSync.Sync(context.Background()); err != nil { r.logger.Printf("[ERROR] [api] Geo sync after override removal failed: %v", err) } }() } writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Alkalmazás geo-korlátozás eltávolítva"}) }