diff --git a/main.go b/main.go index 0449c007dd28ebc2d8517954c419d04f6483f417..517987bd1d0b959f61edf22b18a01f2b32063f05 100644 --- a/main.go +++ b/main.go @@ -33,6 +33,7 @@ func main() { http.HandleFunc("/api/release/", s.handleReleaseMirror) http.HandleFunc("/api/apps/zip/", s.handleAppZip) http.HandleFunc("/api/apps/tar/", s.handleAppTargz) + http.HandleFunc("/api/apps/shame.json", s.handleAppShame) http.HandleFunc("/api/apps/", s.handleApp) log.Printf("Listening on %s...", flagListen) http.ListenAndServe(flagListen, nil) diff --git a/server.go b/server.go index 85cfd4931d1bce090ab3afbab38d79eef9715eb0..a46601a7cffc0ecf78eb43bb84c742ab636eb3b3 100644 --- a/server.go +++ b/server.go @@ -22,7 +22,7 @@ type server struct { type req struct { getReleases chan []GLRelease - getAppRegistry chan []*appDescriptor + getAppRegistry chan *appRegistry } func (s *server) run(ctx context.Context) { @@ -70,11 +70,22 @@ func (s *server) getReleases(ctx context.Context) ([]GLRelease, error) { } func (s *server) getApps(ctx context.Context) ([]*appDescriptor, error) { - sresC := make(chan []*appDescriptor) + sresC := make(chan *appRegistry) select { case s.reqC <- &req{getAppRegistry: sresC}: res := <-sresC - return res, nil + return res.apps, nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func (s *server) getAppShame(ctx context.Context) ([]*appShame, error) { + sresC := make(chan *appRegistry) + select { + case s.reqC <- &req{getAppRegistry: sresC}: + res := <-sresC + return res.shame, nil case <-ctx.Done(): return nil, ctx.Err() } diff --git a/server_apps.go b/server_apps.go index c5ede95057dfeae507088bddeb9ef4d8f3daaaeb..7c324c77a681d7565fbe6df8dd7318e8f7d74b1a 100644 --- a/server_apps.go +++ b/server_apps.go @@ -26,6 +26,16 @@ import ( "github.com/pelletier/go-toml/v2" ) +type appRegistry struct { + shame []*appShame + apps []*appDescriptor +} + +type appShame struct { + repository string + errorMsg string +} + type appDescriptor struct { repository string pathInRepo string @@ -386,7 +396,7 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI return app, nil } -func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { +func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) { s.gitMu.Lock() defer s.gitMu.Unlock() if s.appRepo == nil { @@ -421,7 +431,7 @@ func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { return nil, err } - var res []*appDescriptor + var registry appRegistry for { f, err := iter.Next() if err == io.EOF { @@ -440,6 +450,10 @@ func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { reader, err := f.Reader() if err != nil { log.Printf("App %q: couldn't read TOML: %v", m[1], err) + registry.shame = append(registry.shame, &appShame{ + repository: fmt.Sprintf("<app-list toml: %q>", m[1]), + errorMsg: fmt.Sprintf("couldn't read TOML: %v", err), + }) continue } @@ -452,23 +466,35 @@ func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { reader.Close() if err != nil { log.Printf("App %q: couldn't parse TOML: %v", m[1], err) + registry.shame = append(registry.shame, &appShame{ + repository: fmt.Sprintf("<app-list toml: %q>", m[1]), + errorMsg: fmt.Sprintf("couldn't read TOML: %v", err), + }) continue } n := reAppRepo.FindStringSubmatch(dat.Repo) if n == nil { log.Printf("App %q: invalid repo %q", m[1], dat.Repo) + registry.shame = append(registry.shame, &appShame{ + repository: fmt.Sprintf("<app-list toml: %q>", m[1]), + errorMsg: fmt.Sprintf("invalid repo %q", dat.Repo), + }) continue } - res = append(res, &appDescriptor{ + registry.apps = append(registry.apps, &appDescriptor{ repository: dat.Repo, pathInRepo: dat.PathInRepo, }) } - for _, app := range res { + for _, app := range registry.apps { info, err := s.getAppInfo(ctx, app.pathInRepo, app.repository) if err != nil { log.Printf("App %q: %v", app.repository, err) + registry.shame = append(registry.shame, &appShame{ + repository: app.repository, + errorMsg: fmt.Sprintf("%v", err), + }) continue } else { log.Printf("App: %q: okay", app.repository) @@ -476,8 +502,9 @@ func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { app.appInfo = info } - log.Printf("Got %d apps", len(res)) - return res, nil + log.Printf("Got %d apps and %d apps with shame", len(registry.apps), len(registry.shame)) + + return ®istry, nil } type jsonApp struct { @@ -573,6 +600,39 @@ func (s *server) handleApp(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) } +func (s *server) handleAppShame(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + appShame, err := s.getAppShame(ctx) + if err != nil { + return + } + + type jsonAppShame struct { + Repo string `json:"repo"` + ErrorMsg string `json:"errorMsg"` + } + + type res struct { + DT time.Time `json:"isodatetime"` + Apps []jsonAppShame `json:"shame"` + } + + resp := res{ + DT: time.Now(), + } + for _, shame := range appShame { + // Guarenteed to be exactly 2 parts because of the regex + resp.Apps = append(resp.Apps, jsonAppShame{ + Repo: shame.repository, + ErrorMsg: shame.errorMsg, + }) + } + w.Header().Add("Access-Control-Allow-Origin", "*") + w.Header().Add("Content-Type", "application/json") + j := json.NewEncoder(w) + j.Encode(resp) +} + var ( reAppZipURL = regexp.MustCompile(`^/api/apps/zip/([^/]+)/([^/]+)\.zip$`) reAppTargzURL = regexp.MustCompile(`^/api/apps/tar/([^/]+)/([^/]+)\.tar\.gz$`)