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 }