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>
134 lines
3.7 KiB
Go
134 lines
3.7 KiB
Go
package models
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// configCache holds process-level caches of platform configuration so that
|
|
// per-request rendering and upload validation do not hit the database. The
|
|
// cache is populated once at startup and refreshed whenever an admin saves a
|
|
// settings page (see RefreshConfigCache / the admin handlers).
|
|
var configCache = struct {
|
|
mu sync.RWMutex
|
|
site *SiteSetting
|
|
upload *UploadConfig
|
|
comment *CommentConfig
|
|
types []UploadFileType
|
|
baseURLs []DownloadBaseURL
|
|
navLinks []NavLink
|
|
}{
|
|
site: &SiteSetting{},
|
|
upload: &UploadConfig{Enabled: true, DefaultMaxSize: DefaultUploadMaxSize, StorageDir: "attachments"},
|
|
}
|
|
|
|
// LoadConfigCache reads all platform configuration from the database into the
|
|
// process cache. Called once at startup after InitDB.
|
|
func LoadConfigCache(db *gorm.DB) {
|
|
configCache.mu.Lock()
|
|
defer configCache.mu.Unlock()
|
|
|
|
var s SiteSetting
|
|
if err := db.First(&s, 1).Error; err == nil {
|
|
configCache.site = &s
|
|
} else {
|
|
configCache.site = &SiteSetting{ID: 1}
|
|
}
|
|
|
|
var u UploadConfig
|
|
if err := db.First(&u, 1).Error; err == nil {
|
|
configCache.upload = &u
|
|
} else {
|
|
configCache.upload = &UploadConfig{ID: 1, Enabled: true, DefaultMaxSize: DefaultUploadMaxSize, StorageDir: "attachments"}
|
|
}
|
|
|
|
var cc CommentConfig
|
|
if err := db.First(&cc, 1).Error; err == nil {
|
|
configCache.comment = &cc
|
|
} else {
|
|
configCache.comment = defaultCommentConfig()
|
|
}
|
|
|
|
var t []UploadFileType
|
|
if err := db.Order("sort asc, id asc").Find(&t).Error; err == nil {
|
|
configCache.types = t
|
|
}
|
|
|
|
var b []DownloadBaseURL
|
|
if err := db.Order("is_default desc, priority asc, id asc").Find(&b).Error; err == nil {
|
|
configCache.baseURLs = b
|
|
}
|
|
|
|
var n []NavLink
|
|
if err := db.Where("enabled = ?", true).Order("sort asc, id asc").Find(&n).Error; err == nil {
|
|
configCache.navLinks = n
|
|
}
|
|
}
|
|
|
|
// RefreshConfigCache reloads all cached platform configuration. Admin handlers
|
|
// call this after writing changes so the next request sees them.
|
|
func RefreshConfigCache(db *gorm.DB) {
|
|
LoadConfigCache(db)
|
|
}
|
|
|
|
// GetSiteSetting returns a pointer to the cached site settings (read-only copy
|
|
// semantics: callers must not mutate).
|
|
func GetSiteSetting() *SiteSetting {
|
|
configCache.mu.RLock()
|
|
defer configCache.mu.RUnlock()
|
|
return configCache.site
|
|
}
|
|
|
|
// GetUploadConfig returns the cached upload policy.
|
|
func GetUploadConfig() *UploadConfig {
|
|
configCache.mu.RLock()
|
|
defer configCache.mu.RUnlock()
|
|
return configCache.upload
|
|
}
|
|
|
|
// GetCommentConfig returns the cached comment policy.
|
|
func GetCommentConfig() *CommentConfig {
|
|
configCache.mu.RLock()
|
|
defer configCache.mu.RUnlock()
|
|
return configCache.comment
|
|
}
|
|
|
|
// GetUploadFileTypes returns the cached list of permitted file types.
|
|
func GetUploadFileTypes() []UploadFileType {
|
|
configCache.mu.RLock()
|
|
defer configCache.mu.RUnlock()
|
|
return configCache.types
|
|
}
|
|
|
|
// GetDownloadBaseURLs returns the cached list of download base URLs.
|
|
func GetDownloadBaseURLs() []DownloadBaseURL {
|
|
configCache.mu.RLock()
|
|
defer configCache.mu.RUnlock()
|
|
return configCache.baseURLs
|
|
}
|
|
|
|
// DefaultDownloadBaseURL returns the base URL used to build attachment download
|
|
// links: the enabled row marked IsDefault, else the highest-priority enabled
|
|
// row. Returns an empty string if none is configured.
|
|
func DefaultDownloadBaseURL() string {
|
|
configCache.mu.RLock()
|
|
defer configCache.mu.RUnlock()
|
|
|
|
// Rows are ordered is_default desc, priority asc, so the first enabled row
|
|
// is the right pick.
|
|
for _, b := range configCache.baseURLs {
|
|
if b.Enabled {
|
|
return b.BaseURL
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetNavLinks returns the cached list of enabled navigation links, sorted by sort order.
|
|
func GetNavLinks() []NavLink {
|
|
configCache.mu.RLock()
|
|
defer configCache.mu.RUnlock()
|
|
return configCache.navLinks
|
|
}
|