Scheduled searches with a weekly review of new and rising repositories

A search can now be saved with a schedule (every N hours, daily, or weekly at
a given day and time). An internal scheduler runs it and keeps a snapshot of
the results on a /data volume (JSON files, no new dependency). The new Watch
tab lists the scheduled searches; the review page of each one shows, for any
run of its history, the repositories never seen before and the ones that
gained the most stars since the previous run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude BotandClaude Opus 5.5 committed 2026-10-01 16:36:50 +02:00
1 parent 8f4048d046
commit 02b51fed77
16 files changed
+1745 -45

No files matched your search

+18 -2
View File
@@ -12,8 +12,10 @@ import (
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"
_ "time/tzdata" // TZ works in the minimal Docker image
)
//go:embed web
@@ -33,6 +35,13 @@ func getenvDuration(key string, def time.Duration) time.Duration {
return def
}
func getenvInt(key string, def int) int {
if n, err := strconv.Atoi(os.Getenv(key)); err == nil && n >= 0 {
return n
}
return def
}
func main() {
httpAddr := getenv("HTTP_ADDR", ":8080")
token := os.Getenv("GITHUB_TOKEN")
@@ -45,8 +54,15 @@ func main() {
if err != nil {
log.Fatal(err)
}
store, err := OpenStore(getenv("DATA_DIR", "data"), getenvInt("HISTORY_KEEP", 100))
if err != nil {
log.Fatalf("data: %v", err)
}
sched := NewScheduler(store, gh)
sched.Start(ctx)
mux := http.NewServeMux()
(&API{gh: gh}).Routes(mux)
(&API{gh: gh, store: store, sched: sched}).Routes(mux)
mux.Handle("GET /", http.FileServer(http.FS(static)))
srv := &http.Server{
@@ -65,7 +81,7 @@ func main() {
if token == "" {
log.Printf("GITHUB_TOKEN not set: GitHub allows only 10 searches per minute")
}
log.Printf("web UI on %s", httpAddr)
log.Printf("web UI on %s, data in %s, time zone %s", httpAddr, getenv("DATA_DIR", "data"), time.Local)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("http: %v", err)
}