cmd/pkgserver: add status endpoint

This commit is contained in:
mae
2026-03-09 04:09:18 -05:00
parent 10fe8a0a1e
commit 0cf14847ce

View File

@@ -1,17 +1,20 @@
package main package main
import ( import (
"bytes"
"cmp" "cmp"
"context" "context"
"embed" "embed"
"fmt" "fmt"
"io"
"log" "log"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"path"
"slices" "slices"
"strings"
"syscall" "syscall"
"unique"
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container/check" "hakurei.app/container/check"
@@ -57,68 +60,112 @@ func serveStaticContent(w http.ResponseWriter, r *http.Request) {
} }
} }
func serveAPI(pi *PackageIndex) func(w http.ResponseWriter, r *http.Request) { func serveAPI(index *PackageIndex) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {} return func(w http.ResponseWriter, r *http.Request) {}
} }
func serveStatus(index *PackageIndex) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if index == nil {
http.Error(w, "index is nil", http.StatusInternalServerError)
return
}
base := path.Base(r.URL.Path)
name := strings.TrimSuffix(base, ".log")
p, ok := rosa.ResolveName(name)
if !ok {
http.NotFound(w, r)
return
}
m := rosa.GetMetadata(p)
pk, ok := index.names[m.Name]
if !ok {
http.NotFound(w, r)
return
}
if len(pk.status) > 0 {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.WriteHeader(http.StatusOK)
_, err := io.Copy(w, bytes.NewReader(pk.status))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} else {
http.NotFound(w, r)
}
}
}
type SortOrders int type SortOrders int
const ( const (
DeclarationDescending SortOrders = iota DeclarationAscending SortOrders = iota
DeclarationAscending DeclarationDescending
NameAscending NameAscending
NameDescending NameDescending
limitSortOrders limitSortOrders
) )
type PackageIndex [limitSortOrders][rosa.PresetUnexportedStart]*PackageIndexEntry type PackageIndex struct {
sorts [limitSortOrders][rosa.PresetUnexportedStart]*PackageIndexEntry
names map[string]*PackageIndexEntry
}
type PackageIndexEntry struct { type PackageIndexEntry struct {
id unique.Handle[pkg.ID] Name string `json:"name"`
name string Description string `json:"description"`
description string Website string `json:"website"`
website string Version string `json:"version"`
version string
status []byte status []byte
} }
func createPackageIndex(cache *pkg.Cache, report *rosa.Report) *PackageIndex { func createPackageIndex(cache *pkg.Cache, report *rosa.Report) (_ *PackageIndex, err error) {
var index PackageIndex index := new(PackageIndex)
var work []PackageIndexEntry index.names = make(map[string]*PackageIndexEntry, rosa.PresetUnexportedStart)
work := make([]PackageIndexEntry, rosa.PresetUnexportedStart)
defer report.HandleAccess(&err)()
for p := range rosa.PresetUnexportedStart { for p := range rosa.PresetUnexportedStart {
m := rosa.GetMetadata(p) m := rosa.GetMetadata(p)
v := rosa.Std.Version(p) v := rosa.Std.Version(p)
a := rosa.Std.Load(p) a := rosa.Std.Load(p)
id := cache.Ident(a) id := cache.Ident(a)
status, n := report.ArtifactOf(id) st, n := report.ArtifactOf(id)
work[p] = PackageIndexEntry{ var status []byte
id: id, if n < 1 {
name: m.Name, status = nil
description: m.Description, } else {
website: m.Website, status = st
version: v,
status: status[:n],
} }
log.Printf("Processing package %s...\n", m.Name)
entry := PackageIndexEntry{
Name: m.Name,
Description: m.Description,
Website: m.Website,
Version: v,
status: status,
}
work[p] = entry
index.names[m.Name] = &entry
} }
for i, p := range work { for i, p := range work {
index[DeclarationAscending][i] = &p index.sorts[DeclarationAscending][i] = &p
} }
slices.Reverse(work) slices.Reverse(work)
for i, p := range work { for i, p := range work {
index[DeclarationDescending][i] = &p index.sorts[DeclarationDescending][i] = &p
} }
slices.SortFunc(work, func(a PackageIndexEntry, b PackageIndexEntry) int { slices.SortFunc(work, func(a PackageIndexEntry, b PackageIndexEntry) int {
return cmp.Compare(a.name, b.name) return cmp.Compare(a.Name, b.Name)
}) })
for i, p := range work { for i, p := range work {
index[NameAscending][i] = &p index.sorts[NameAscending][i] = &p
} }
slices.Reverse(work) slices.Reverse(work)
for i, p := range work { for i, p := range work {
index[NameDescending][i] = &p index.sorts[NameDescending][i] = &p
} }
return index, err
return &index
} }
func main() { func main() {
log.SetFlags(0) log.SetFlags(0)
@@ -126,7 +173,7 @@ func main() {
var ( var (
flagBaseDir string flagBaseDir string
flagPort uint16 flagPort int
) )
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
@@ -139,6 +186,7 @@ func main() {
if err != nil { if err != nil {
return err return err
} }
log.Println("baseDir:", baseDir)
cache, err := pkg.Open(ctx, msg, 0, baseDir) cache, err := pkg.Open(ctx, msg, 0, baseDir)
if err != nil { if err != nil {
return err return err
@@ -147,12 +195,19 @@ func main() {
if err != nil { if err != nil {
return err return err
} }
defer report.HandleAccess(&err)() log.Println("reportPath:", reportPath)
index := createPackageIndex(cache, report) log.Println("indexing packages...")
index, err := createPackageIndex(cache, report)
if err != nil {
return err
}
log.Println("created package index")
http.HandleFunc("GET /{$}", serveWebUI) http.HandleFunc("GET /{$}", serveWebUI)
http.HandleFunc("GET /favicon.ico", serveStaticContent) http.HandleFunc("GET /favicon.ico", serveStaticContent)
http.HandleFunc("GET /static/", serveStaticContent) http.HandleFunc("GET /static/", serveStaticContent)
http.HandleFunc("GET /api/", serveAPI(index)) http.HandleFunc("GET /api/", serveAPI(index))
http.HandleFunc("GET /api/status/", serveStatus(index))
log.Println("listening on", flagPort)
err = http.ListenAndServe(fmt.Sprintf(":%d", flagPort), nil) err = http.ListenAndServe(fmt.Sprintf(":%d", flagPort), nil)
if err != nil { if err != nil {
return err return err