package handlers import ( "context" "net/http" "time" "git.haelnorr.com/h/golib/cookies" "git.haelnorr.com/h/golib/hws" "git.haelnorr.com/h/golib/hwsauth" "github.com/pkg/errors" "github.com/uptrace/bun" "git.haelnorr.com/h/oslstats/internal/config" "git.haelnorr.com/h/oslstats/internal/db" "git.haelnorr.com/h/oslstats/internal/store" "git.haelnorr.com/h/oslstats/internal/view/page" ) func Register( server *hws.Server, auth *hwsauth.Authenticator[*db.User, bun.Tx], conn *bun.DB, cfg *config.Config, store *store.Store, ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { attempts, exceeded, track := store.TrackRedirect(r, "/register", 3) if exceeded { err := errors.Errorf( "registration redirect loop detected after %d attempts | ip=%s ua=%s path=%s first_seen=%s ssl=%t", attempts, track.IP, track.UserAgent, track.Path, track.FirstSeen.Format("2006-01-02T15:04:05Z07:00"), cfg.HWSAuth.SSL, ) store.ClearRedirectTrack(r, "/register") throwError( server, w, r, http.StatusBadRequest, "Registration failed: Cookies appear to be blocked or disabled. Please enable cookies in your browser and try again. If this problem persists, try a different browser or contact support.", err, "warn", ) return } sessionCookie, err := r.Cookie("registration_session") if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } details, ok := store.GetRegistrationSession(sessionCookie.Value) if !ok { http.Redirect(w, r, "/login", http.StatusSeeOther) return } store.ClearRedirectTrack(r, "/register") ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() tx, err := conn.BeginTx(ctx, nil) if err != nil { throwInternalServiceError(server, w, r, "Database transaction failed", err) return } defer tx.Rollback() method := r.Method if method == "GET" { tx.Commit() page.Register(details.DiscordUser.Username).Render(r.Context(), w) return } if method == "POST" { username := r.FormValue("username") user, err := registerUser(ctx, tx, username, details) if err != nil { throwInternalServiceError(server, w, r, "Registration failed", err) return } tx.Commit() if user == nil { w.WriteHeader(http.StatusConflict) } else { err = auth.Login(w, r, user, true) if err != nil { throwInternalServiceError(server, w, r, "Login failed", err) return } pageFrom := cookies.CheckPageFrom(w, r) w.Header().Set("HX-Redirect", pageFrom) } return } }, ) } func registerUser( ctx context.Context, tx bun.Tx, username string, details *store.RegistrationSession, ) (*db.User, error) { unique, err := db.IsUsernameUnique(ctx, tx, username) if err != nil { return nil, errors.Wrap(err, "db.IsUsernameUnique") } if !unique { return nil, nil } user, err := db.CreateUser(ctx, tx, username, details.DiscordUser) if err != nil { return nil, errors.Wrap(err, "db.CreateUser") } err = user.UpdateDiscordToken(ctx, tx, details.Token) if err != nil { return nil, errors.Wrap(err, "db.UpdateDiscordToken") } return user, nil }