Files
go-cart-actor/cmd/backoffice/fileserver.go
matst80 a1ba48053a
All checks were successful
Build and Publish / Metadata (push) Successful in 8s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 1m33s
Build and Publish / BuildAndDeployArm64 (push) Successful in 4m13s
update backoffice
2025-10-15 08:47:05 +02:00

160 lines
3.5 KiB
Go

package main
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"git.tornberg.me/go-cart-actor/pkg/cart"
)
type FileServer struct {
// Define fields here
dataDir string
}
func NewFileServer(dataDir string) *FileServer {
return &FileServer{
dataDir: dataDir,
}
}
func listCartFiles(dir string) ([]CartFileInfo, error) {
entries, err := os.ReadDir(dir)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return []CartFileInfo{}, nil
}
return nil, err
}
out := make([]CartFileInfo, 0)
for _, e := range entries {
if e.IsDir() {
continue
}
name := e.Name()
m := cartFileRe.FindStringSubmatch(name)
if m == nil {
continue
}
idStr := m[1]
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
continue
}
p := filepath.Join(dir, name)
info, err := e.Info()
if err != nil {
continue
}
out = append(out, CartFileInfo{
ID: id,
Path: p,
Size: info.Size(),
Modified: info.ModTime(),
})
}
return out, nil
}
func readRawLogLines(path string) ([]string, error) {
fh, err := os.Open(path)
if err != nil {
return nil, err
}
defer fh.Close()
lines := make([]string, 0, 64)
s := bufio.NewScanner(fh)
// increase buffer to handle larger JSON lines
buf := make([]byte, 0, 1024*1024)
s.Buffer(buf, 1024*1024)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" {
continue
}
lines = append(lines, line)
}
if err := s.Err(); err != nil {
return nil, err
}
return lines, nil
}
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(v)
}
func (fs *FileServer) CartsHandler(w http.ResponseWriter, r *http.Request) {
list, err := listCartFiles(fs.dataDir)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
// sort by modified desc
sort.Slice(list, func(i, j int) bool { return list[i].Modified.After(list[j].Modified) })
writeJSON(w, http.StatusOK, map[string]any{
"count": len(list),
"carts": list,
})
}
func (fs *FileServer) CartHandler(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
if idStr == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing id"})
return
}
// allow both decimal id and filename-like with suffix
if strings.HasSuffix(idStr, ".events.log") {
idStr = strings.TrimSuffix(idStr, ".events.log")
}
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"})
return
}
// reconstruct state from event log if present
grain := cart.NewCartGrain(id, time.Now())
if globalDisk != nil {
_ = globalDisk.LoadEvents(id, grain)
}
path := filepath.Join(fs.dataDir, fmt.Sprintf("%d.events.log", id))
info, err := os.Stat(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
writeJSON(w, http.StatusNotFound, map[string]string{"error": "cart not found"})
return
}
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
lines, err := readRawLogLines(path)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]any{
"id": id,
"state": grain,
"rawLog": lines,
"meta": map[string]any{
"size": info.Size(),
"modified": info.ModTime(),
"path": path,
},
})
}