going-further/api/server.go
2024-04-22 21:53:20 -04:00

118 lines
3.1 KiB
Go

package api
import (
"context"
"log/slog"
"net"
"net/http"
"net/url"
"time"
"git.yetaga.in/alazyreader/going-further/storage"
)
type LinkProvider interface {
Link(key string) *url.URL
List() []string
Details(key string) storage.Link
}
type SessionProvider interface {
Login(username string, password string) (string, bool) // session token, valid
Valid(token string) bool
User(token string) string // token -> username
}
type UserProvider interface {
List() []string // usernames
Add(username string, password string) error
Update(username string, password string) error
Remove(username string) error
}
type shutdown struct {
wait int
timeout int
}
type Server struct {
host string
port string
version string
users UserProvider
sessions SessionProvider
links LinkProvider
http *http.Server
waits shutdown
log *slog.Logger
}
func NewServer(logger *slog.Logger, port string, version string) *Server {
return &Server{
log: logger,
port: port,
version: version,
waits: shutdown{
wait: 10,
timeout: 10,
},
}
}
// Setup returns start and stop functions for the http service
func (s *Server) Setup() (func(), func()) {
mux := http.NewServeMux()
mux.HandleFunc("GET /version", versionHandleFunc(s.log, s.version))
mux.HandleFunc("GET /login", getLoginHandleFunc(s.sessions, s.log))
mux.HandleFunc("POST /login", postLoginHandleFunc(s.sessions, s.log))
mux.HandleFunc("GET /manage", getManageHandleFunc(s.log))
mux.HandleFunc("GET /manage/links", getLinkListHandleFunc(s.links, s.log))
mux.HandleFunc("GET /manage/links/{name}", getLinkHandleFunc(s.links, s.log))
mux.HandleFunc("POST /manage/links", postLinkHandleFunc(s.links, s.log))
mux.HandleFunc("PUT /manage/links/{name}", putLinkHandleFunc(s.links, s.log))
mux.HandleFunc("DELETE /manage/links/{name}", deleteLinkHandleFunc(s.links, s.log))
mux.HandleFunc("GET /manage/users", getUserListHandleFunc(s.users, s.log))
mux.HandleFunc("GET /manage/users/{name}", getUserHandleFunc(s.users, s.log))
mux.HandleFunc("POST /manage/users", postUserHandleFunc(s.users, s.log))
mux.HandleFunc("PUT /manage/users/{name}", putUserHandleFunc(s.users, s.log))
mux.HandleFunc("DELETE /manage/users/{name}", deleteUserHandleFunc(s.users, s.log))
mux.HandleFunc("GET /static/", staticHandleFunc())
mux.HandleFunc("GET /", linkHandleFunc(s.links, s.log))
s.http = &http.Server{
Addr: net.JoinHostPort(s.host, s.port),
Handler: mux,
}
return s.start(), s.stop()
}
func (s *Server) start() func() {
return func() {
s.log.Info("server startup", slog.String("addr", s.http.Addr))
if err := s.http.ListenAndServe(); err != nil && err != http.ErrServerClosed {
s.log.Error("error listening and serving", slog.String("error", err.Error()))
}
}
}
func (s *Server) stop() func() {
return func() {
s.log.Info("server shutdown")
time.Sleep(time.Duration(s.waits.wait) * time.Second)
shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Duration(s.waits.timeout)*time.Second)
defer cancel()
if err := s.http.Shutdown(shutdownCtx); err != nil {
s.log.Error("error shutting down http server", slog.String("error", err.Error()))
}
}
}