fix: 完成 Gitea 目录改组收尾(import、构建与 LICENSE)

同步包路径与路由,去掉 SPA 构建步骤,对齐 Gitea 式 LICENSE,并更新规格/规则与占位 SSR。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-29 04:01:20 +08:00
parent 9fe299a45f
commit 3f50316ad0
93 changed files with 1472 additions and 1522 deletions

View File

@@ -1,4 +1,4 @@
package handler
package api
import (
"encoding/base64"
@@ -10,58 +10,58 @@ import (
"github.com/gin-gonic/gin"
"git.iioio.com/freefire/jiang13-forum/config"
"git.iioio.com/freefire/jiang13-forum/middleware"
"git.iioio.com/freefire/jiang13-forum/model"
"git.iioio.com/freefire/jiang13-forum/service"
"git.iioio.com/freefire/jiang13-forum/modules/auth"
"git.iioio.com/freefire/jiang13-forum/models"
"git.iioio.com/freefire/jiang13-forum/services"
)
// Handlers 聚合所有 HTTP 处理器
type Handlers struct {
Cfg *config.Config
Store *service.UploadStore
Auth *service.AuthService
User *service.UserService
Board *service.BoardService
Post *service.PostService
Comment *service.CommentService
Message *service.MessageService
Notify *service.NotifyService
Report *service.ReportService
Backup *service.BackupService
Filter *service.SensitiveFilter
Limiter *service.RateLimiter
Settings *service.ForumSettingsService
Captcha *service.CaptchaService
Mail *service.MailService
EmailCode *service.EmailCodeService
OIDC *service.OIDCService
Gitea *service.GiteaService
Points *service.PointsService
Badge *service.BadgeService
SitePage *service.SitePageService
FriendLinkApply *service.FriendLinkApplyService
Store *services.UploadStore
Auth *services.AuthService
User *services.UserService
Board *services.BoardService
Post *services.PostService
Comment *services.CommentService
Message *services.MessageService
Notify *services.NotifyService
Report *services.ReportService
Backup *services.BackupService
Filter *services.SensitiveFilter
Limiter *services.RateLimiter
Settings *services.ForumSettingsService
Captcha *services.CaptchaService
Mail *services.MailService
EmailCode *services.EmailCodeService
OIDC *services.OIDCService
Gitea *services.GiteaService
Points *services.PointsService
Badge *services.BadgeService
SitePage *services.SitePageService
FriendLinkApply *services.FriendLinkApplyService
}
func (h *Handlers) setAuthCookie(c *gin.Context, token string) {
c.SetCookie(middleware.CookieName, token, int(service.TokenExpire.Seconds()), "/", "", false, true)
c.SetCookie(auth.CookieName, token, int(services.TokenExpire.Seconds()), "/", "", false, true)
}
func (h *Handlers) currentUserID(c *gin.Context) uint {
if v, ok := c.Get(middleware.CtxUserID); ok {
if v, ok := c.Get(auth.CtxUserID); ok {
return v.(uint)
}
return 0
}
func (h *Handlers) isAdmin(c *gin.Context) bool {
if v, ok := c.Get(middleware.CtxRole); ok {
return v == model.RoleAdmin
if v, ok := c.Get(auth.CtxRole); ok {
return v == models.RoleAdmin
}
return false
}
// loadCurrentUser 加载当前登录用户完整资料(含认证/积分)
func (h *Handlers) loadCurrentUser(c *gin.Context) (*model.User, error) {
func (h *Handlers) loadCurrentUser(c *gin.Context) (*models.User, error) {
uid := h.currentUserID(c)
if uid == 0 {
return nil, errors.New("未登录")
@@ -133,7 +133,7 @@ func (h *Handlers) APIRegisterConfig(c *gin.Context) {
"mail_ready": mailReady,
"require_email_code": mailReady,
"register_open": userCount == 0 || mailReady,
"email_code_len": service.EmailCodeLen,
"email_code_len": services.EmailCodeLen,
})
}
@@ -147,7 +147,7 @@ func (h *Handlers) APISendRegisterEmailCode(c *gin.Context) {
return
}
if !h.Settings.MailReady() {
c.JSON(http.StatusBadRequest, gin.H{"error": service.ErrMailNotConfigured.Error()})
c.JSON(http.StatusBadRequest, gin.H{"error": services.ErrMailNotConfigured.Error()})
return
}
if err := h.EmailCode.SendRegisterCode(req.Email); err != nil {
@@ -167,7 +167,7 @@ func (h *Handlers) APISendResetEmailCode(c *gin.Context) {
return
}
if !h.Settings.MailReady() {
c.JSON(http.StatusBadRequest, gin.H{"error": service.ErrMailNotConfigured.Error()})
c.JSON(http.StatusBadRequest, gin.H{"error": services.ErrMailNotConfigured.Error()})
return
}
if err := h.EmailCode.SendResetCode(req.Email); err != nil {
@@ -189,11 +189,11 @@ func (h *Handlers) APIResetPassword(c *gin.Context) {
return
}
if !h.Settings.MailReady() {
c.JSON(http.StatusBadRequest, gin.H{"error": service.ErrMailNotConfigured.Error()})
c.JSON(http.StatusBadRequest, gin.H{"error": services.ErrMailNotConfigured.Error()})
return
}
if !h.EmailCode.VerifyPurpose(service.EmailCodePurposeReset, req.Email, req.EmailCode) {
c.JSON(http.StatusBadRequest, gin.H{"error": service.ErrEmailCodeInvalid.Error()})
if !h.EmailCode.VerifyPurpose(services.EmailCodePurposeReset, req.Email, req.EmailCode) {
c.JSON(http.StatusBadRequest, gin.H{"error": services.ErrEmailCodeInvalid.Error()})
return
}
if err := h.User.ResetPasswordByEmail(req.Email, req.NewPassword); err != nil {
@@ -244,12 +244,12 @@ func (h *Handlers) APIRegister(c *gin.Context) {
userCount := h.Auth.UserCount()
mailReady := h.Settings.MailReady()
if userCount > 0 && !mailReady {
c.JSON(http.StatusBadRequest, gin.H{"error": service.ErrRegisterClosed.Error()})
c.JSON(http.StatusBadRequest, gin.H{"error": services.ErrRegisterClosed.Error()})
return
}
if mailReady {
if !h.EmailCode.Verify(req.Email, req.EmailCode) {
c.JSON(http.StatusBadRequest, gin.H{"error": service.ErrEmailCodeInvalid.Error()})
c.JSON(http.StatusBadRequest, gin.H{"error": services.ErrEmailCodeInvalid.Error()})
return
}
}
@@ -283,7 +283,7 @@ func (h *Handlers) APILogin(c *gin.Context) {
}
func (h *Handlers) APILogout(c *gin.Context) {
c.SetCookie(middleware.CookieName, "", -1, "/", "", false, true)
c.SetCookie(auth.CookieName, "", -1, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{"message": "已退出"})
}
@@ -323,7 +323,7 @@ func (h *Handlers) APIUserPublic(c *gin.Context) {
if h.Badge != nil {
_ = h.Badge.EvaluateAuto(user.ID)
if badges, bErr := h.Badge.ListUserBadges(user.ID); bErr == nil {
view.Badges = service.BadgeViews(badges, 0)
view.Badges = services.BadgeViews(badges, 0)
}
}
c.JSON(http.StatusOK, gin.H{
@@ -396,10 +396,10 @@ func (h *Handlers) APIUploadPostImage(c *gin.Context) {
return
}
uid := h.currentUserID(c)
url, err := service.SaveUploadedImage(
url, err := services.SaveUploadedImage(
h.Store,
file,
service.UploadCategoryPosts,
services.UploadCategoryPosts,
fmt.Sprintf("%d", uid),
)
if err != nil {
@@ -421,20 +421,20 @@ func (h *Handlers) APICreatePost(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
extras := service.ParsePostExtrasFromForm(
extras := services.ParsePostExtrasFromForm(
c.PostForm("poll_options"),
c.PostForm("bounty_points"),
c.PostForm("lottery_winner_count"),
)
if post.PostType == model.PostTypePoll || post.PostType == model.PostTypeBounty || post.PostType == model.PostTypeLottery {
if err := service.FinalizeSpecialPostCreate(post, h.currentUserID(c), extras); err != nil {
if post.PostType == models.PostTypePoll || post.PostType == models.PostTypeBounty || post.PostType == models.PostTypeLottery {
if err := services.FinalizeSpecialPostCreate(post, h.currentUserID(c), extras); err != nil {
_ = h.Post.Delete(h.currentUserID(c), post.ID, true)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
}
msg := "发帖成功"
if post.Status == model.ContentStatusPending {
if post.Status == models.ContentStatusPending {
msg = "已提交审核,通过后将公开显示"
if h.Notify != nil {
h.Notify.AsyncNotifyPendingPost(post)
@@ -479,8 +479,8 @@ func (h *Handlers) APIToggleLike(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var post model.Post
model.DB.First(&post, id)
var post models.Post
models.DB.First(&post, id)
c.JSON(http.StatusOK, gin.H{"liked": liked, "like_count": post.LikeCount})
}
@@ -535,7 +535,7 @@ func (h *Handlers) APICreateComment(c *gin.Context) {
return
}
in := service.CommentCreateInput{
in := services.CommentCreateInput{
UserID: uid,
PostID: uint(postID),
Content: content,
@@ -551,14 +551,14 @@ func (h *Handlers) APICreateComment(c *gin.Context) {
msg := "评论成功"
if h.Notify != nil {
switch comment.Status {
case model.ContentStatusPublished:
case models.ContentStatusPublished:
h.Notify.AsyncNotifyCommentPublished(comment)
h.Notify.AsyncNotifyCommentMentions(comment)
case model.ContentStatusPending:
case models.ContentStatusPending:
msg = "评论已提交,审核通过后公开显示"
h.Notify.AsyncNotifyPendingComment(comment)
}
} else if comment.Status == model.ContentStatusPending {
} else if comment.Status == models.ContentStatusPending {
msg = "评论已提交,审核通过后公开显示"
}
c.JSON(http.StatusOK, gin.H{"message": msg, "floor": comment.Floor, "id": comment.ID, "status": comment.Status})
@@ -586,7 +586,7 @@ func (h *Handlers) APIUpdateComment(c *gin.Context) {
status := ""
if comment, e := h.Comment.GetByID(uint(id)); e == nil {
status = comment.Status
if status == model.ContentStatusPending && !h.isAdmin(c) {
if status == models.ContentStatusPending && !h.isAdmin(c) {
msg = "评论已更新,审核通过后公开显示"
}
if enteredPending && h.Notify != nil {