package main import ( "bytes" "crypto/sha256" "encoding/hex" "errors" "html/template" "path/filepath" "strings" "time" "github.com/gofiber/fiber/v3" "gorm.io/gorm" "prada.ch/controllers" ) type controller struct { db *gorm.DB mailConfig smtpConfig authController *controllers.AuthController subscribeController *controllers.SubscribeController assetVersion string landingTemplate *template.Template howToWorkTemplate *template.Template loginTemplate *template.Template signupTemplate *template.Template forgotPasswordTemplate *template.Template resetPasswordTemplate *template.Template welcomeTemplate *template.Template } const sessionCookieName = "bag_exchange_session" func (h *controller) staticFile(c fiber.Ctx) error { relativePath := filepath.Clean(c.Params("*")) if relativePath == "." || strings.HasPrefix(relativePath, "..") { return c.SendStatus(fiber.StatusNotFound) } return c.SendFile(filepath.Join("static", relativePath)) } func (h *controller) home(c fiber.Ctx) error { data := landingData{ Brand: "Bag Exchange", InitialTitle: "Bag Exchange | Swap bags and handbags", FooterText: "© 2026 Bag Exchange · Bag and handbag exchange between individuals", AssetVersion: h.assetVersion, } var out bytes.Buffer if err := h.landingTemplate.Execute(&out, data); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("unable to render page") } c.Type("html", "utf-8") return c.SendString(out.String()) } func (h *controller) resetPasswordPage(c fiber.Ctx) error { token := strings.TrimSpace(c.Query("token")) var out bytes.Buffer if err := h.resetPasswordTemplate.Execute(&out, resetPasswordPageData{Token: token}); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("unable to render reset password page") } c.Type("html", "utf-8") return c.SendString(out.String()) } func (h *controller) howToWorkPage(c fiber.Ctx) error { var out bytes.Buffer if err := h.howToWorkTemplate.Execute(&out, nil); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("unable to render howtowork page") } c.Type("html", "utf-8") return c.SendString(out.String()) } func (h *controller) loginPage(c fiber.Ctx) error { var out bytes.Buffer if err := h.loginTemplate.Execute(&out, nil); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("unable to render login page") } c.Type("html", "utf-8") return c.SendString(out.String()) } func (h *controller) signupPage(c fiber.Ctx) error { var out bytes.Buffer if err := h.signupTemplate.Execute(&out, nil); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("unable to render signup page") } c.Type("html", "utf-8") return c.SendString(out.String()) } func (h *controller) forgotPasswordPage(c fiber.Ctx) error { var out bytes.Buffer if err := h.forgotPasswordTemplate.Execute(&out, nil); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("unable to render forgot password page") } c.Type("html", "utf-8") return c.SendString(out.String()) } func (h *controller) welcomePage(c fiber.Ctx) error { email, ok := h.currentUserEmail(c) if !ok { return c.Redirect().To("/login") } var out bytes.Buffer if err := h.welcomeTemplate.Execute(&out, map[string]string{"Email": email}); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("unable to render welcome page") } c.Type("html", "utf-8") return c.SendString(out.String()) } func (h *controller) currentUserEmail(c fiber.Ctx) (string, bool) { sessionToken := c.Cookies(sessionCookieName) if sessionToken == "" { return "", false } var sessionUser struct { Email string `gorm:"column:email"` } err := h.db.Table("sessions"). Select("users.email"). Joins("JOIN users ON users.id = sessions.user_id"). Where("sessions.token_hash = ? AND sessions.expires_at > ?", hashSessionToken(sessionToken), time.Now().Unix()). Take(&sessionUser).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { clearAuthSessionCookie(c) return "", false } return "", false } return sessionUser.Email, true } func hashSessionToken(value string) string { sum := sha256.Sum256([]byte(value)) return hex.EncodeToString(sum[:]) } func clearAuthSessionCookie(c fiber.Ctx) { c.Cookie(&fiber.Cookie{ Name: sessionCookieName, Value: "", Path: "/", HTTPOnly: true, Secure: false, SameSite: "Lax", Expires: time.Unix(0, 0), MaxAge: -1, }) }