对齐 SPA 顶栏工具条与清青强调色,CSS/JS 带构建 ?v= 长缓存穿透。 Co-authored-by: Cursor <cursoragent@cursor.com>
143 lines
4.8 KiB
Go
143 lines
4.8 KiB
Go
package routers
|
||
|
||
import (
|
||
"fmt"
|
||
"io/fs"
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"git.iioio.com/freefire/jiang13-forum/config"
|
||
"git.iioio.com/freefire/jiang13-forum/modules/auth"
|
||
webpublic "git.iioio.com/freefire/jiang13-forum/public"
|
||
"git.iioio.com/freefire/jiang13-forum/routers/api"
|
||
"git.iioio.com/freefire/jiang13-forum/routers/install"
|
||
webpages "git.iioio.com/freefire/jiang13-forum/routers/web"
|
||
"git.iioio.com/freefire/jiang13-forum/services"
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
func Setup(cfg *config.Config) (*gin.Engine, error) {
|
||
gin.SetMode(gin.ReleaseMode)
|
||
r := gin.New()
|
||
r.Use(gin.Recovery())
|
||
r.Use(gin.Logger())
|
||
|
||
if err := services.EnsureInstallLockFromExistingData(cfg.DataDir); err != nil {
|
||
fmt.Fprintf(os.Stderr, "警告: 安装锁检查失败: %v\n", err)
|
||
}
|
||
|
||
if sub, err := fs.Sub(webpublic.Assets, "assets"); err == nil {
|
||
ssrFiles := http.StripPrefix("/ssr-assets", http.FileServer(http.FS(sub)))
|
||
r.GET("/ssr-assets/*filepath", func(c *gin.Context) {
|
||
// 模板带 ?v=AssetVersion;部署换版本即穿透,可长缓存
|
||
c.Header("Cache-Control", "public, max-age=31536000, immutable")
|
||
ssrFiles.ServeHTTP(c.Writer, c.Request)
|
||
})
|
||
}
|
||
|
||
r.Use(install.Guard(cfg.DataDir))
|
||
|
||
filter := services.NewSensitiveFilter()
|
||
settingsSvc := services.NewForumSettingsService()
|
||
services.EnsureFilterWordsInSettings(settingsSvc, cfg.FilterWordsPath(), filter)
|
||
|
||
authSvc := services.NewAuthService(cfg.JWTSecret, filter, settingsSvc)
|
||
userSvc := services.NewUserService(filter, settingsSvc)
|
||
boardSvc := services.NewBoardService()
|
||
boardSvc.EnsureDefaultBoard()
|
||
postSvc := services.NewPostService(filter, settingsSvc)
|
||
commentSvc := services.NewCommentService(filter, settingsSvc)
|
||
messageSvc := services.NewMessageService(filter, settingsSvc)
|
||
reportSvc := services.NewReportService(filter, settingsSvc, messageSvc, postSvc, commentSvc)
|
||
backupSvc := services.NewBackupService(cfg.DBPath(), cfg.DataDir)
|
||
limiter := services.NewRateLimiter(settingsSvc)
|
||
captchaSvc := services.NewCaptchaService()
|
||
mailSvc := services.NewMailService(settingsSvc)
|
||
emailCodeSvc := services.NewEmailCodeService(mailSvc)
|
||
notifySvc := services.NewNotifyService(messageSvc, mailSvc, settingsSvc)
|
||
friendLinkApplySvc := services.NewFriendLinkApplyService(settingsSvc, messageSvc)
|
||
oidcSvc, err := services.NewOIDCService(cfg, settingsSvc)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
uploadStore := services.NewUploadStore(cfg.DataDir, settingsSvc)
|
||
if err := uploadStore.ReloadFromSettings(settingsSvc); err != nil {
|
||
fmt.Fprintf(os.Stderr, "警告: 对象存储初始化失败,暂用本地磁盘: %v\n", err)
|
||
_ = uploadStore.Apply(services.StorageConfig{Type: "local"})
|
||
}
|
||
go func() {
|
||
if n, err := uploadStore.SyncMediaIndex(); err != nil {
|
||
fmt.Fprintf(os.Stderr, "警告: 媒体索引同步失败: %v\n", err)
|
||
} else if n > 0 {
|
||
fmt.Fprintf(os.Stderr, "媒体索引已同步 %d 条\n", n)
|
||
}
|
||
}()
|
||
|
||
sitePageSvc := services.NewSitePageService(filter)
|
||
|
||
h := &api.Handlers{
|
||
Cfg: cfg, Settings: settingsSvc,
|
||
Board: boardSvc, Post: postSvc,
|
||
OIDC: oidcSvc,
|
||
}
|
||
authMW := auth.NewAuthMiddleware(authSvc)
|
||
|
||
assetVer := strings.TrimSpace(cfg.Version)
|
||
if assetVer == "" {
|
||
assetVer = "dev"
|
||
}
|
||
|
||
install.Register(r, install.Deps{
|
||
DataDir: cfg.DataDir, JWTSecret: cfg.JWTSecret,
|
||
Auth: authSvc, Settings: settingsSvc,
|
||
AssetVersion: assetVer,
|
||
})
|
||
|
||
webpages.Register(r, webpages.Deps{
|
||
DataDir: cfg.DataDir, JWTSecret: cfg.JWTSecret,
|
||
Settings: settingsSvc, Auth: authSvc, User: userSvc,
|
||
Board: boardSvc, Post: postSvc, Comment: commentSvc,
|
||
Message: messageSvc, Filter: filter,
|
||
Limiter: limiter, EmailCode: emailCodeSvc, Store: uploadStore,
|
||
Points: services.NewPointsService(),
|
||
Badge: services.NewBadgeService(),
|
||
FriendLink: friendLinkApplySvc,
|
||
Mail: mailSvc,
|
||
SitePage: sitePageSvc,
|
||
Report: reportSvc,
|
||
Captcha: captchaSvc,
|
||
Notify: notifySvc,
|
||
Backup: backupSvc,
|
||
AssetVersion: assetVer,
|
||
}, authMW)
|
||
|
||
r.GET("/media/thumb/*filepath", h.ServeImageThumb)
|
||
r.Static("/uploads", filepath.Join(cfg.DataDir, "uploads"))
|
||
r.GET("/health", h.APIHealth)
|
||
r.GET("/robots.txt", h.RobotsTxt)
|
||
r.GET("/sitemap.xml", h.SitemapXML)
|
||
|
||
// OIDC Provider(外部机器 / Gitea SSO)
|
||
r.GET("/.well-known/openid-configuration", h.OIDCDiscovery)
|
||
r.GET("/oauth/jwks", h.OIDCJWKS)
|
||
r.GET("/oauth/authorize", authMW.OptionalAuth(), h.OIDCAuthorize)
|
||
r.POST("/oauth/token", h.OIDCToken)
|
||
r.GET("/oauth/userinfo", h.OIDCUserInfo)
|
||
r.POST("/oauth/userinfo", h.OIDCUserInfo)
|
||
r.GET("/oauth/logout", h.OIDCLogout)
|
||
r.POST("/oauth/logout", h.OIDCLogout)
|
||
|
||
// 机器入口:健康检查 / SEO / OIDC / 缩略图;论坛 UI 仅走 routers/web
|
||
r.NoRoute(webpages.Deps{
|
||
DataDir: cfg.DataDir, JWTSecret: cfg.JWTSecret,
|
||
Settings: settingsSvc, Auth: authSvc,
|
||
Board: boardSvc, Post: postSvc, Comment: commentSvc,
|
||
AssetVersion: assetVer,
|
||
}.NotFound)
|
||
|
||
return r, nil
|
||
}
|