josie / simplegit

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
}