diff --git a/app.go b/app.go index 31543d6..bb0ea04 100644 --- a/app.go +++ b/app.go @@ -7846,7 +7846,15 @@ func (a *App) runManualUpload(svc extsvc.Service, ids []int64, cfg extsvc.Extern if err != nil { msg = err.Error() } + // Name the QSOs in the failing batch so a per-record rejection + // (e.g. a field value nginx's WAF blocks with a 403) can actually + // be located — otherwise "batch FAILED" hides which contact it is. + who := make([]string, 0, len(batch)) + for _, it := range batch { + who = append(who, fmt.Sprintf("%s#%d", it.call, it.id)) + } emit(fmt.Sprintf("Club Log: batch of %d FAILED: %s", len(batch), msg)) + applog.Printf("extsvc: Club Log batch FAILED (%s) — QSOs: %s", msg, strings.Join(who, ", ")) } } } else { diff --git a/internal/uls/uls.go b/internal/uls/uls.go index 91fd8b5..d156f17 100644 --- a/internal/uls/uls.go +++ b/internal/uls/uls.go @@ -24,6 +24,7 @@ import ( "bufio" "context" "database/sql" + "errors" "fmt" "io" "math" @@ -37,6 +38,10 @@ import ( _ "modernc.org/sqlite" ) +// errFCCMaintenance is raised when the FCC ULS download host bounces us to its +// maintenance page instead of serving the file (a frequent, FCC-side event). +var errFCCMaintenance = errors.New("fcc uls under maintenance") + // Default download URLs (overridable in Import for tests). const ( fccAmateurURL = "https://data.fcc.gov/download/pub/uls/complete/l_amat.zip" @@ -325,8 +330,26 @@ func download(ctx context.Context, url, dest string, prog func(pct int)) error { if err != nil { return err } - resp, err := http.DefaultClient.Do(req) + // Catch the FCC maintenance bounce BEFORE following it: data.fcc.gov redirects + // to www.fcc.gov/system-maintenance during maintenance windows, and that page + // then HTTP/2-stream-errors — which surfaced as a cryptic "INTERNAL_ERROR" + // instead of a plain "try again later". + client := &http.Client{ + CheckRedirect: func(r *http.Request, via []*http.Request) error { + if strings.Contains(r.URL.String(), "system-maintenance") { + return errFCCMaintenance + } + if len(via) >= 10 { + return errors.New("stopped after 10 redirects") + } + return nil + }, + } + resp, err := client.Do(req) if err != nil { + if errors.Is(err, errFCCMaintenance) || strings.Contains(err.Error(), "system-maintenance") { + return fmt.Errorf("the FCC ULS download service is under maintenance (fcc.gov redirected to its maintenance page) — please try again later") + } return err } defer resp.Body.Close()