krply/cmd/krply-server/main.go
lakshit verma 2e15d84dd6
feat(server): add query api, cli, metrics, audit, and web ui
- grpc query server for events, snapshots, diffs, and replay plans
- server metrics, request audit logging, and identity endpoint
- krply-server and krply cli entrypoints
- dependency-light dark web ui backed by the query api
2026-08-06 05:33:59 +05:30

87 lines
2 KiB
Go

// Command krply-server runs the krply query API and serves the web UI.
package main
import (
"context"
"errors"
"flag"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/krply/krply/internal/api"
"github.com/krply/krply/internal/materialize"
"github.com/krply/krply/internal/metrics"
"github.com/krply/krply/internal/replay"
"github.com/krply/krply/internal/storage"
)
const version = "0.1.0"
func main() {
if err := run(); err != nil {
slog.Error("krply-server failed", "err", err)
os.Exit(1)
}
}
func run() error {
var (
storePath = flag.String("store", "krply.db", "path to the SQLite journal")
listen = flag.String("listen", ":8080", "listen address")
showVer = flag.Bool("version", false, "print version and exit")
)
flag.Parse()
if *showVer {
fmt.Println(version)
return nil
}
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, nil)))
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
store, err := storage.NewSQLiteStore(*storePath)
if err != nil {
return fmt.Errorf("open store: %w", err)
}
defer store.Close()
mat := materialize.NewMaterializer(store)
planner := replay.NewPlanner(store, mat, replay.DefaultPolicy())
m := metrics.New()
m.RefreshFromStore(ctx, store)
srv, err := api.NewServer(store, mat, planner, m, version)
if err != nil {
return fmt.Errorf("build api server: %w", err)
}
httpSrv := &http.Server{Addr: *listen, Handler: srv.Handler()}
errCh := make(chan error, 1)
go func() {
slog.Info("krply-server listening", "addr", *listen, "store", *storePath, "version", version)
errCh <- httpSrv.ListenAndServe()
}()
select {
case <-ctx.Done():
slog.Info("shutting down")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return httpSrv.Shutdown(shutdownCtx)
case err := <-errCh:
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}
}