02a4ba0491
- Geo settings save and manual geo sync now fire an out-of-band, non-blocking hub
report push (Router.reportPushNow seam, wired in main.go to BuildReport+Push in a
goroutine) so the hub reflects the new geo state / clears a stale last_sync_error
within seconds instead of after the next ~15-min cycle. Scope: geo handlers only.
- builder.go always populates report.GeoRestriction (Enabled=false, empty countries
when nil/disabled) via new buildGeoRestrictionReport helper, so the hub always
renders the geo section ("Inaktív" when off) instead of hiding it via omitempty.
- Tests: geo save success → push once; invalid country → no push (companion);
buildGeoRestrictionReport(nil) → non-nil disabled (companion vs old nil-omit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
190 lines
6.0 KiB
Go
190 lines
6.0 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("[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)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Push a fresh hub report out-of-band so the hub reflects the new geo state right
|
|
// away instead of after the next ~15-min cycle.
|
|
r.reportPushNow()
|
|
|
|
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)
|
|
return
|
|
}
|
|
// On success the sync clears any stale last_sync_error — push a report so the hub
|
|
// reflects the cleared state immediately rather than after the next cycle.
|
|
r.reportPushNow()
|
|
}()
|
|
|
|
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"})
|
|
}
|