Newer
Older
kitlog "github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/jmoiron/sqlx"
"modernc.org/ql"
_ "modernc.org/ql/driver"
)
//go:embed templates/*.tpl
var templateFS embed.FS
//go:embed static/*
var staticFS embed.FS
// Build info. Will be set by Gitlab pipeline.
var (
commitHash string
buildTime string
)
func httpError(w http.ResponseWriter, r *http.Request, msg string, err error, status int) {
level.Error(log.Get(r)).
Log("status", status,
"error", err,
"msg", msg)
type sessionProvider interface {
ListSessions(context.Context) ([]session.Info, error)
DeleteSession(ctx context.Context, token string) error
}
type userProvider interface {
ListUsers(context.Context) ([]string, error)
CreateUser(ctx context.Context, name string) (string, error)
DeleteUser(ctx context.Context, name string) error
UpdatePassword(ctx context.Context, name string, oldPW, newPW string) error
}
sp sessionProvider
up userProvider
func addCacheHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Cache-Control", "public, max-age=86400, immutable")
next.ServeHTTP(w, r)
})
}
func openDB(path string, logger kitlog.Logger) (*sqlx.DB, error) {
var retried bool
path += ".ql"
retry:
db, err := sqlx.Open("ql2", path)
if err != nil {
return nil, err
}
_, err = db.Exec(`SELECT count(*) FROM __Table`)
if err != nil && !retried {
level.Error(logger).
Log("error", err,
"msg", "got error")
retried = true
// WAL may be corrupted. Manually remove it and re-try open. See
// https://gitlab.com/cznic/ql/-/issues/227 for more info.
err := os.Remove(ql.WalName(path))
if err != nil {
return nil, err
}
goto retry
}
if err != nil {
return nil, err
}
return db, nil
}
// wrapMiddleware wraps common middleware around hdlr:
// - log.Request to make a logger available in the request context
// - addCacheHeader for caching
// - auth.Require
func wrapMiddleware(hdlr http.Handler, sessions session.Provider, logger kitlog.Logger) http.Handler {
authFailed := log.Request(sessions.Handler(templateFS), logger)
return log.Request(auth.Require(hdlr, authFailed, sessions), logger)
dbPath := flag.String("db", "vino", "Path to database file")
listenAddr := flag.String("listen", "127.0.0.1:7878", "Listening address")
debug := flag.Bool("debug", false, "Enable debug logging")
logger := kitlog.NewLogfmtLogger(kitlog.NewSyncWriter(os.Stdout))
logger = kitlog.With(logger, "ts", kitlog.DefaultTimestampUTC)
filter := level.AllowInfo()
Log("commit", commitHash, "build", buildTime)
logger.Log("error", err, "msg", "can't open DB")
os.Exit(1)
ctx, done := context.WithCancel(context.Background())
defer done()
err = initDB(ctx, db)
level.Error(logger).
Log("wal_name", ql.WalName(*dbPath+".ql"),
"error", err, "msg", "can't initalize DB")
http.HandleFunc("/favicon.ico", http.NotFound)
http.Handle("/static/", log.Request(addCacheHeaders(http.FileServer(http.FS(staticFS))), logger))
sessions := session.Provider{
sp: sessions,
up: sessions,
http.Handle("/details/img/", addCacheHeaders(wrapMiddleware(handler.img(), sessions, logger)))
http.Handle("/details/", wrapMiddleware(handler.details(), sessions, logger))
http.Handle("/user/sessions", wrapMiddleware(handler.user(pageSessions), sessions, logger))
http.Handle("/user/all-users", wrapMiddleware(handler.user(pageAllUsers), sessions, logger))
http.Handle("/user/you", wrapMiddleware(handler.user(pageYou), sessions, logger))
http.Handle("/", wrapMiddleware(handler.index(), sessions, logger))
srv := http.Server{
Addr: *listenAddr,
}
Log("addr", "http://"+*listenAddr, "msg", "starting http server")
errs := make(chan error, 1)
go func() {
errs <- http.ListenAndServe(*listenAddr, nil)
}()
// Wait for OS signal or error, shut down server if signal received
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
select {
case sig := <-sigChan:
level.Info(logger).
Log("signal", sig, "msg", "received signal, terminating")
shutdownCtx, done := context.WithTimeout(context.Background(), 15*time.Second)
defer done()
err = srv.Shutdown(shutdownCtx)
if err != nil {
level.Error(logger).
Log("error", err, "msg", "clean shut down failed")
}
case err = <-errs:
Log("error", err, "msg", "HTTP server failed to start")