Files
mce/cmd/server/main.go
Victor_Jay 4d212a8f8a feat: 收藏夹系统 + Studio 趋势图 + 通知偏好设置
- 新增收藏夹系统 (Folder/FolderItem 模型、CRUD API、前端管理页)
- 新增文章详情页收藏按钮 (书签图标、ToggleFavorite 一键收藏)
- 新增 Studio 趋势图 (赋能值/评论/点赞/收藏 4 线图, 7d/30d/90d)
- 新增通知偏好设置页 (评论/回复/点赞/关注 4 类开关)
- 后端通知列表支持按偏好过滤 (排除已关闭的通知类型)
- 下载 Chart.js 到本地静态资源 (static/js/chart.umd.min.js)
- 补充收藏夹卡片、开关滑块、趋势图网格等 CSS 样式
2026-06-01 17:33:59 +08:00

105 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"log"
"metazone.cc/metalab/internal/common"
"metazone.cc/metalab/internal/config"
"metazone.cc/metalab/internal/model"
"metazone.cc/metalab/internal/router"
"metazone.cc/metalab/internal/theme"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func main() {
// 配置
cfg := config.Load("config.yaml")
// 从配置初始化角色系统OCP新增角色无需修改源代码
model.InitRoles(cfg.Roles.Levels, cfg.Roles.Names, cfg.Roles.Permissions.OperableRoles)
// 数据库
db, err := gorm.Open(postgres.Open(cfg.Database.DSN()), &gorm.Config{})
if err != nil {
log.Fatalf("连接数据库失败: %v", err)
}
if err := db.AutoMigrate(&model.User{}, &model.AuditSubmission{}, &model.SiteSetting{}, &model.Notification{}, &model.Post{}, &model.UserCheckIn{}, &model.UserTask{}, &model.EnergyLog{}, &model.PostEnergizeLog{}, &model.DailyExpSummary{}, &model.Comment{}, &model.CommentMention{}, &model.CommunityFund{}, &model.FundLog{}, &model.PostLike{}, &model.PostDislike{}, &model.UserFollow{}, &model.DailyLikeSummary{}, &model.Folder{}, &model.FolderItem{}); err != nil {
log.Fatalf("数据库迁移失败: %v", err)
}
// 初始化公户行id=1, balance=0如已存在则忽略
if err := db.FirstOrCreate(&model.CommunityFund{ID: 1, Balance: 0}).Error; err != nil {
log.Fatalf("初始化公户失败: %v", err)
}
// 邮箱唯一索引迁移:从全表唯一改为部分唯一(仅 deleted_at IS NULL 的行)
// 支持软删除用户邮箱复用
migrations := []string{
`DROP INDEX IF EXISTS idx_users_email`,
`CREATE INDEX IF NOT EXISTS idx_users_email ON users (email)`,
`CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_active ON users (email) WHERE deleted_at IS NULL`,
}
for _, m := range migrations {
if err := db.Exec(m).Error; err != nil {
log.Fatalf("数据库迁移失败 (%s): %v", m, err)
}
}
log.Println("数据库连接成功")
// Gin 模式
gin.SetMode(cfg.Server.Mode)
r := gin.Default()
// 计算静态资源哈希(用于缓存破坏,文件不变哈希不变)
common.ComputeAssetHashes("templates/MetaLab-2026")
// 模板加载(前端主题 + 管理后台)
tmpl, err := theme.LoadTemplates(
theme.TemplateRoot{Dir: "templates/MetaLab-2026/html", Prefix: ""},
theme.TemplateRoot{Dir: "templates/admin/html", Prefix: "admin/"},
)
if err != nil {
panic("加载模板失败: " + err.Error())
}
r.SetHTMLTemplate(tmpl)
// 静态资源
r.Static("/static", "./templates/MetaLab-2026/static")
r.Static("/admin/static", "./templates/admin/static")
r.Static("/shared/static", "./templates/shared/static")
r.Static("/uploads/avatars", "./storage/uploads/avatars")
r.Static("/uploads/posts", "./storage/uploads/posts")
// Redis 客户端(可选,不可达时 FallbackStore 自动降级,恢复后自动切回)
var redisClient *redis.Client
if cfg.Redis.Enabled {
redisClient, err = common.NewRedisClient(cfg.Redis)
if err != nil {
log.Printf("Redis 不可达,启动后首次请求将自动降级,后续恢复自动切回: %v", err)
// 不置 nilgo-redis 自带重连FallbackStore 负责健康检测与恢复
} else {
log.Printf("Redis 已连接 %s:%s (DB=%d)", cfg.Redis.Host, cfg.Redis.Port, cfg.Redis.DB)
}
} else {
log.Println("Redis 未启用,使用内存存储")
}
// 站点设置管理器DB 持久化 + 内存缓存)
siteSettings, err := config.NewSiteSettings(db)
if err != nil {
log.Fatalf("初始化站点设置失败: %v", err)
}
// 路由注册
router.Setup(r, db, cfg, siteSettings, redisClient)
// 启动
log.Printf("MetaZone.FAN 启动于 0.0.0.0:%s", cfg.Server.Port)
r.Run("0.0.0.0:" + cfg.Server.Port)
}