all repos — searchix @ 383ee780613116e78db9114a39a2d6127533463c

Search engine for NixOS, nix-darwin, home-manager and NUR users

feat: show last/next/current indexing run time
Alan Pearce alan@alanpearce.eu
Wed, 19 Mar 2025 20:49:46 +0100
commit

383ee780613116e78db9114a39a2d6127533463c

parent

49e3004d33bf84aa081460e4a6d89a8d84cc12b0

M internal/components/data.gointernal/components/data.go
@@ -1,11 +1,20 @@ package components
 
 import (
+	"time"
+
 	"go.alanpearce.eu/searchix/frontend"
 	"go.alanpearce.eu/searchix/internal/config"
 	search "go.alanpearce.eu/searchix/internal/index"
 	"go.alanpearce.eu/searchix/internal/nix"
 )
+
+var Indexing struct {
+	InProgress bool
+	StartedAt  time.Time
+	FinishedAt time.Time
+	NextRun    time.Time
+}
 
 type TemplateData struct {
 	Sources       []*config.Source
@@ -34,3 +43,22 @@ } 
 	return &i
 }
+
+func SetNextRun(nextRun time.Time) {
+	Indexing.NextRun = nextRun
+}
+
+func SetLastUpdated(last time.Time) {
+	Indexing.FinishedAt = last
+}
+
+func MarkIndexingStarted() {
+	Indexing.StartedAt = time.Now()
+	Indexing.InProgress = true
+}
+
+func MarkIndexingFinished(nextRun time.Time) {
+	Indexing.FinishedAt = time.Now()
+	Indexing.InProgress = false
+	Indexing.NextRun = nextRun
+}
M internal/components/search.gointernal/components/search.go
@@ -1,6 +1,8 @@ package components
 
 import (
+	"time"
+
 	g "go.alanpearce.eu/gomponents"
 	. "go.alanpearce.eu/gomponents/html"
 )
@@ -39,6 +41,40 @@ g.Text(", "), 			A(Href("https://github.com/LnL7/nix-darwin"), g.Text("nix-darwin")),
 			g.Text(" and "),
 			A(Href("https://github.com/nix-community/home-manager"), g.Text("home-manager")),
+		),
+		g.If(Indexing.InProgress,
+			P(Class("notice"),
+				g.Text("Indexing in progress, started "),
+				Time(
+					DateTime(Indexing.StartedAt.Format(time.RFC3339)),
+					Title(Indexing.StartedAt.Format(time.RFC3339)),
+					g.Text(time.Since(Indexing.StartedAt).Round(time.Second).String()),
+				),
+				g.Text(" ago. "),
+				g.If(!Indexing.FinishedAt.IsZero(),
+					g.Text("Last run took "),
+					Time(
+						DateTime(Indexing.FinishedAt.Format(time.RFC3339)),
+						Title(Indexing.FinishedAt.Format(time.RFC3339)),
+						g.Text(time.Since(Indexing.FinishedAt).Round(time.Minute).String()),
+					),
+				),
+			),
+			P(
+				g.Text("Indexing last ran "),
+				Time(
+					DateTime(Indexing.FinishedAt.Format(time.RFC3339)),
+					Title(Indexing.FinishedAt.Format(time.RFC3339)),
+					g.Text(time.Since(Indexing.FinishedAt).Round(time.Minute).String()),
+				),
+				g.Text(" ago, will run again in "),
+				Time(
+					DateTime(Indexing.NextRun.Format(time.RFC3339)),
+					Title(Indexing.NextRun.Format(time.RFC3339)),
+					g.Text(time.Until(Indexing.NextRun).Round(time.Minute).String()),
+				),
+				g.Text("."),
+			),
 		),
 		script(tdata.Assets.ByPath["/static/search.js"]),
 		Search(tdata, r),
M internal/index/index_meta.gointernal/index/index_meta.go
@@ -118,3 +118,14 @@ i.data.Sources = make(map[string]*SourceMeta) 	}
 	i.data.Sources[source] = meta
 }
+
+func (i *Meta) LastUpdated() time.Time {
+	var last time.Time
+	for _, sourceMeta := range i.data.Sources {
+		if sourceMeta.Updated.After(last) {
+			last = sourceMeta.Updated
+		}
+	}
+
+	return last
+}
M internal/index/search.gointernal/index/search.go
@@ -4,6 +4,7 @@ import ( 	"bytes"
 	"context"
 	"encoding/gob"
+	"time"
 
 	"go.alanpearce.eu/searchix/internal/config"
 	"go.alanpearce.eu/searchix/internal/nix"
@@ -31,6 +32,10 @@ type ReadIndex struct { 	index bleve.Index
 	log   *log.Logger
 	meta  *Meta
+}
+
+func (index *ReadIndex) LastUpdated() time.Time {
+	return index.meta.LastUpdated()
 }
 
 func (index *ReadIndex) GetEnabledSources() ([]string, errors.E) {
M searchix.gosearchix.go
@@ -7,6 +7,7 @@ "slices" 	"sync"
 	"time"
 
+	"go.alanpearce.eu/searchix/internal/components"
 	"go.alanpearce.eu/searchix/internal/config"
 	"go.alanpearce.eu/searchix/internal/importer"
 	"go.alanpearce.eu/searchix/internal/index"
@@ -125,6 +126,7 @@ } 			}
 		}
 	}
+	components.SetLastUpdated(read.LastUpdated())
 
 	return nil
 }
@@ -175,6 +177,7 @@ } 
 		s.wg.Add(1)
 		nextRun := nextUTCOccurrenceOfTime(s.cfg.Importer.UpdateAt)
+		components.SetNextRun(nextRun)
 		for {
 			s.log.Debug("scheduling next run", "next-run", nextRun)
 			select {
@@ -192,6 +195,7 @@ eventID := localHub.CaptureCheckIn(&sentry.CheckIn{ 				MonitorSlug: monitorSlug,
 				Status:      sentry.CheckInStatusInProgress,
 			}, monitorConfig)
+			components.MarkIndexingStarted()
 
 			imp := importer.New(s.cfg, s.log.Named("importer"), s.writeIndex)
 			err = imp.Start(ctx, false, nil)
@@ -215,6 +219,7 @@ Status:      sentry.CheckInStatusOK, 				}, monitorConfig)
 			}
 			nextRun = nextRun.AddDate(0, 0, 1)
+			components.MarkIndexingFinished(nextRun)
 		}
 	})
 }