Files
felhom-controller/controller/internal/api/geo.go
T
admin 95c821deb2 feat: comprehensive debug logging across all controller modules
Add detailed [DEBUG] logging to every controller module when
logging.level is set to "debug". Each module with stateful debug
uses SetDebug(bool) wired from main.go. Covers stacks, backup,
cloudflare, integrations, system, monitor, settings, scheduler,
web handlers, storage, metrics, API, selfupdate, and assets.

Also includes the app export/import (.fab bundles) feature from
v0.32.0 and its debug page integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:14:43 +01:00

182 lines
5.5 KiB
Go

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("[API] Failed to save geo settings: %v", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
r.logger.Printf("[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("[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("[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("[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("[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("[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("[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("[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("[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"})
}