package web import ( "context" "net/http" "strings" "time" "git.josie-c.com/josie/simplegit/internal/db" ) const ( sessionCookieName = "session" sessionDuration = 30 * 24 * time.Hour ) type contextKey int const userKey contextKey = iota // withUser resolves a valid session cookie into a *db.User on the // request context. Requests without a session pass through anonymous. func (s *Server) withUser(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie(sessionCookieName) if err == nil && cookie.Value != "" { session, err := db.GetSession(s.database, cookie.Value) if err == nil { user, err := db.GetUserByID(s.database, session.UserID) if err == nil { r = r.WithContext(context.WithValue(r.Context(), userKey, &user)) } } } next.ServeHTTP(w, r) }) } // currentUser returns the authenticated user, or nil for anonymous requests. func currentUser(r *http.Request) *db.User { user, _ := r.Context().Value(userKey).(*db.User) return user } // sessionCookie builds the session cookie. TLS terminates at the reverse // proxy, so the Secure flag follows the configured base URL's scheme. func (s *Server) sessionCookie(value string, maxAge time.Duration) *http.Cookie { return &http.Cookie{ Name: sessionCookieName, Value: value, Path: "/", MaxAge: int(maxAge.Seconds()), HttpOnly: true, SameSite: http.SameSiteLaxMode, Secure: strings.HasPrefix(s.cfg.BaseURL, "https://"), } } // navData is the model shared by repo pages — who is viewing, which repo, // which tab. Templates read the promoted fields unchanged. type navData struct { Username string Owner string RepoName string Visibility string Active string } // nav builds navData for a repo page from the request and viewer. func nav(r *http.Request, repo db.Repo, user *db.User, active string) navData { n := navData{ Owner: r.PathValue("user"), RepoName: repo.Name, Visibility: repo.Visibility, Active: active, } if user != nil { n.Username = user.Username } return n }