Features: - Custom navigation links management in admin settings - Multi-language support for navigation links (Chinese/English) - Control link behavior (open in new window) - Sort order and enable/disable toggle for nav links - User registration system with validation - Admin can enable/disable user registration - Registration form with username, display name, email, password - Link to registration from login page Navigation Links: - Admin interface to add/edit/delete custom nav links - Support for both internal and external URLs - Display links in header navigation bar - Respects language preference User Registration: - Username validation (3-32 characters, alphanumeric + underscore/dash) - Password validation (minimum 6 characters) - Password confirmation matching - Optional display name and email fields - Can be toggled on/off by admin in site settings Templates: - Added navigation links settings page - Added user registration page - Updated login page with registration link - Updated base layout to render custom nav links Documentation: - NAV_LINKS_FEATURE.md - Navigation links feature documentation - REGISTRATION_FEATURE.md - User registration documentation - IMPLEMENTATION_SUMMARY.md - Overall implementation summary Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
141 lines
3.6 KiB
Go
141 lines
3.6 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-contrib/sessions"
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"go_blog/i18n"
|
|
"go_blog/models"
|
|
)
|
|
|
|
// AuthRequired is middleware that protects routes. If the user is not logged in,
|
|
// they are redirected to /login.
|
|
func AuthRequired() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
session := sessions.Default(c)
|
|
userID := session.Get("user_id")
|
|
if userID == nil {
|
|
c.Redirect(http.StatusFound, "/login")
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// AdminRequired is middleware that restricts a route to admin-role users. It
|
|
// must run after AuthRequired (which guarantees a session user exists). Non-admin
|
|
// users are redirected back to the admin dashboard.
|
|
func AdminRequired(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
session := sessions.Default(c)
|
|
userID := session.Get("user_id")
|
|
if userID == nil {
|
|
c.Redirect(http.StatusFound, "/login")
|
|
c.Abort()
|
|
return
|
|
}
|
|
var user models.User
|
|
if err := db.First(&user, userID).Error; err != nil || user.Role != models.RoleAdmin {
|
|
c.Redirect(http.StatusFound, "/admin")
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// SetUserContext is global middleware that reads the session and sets
|
|
// template-friendly context values for all pages (language, auth state, etc.).
|
|
func SetUserContext(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
session := sessions.Default(c)
|
|
|
|
// --- Language detection ---
|
|
// Priority: query param > session > Accept-Language header > default EN
|
|
var lang i18n.Lang
|
|
queryLang := c.Query("lang")
|
|
|
|
switch queryLang {
|
|
case "zh":
|
|
lang = i18n.ZH
|
|
case "en":
|
|
lang = i18n.EN
|
|
case "":
|
|
// Try session
|
|
if saved, ok := session.Get("lang").(string); ok {
|
|
lang = i18n.Lang(saved)
|
|
}
|
|
if lang == "" {
|
|
// Try Accept-Language header
|
|
lang = i18n.DetectLang(c.GetHeader("Accept-Language"))
|
|
}
|
|
default:
|
|
// Unsupported language in query — fall back to English.
|
|
lang = i18n.EN
|
|
}
|
|
|
|
// Persist language in session.
|
|
session.Set("lang", string(lang))
|
|
session.Save()
|
|
|
|
// Make translations available in the Gin context.
|
|
c.Set("tr", i18n.T(lang))
|
|
c.Set("lang", string(lang))
|
|
|
|
// Set the opposite language code for the language switcher link.
|
|
switchLang := "zh"
|
|
if lang == i18n.ZH {
|
|
switchLang = "en"
|
|
}
|
|
c.Set("switch_lang", switchLang)
|
|
|
|
// --- Auth state ---
|
|
userID := session.Get("user_id")
|
|
isLoggedIn := false
|
|
var username string
|
|
var avatar string
|
|
var displayName string
|
|
var role string
|
|
|
|
if userID != nil {
|
|
isLoggedIn = true
|
|
var user models.User
|
|
if err := db.First(&user, userID).Error; err == nil {
|
|
username = user.Username
|
|
avatar = user.Avatar
|
|
displayName = user.DisplayName
|
|
role = user.Role
|
|
}
|
|
}
|
|
|
|
c.Set("is_logged_in", isLoggedIn)
|
|
c.Set("username", username)
|
|
c.Set("avatar", avatar)
|
|
c.Set("display_name", displayName)
|
|
c.Set("role", role)
|
|
|
|
// --- Site platform configuration (from DB cache) ---
|
|
site := models.GetSiteSetting()
|
|
c.Set("site_setting", site)
|
|
c.Set("site_logo", site.Logo)
|
|
c.Set("site_logo_is_url", site.LogoIsURL())
|
|
c.Set("site_favicon", site.Favicon)
|
|
c.Set("site_favicon_is_url", site.FaviconIsURL())
|
|
c.Set("site_logo_text", site.LogoText(string(lang)))
|
|
c.Set("site_header_text", site.HeaderText(string(lang)))
|
|
c.Set("site_home_welcome", site.HomeWelcome(string(lang)))
|
|
c.Set("site_home_subtitle", site.HomeSubtitle(string(lang)))
|
|
c.Set("site_footer_text", site.FooterText(string(lang)))
|
|
|
|
// --- Navigation links (from DB cache) ---
|
|
navLinks := models.GetNavLinks()
|
|
c.Set("nav_links", navLinks)
|
|
|
|
c.Next()
|
|
}
|
|
}
|