feat: add platform configuration tables and admin settings
Add four DB-backed configuration tables for site-wide settings: site_settings (logo, top-left title, header/footer text with zh/en variants), upload_configs (master switch, default size, storage dir), upload_file_types (per-extension whitelist with per-type size limits), and download_baseurls (multiple download sources with priority/default). - Models, AutoMigrate, and first-run seed (18 common file types) - Process-level config cache warmed at startup, refreshed on admin save - SetUserContext injects cached site settings into templates - base.html renders logo (local upload or external URL), title, header banner, and footer with i18n fallback - Upload validator drives avatar uploads (form + AJAX) through the configured switch/type/size policy - Admin pages for site/upload/download settings with i18n keys Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// SiteSetting holds the singleton (id=1) global display configuration:
|
||||
// logo, top-left title, header banner text, and footer text, each with
|
||||
// zh/en variants that fall back to i18n defaults when empty.
|
||||
type SiteSetting struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
Logo string `gorm:"size:512" json:"logo"` // local filename (served under /uploads/logos) OR a full URL
|
||||
LogoTextZh string `gorm:"size:255" json:"logo_text_zh"` // top-left title (zh)
|
||||
LogoTextEn string `gorm:"size:255" json:"logo_text_en"` // top-left title (en)
|
||||
HeaderTextZh string `gorm:"size:512" json:"header_text_zh"` // header banner text (zh)
|
||||
HeaderTextEn string `gorm:"size:512" json:"header_text_en"` // header banner text (en)
|
||||
FooterTextZh string `gorm:"size:512" json:"footer_text_zh"` // footer text (zh)
|
||||
FooterTextEn string `gorm:"size:512" json:"footer_text_en"` // footer text (en)
|
||||
UpdatedBy uint `gorm:"index" json:"updated_by"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName overrides the default GORM table name.
|
||||
func (SiteSetting) TableName() string {
|
||||
return "site_settings"
|
||||
}
|
||||
|
||||
// LogoIsURL reports whether the logo value is an external URL rather than a
|
||||
// local filename.
|
||||
func (s *SiteSetting) LogoIsURL() bool {
|
||||
if s == nil || s.Logo == "" {
|
||||
return false
|
||||
}
|
||||
return len(s.Logo) >= 4 && (s.Logo[:4] == "http")
|
||||
}
|
||||
|
||||
// LogoText returns the title for the given language code, falling back to the
|
||||
// other language when the requested one is empty.
|
||||
func (s *SiteSetting) LogoText(lang string) string {
|
||||
if lang == "zh" {
|
||||
if s.LogoTextZh != "" {
|
||||
return s.LogoTextZh
|
||||
}
|
||||
return s.LogoTextEn
|
||||
}
|
||||
if s.LogoTextEn != "" {
|
||||
return s.LogoTextEn
|
||||
}
|
||||
return s.LogoTextZh
|
||||
}
|
||||
|
||||
// HeaderText returns the header banner text for the given language code,
|
||||
// falling back to the other language when the requested one is empty.
|
||||
func (s *SiteSetting) HeaderText(lang string) string {
|
||||
if lang == "zh" {
|
||||
if s.HeaderTextZh != "" {
|
||||
return s.HeaderTextZh
|
||||
}
|
||||
return s.HeaderTextEn
|
||||
}
|
||||
if s.HeaderTextEn != "" {
|
||||
return s.HeaderTextEn
|
||||
}
|
||||
return s.HeaderTextZh
|
||||
}
|
||||
|
||||
// FooterText returns the footer text for the given language code, falling
|
||||
// back to the other language when the requested one is empty.
|
||||
func (s *SiteSetting) FooterText(lang string) string {
|
||||
if lang == "zh" {
|
||||
if s.FooterTextZh != "" {
|
||||
return s.FooterTextZh
|
||||
}
|
||||
return s.FooterTextEn
|
||||
}
|
||||
if s.FooterTextEn != "" {
|
||||
return s.FooterTextEn
|
||||
}
|
||||
return s.FooterTextZh
|
||||
}
|
||||
Reference in New Issue
Block a user