package handlers import ( "context" "encoding/json" "html/template" "log/slog" "net/http" "git.eeqj.de/sneak/webhooker/internal/database" "git.eeqj.de/sneak/webhooker/internal/globals" "git.eeqj.de/sneak/webhooker/internal/healthcheck" "git.eeqj.de/sneak/webhooker/internal/logger" "git.eeqj.de/sneak/webhooker/internal/session" "go.uber.org/fx" ) // nolint:revive // HandlersParams is a standard fx naming convention type HandlersParams struct { fx.In Logger *logger.Logger Globals *globals.Globals Database *database.Database Healthcheck *healthcheck.Healthcheck Session *session.Session } type Handlers struct { params *HandlersParams log *slog.Logger hc *healthcheck.Healthcheck db *database.Database session *session.Session } func New(lc fx.Lifecycle, params HandlersParams) (*Handlers, error) { s := new(Handlers) s.params = ¶ms s.log = params.Logger.Get() s.hc = params.Healthcheck s.db = params.Database s.session = params.Session lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { // FIXME compile some templates here or something return nil }, }) return s, nil } //nolint:unparam // r parameter will be used in the future for request context func (s *Handlers) respondJSON(w http.ResponseWriter, r *http.Request, data interface{}, status int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if data != nil { err := json.NewEncoder(w).Encode(data) if err != nil { s.log.Error("json encode error", "error", err) } } } //nolint:unparam,unused // will be used for handling JSON requests func (s *Handlers) decodeJSON(w http.ResponseWriter, r *http.Request, v interface{}) error { return json.NewDecoder(r.Body).Decode(v) } // TemplateData represents the common data passed to templates type TemplateData struct { User *UserInfo Version string UserCount int64 Uptime string } // UserInfo represents user information for templates type UserInfo struct { ID string Username string } // renderTemplate renders a template with common data func (s *Handlers) renderTemplate(w http.ResponseWriter, r *http.Request, templateFiles []string, data interface{}) { // Always include the common templates allTemplates := []string{"templates/htmlheader.html", "templates/navbar.html"} allTemplates = append(allTemplates, templateFiles...) // Parse templates tmpl, err := template.ParseFiles(allTemplates...) if err != nil { s.log.Error("failed to parse template", "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } // Get user from session if available var userInfo *UserInfo sess, err := s.session.Get(r) if err == nil && s.session.IsAuthenticated(sess) { if username, ok := s.session.GetUsername(sess); ok { if userID, ok := s.session.GetUserID(sess); ok { userInfo = &UserInfo{ ID: userID, Username: username, } } } } // Wrap data with base template data type templateDataWrapper struct { User *UserInfo Data interface{} } wrapper := templateDataWrapper{ User: userInfo, Data: data, } // If data is a map, merge user info into it if m, ok := data.(map[string]interface{}); ok { m["User"] = userInfo if err := tmpl.Execute(w, m); err != nil { s.log.Error("failed to execute template", "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } return } // Otherwise use wrapper if err := tmpl.Execute(w, wrapper); err != nil { s.log.Error("failed to execute template", "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } }