package serve
import (
"context"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"gopkg.awl.red/bizdex/auth"
"gopkg.awl.red/bizdex/config"
"gopkg.awl.red/bizdex/db"
"gopkg.awl.red/bizdex/h"
"gopkg.awl.red/bizdex/log"
)
func r___GET(path string, f http.Handler) {
http.Handle("GET "+path, f)
}
func middlewareSession(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" && r.URL.Path == "/login" || r.URL.Path == "login" {
// do login
}
// sessionCookie, err := r.Cookie("Session")
next.ServeHTTP(w, r)
})
}
func r__POST(path string, f http.Handler) {
http.Handle("POST "+path, f)
}
func r_PATCH(path string, f http.Handler) {
http.Handle("PATCH "+path, f)
}
func rDELETE(path string, f http.Handler) {
http.Handle("DELETE "+path, f)
}
func Serve() error {
chAddrChange := make(chan config.AddrChange, 5)
db, err := db.New(config.GetDsn(), chAddrChange)
if err != nil {
return err
}
cfg := db.GetConfig()
log.SetFromConfig(cfg)
if cfg == nil {
return err
}
var wBdTp = func(w http.ResponseWriter, r *http.Request, err error) {
if err == nil {
return
}
slog.Error("could not write route", "route", r.URL.Path, "err", err)
w.WriteHeader(http.StatusInternalServerError)
}
r___GET("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
r___GET("/about", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
wBdTp(w, r, h.TAbout(w, db.GetConfig()))
}))
r___GET("/login", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
wBdTp(w, r, h.TLogin(w, db.GetConfig()))
}))
r___GET("/signup", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
wBdTp(w, r, h.TSignup(w, db.GetConfig()))
}))
r__POST("/signup", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
slog.Error("could not parse form", "route", r.URL.Path, "err", err)
w.WriteHeader(http.StatusBadRequest)
return
}
cfg := db.GetConfig()
signupReq, err := auth.HandleSignupForm(r.Form, cfg)
if err != nil {
slog.Error("one or more signup errors", "route", r.URL.Path, "err", err)
w.WriteHeader(http.StatusBadRequest)
return
}
userId, err := db.InsertUser(signupReq, cfg)
if err != nil {
slog.Error("one or more signup errors", "route", r.URL.Path, "err", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
token, _, err := db.InsertSession(userId, cfg)
cookieVal := fmt.Sprintf(
"Session=%s; Path=/; Max-Age=%d; HttpOnly; Secure; Partitioned; SameSite=Lax",
token,
cfg.SessionLengthMax,
)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Location", "/newlocation/"+strconv.Itoa(userId))
w.Header().Set("Set-Cookie", cookieVal)
w.WriteHeader(http.StatusSeeOther)
}))
addr := cfg.ListenAddress + ":" + strconv.Itoa(int(cfg.Port))
server := &http.Server{Addr: addr}
chSig := make(chan os.Signal, 1)
signal.Notify(chSig, os.Interrupt, syscall.SIGTERM)
go func() {
dieSig := <-chSig
gracefulTime := 1 * time.Second
if dieSig == syscall.SIGTERM {
gracefulTime = 0
}
ctx, cancel := context.WithTimeout(context.Background(), gracefulTime)
slog.Info("signal received, shutting down server", "signal", dieSig, "grace_period", gracefulTime.String())
server.Shutdown(ctx)
cancel()
}()
go func() {
for msg := range chAddrChange {
slog.Info("got address change")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
chAddrChange <- msg
server.Shutdown(ctx)
cancel()
}
}()
stopServeLoop:
for {
slog.Info("listening", "address", cfg.ListenAddress, "port", cfg.Port)
err = server.ListenAndServe()
slog.Info("server stopped listening", "err", err)
select {
case newAddr := <-chAddrChange:
addr = newAddr.Address + ":" + strconv.Itoa(int(newAddr.Port))
server = &http.Server{Addr: addr}
case <-time.After(10 * time.Millisecond):
break stopServeLoop
}
}
return nil
}