90 lines
2.4 KiB
Go
90 lines
2.4 KiB
Go
package controller
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/rand"
|
||
"encoding/hex"
|
||
"image"
|
||
|
||
"metazone.cc/mce/internal/model"
|
||
"metazone.cc/mce/internal/service"
|
||
|
||
"github.com/chai2010/webp"
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// PostController 帖子控制器(SSR 页面 + API)
|
||
type PostController struct {
|
||
postService postUseCase
|
||
shortcodeSvc *service.ShortcodeService
|
||
}
|
||
|
||
// NewPostController 构造函数
|
||
func NewPostController(ps postUseCase, scs *service.ShortcodeService) *PostController {
|
||
return &PostController{postService: ps, shortcodeSvc: scs}
|
||
}
|
||
|
||
// allowedImageExt 允许的图片扩展名
|
||
var allowedImageExt = map[string]bool{
|
||
".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true,
|
||
}
|
||
|
||
// visitorCookieName Cookie 名称
|
||
const visitorCookieName = "visitor_id"
|
||
|
||
// visitorCookieMaxAge 访客 Cookie 有效期(30 天)
|
||
const visitorCookieMaxAge = 30 * 24 * 3600
|
||
|
||
// getVisitorID 获取或创建访客标识 Cookie
|
||
func (ctrl *PostController) getVisitorID(c *gin.Context) string {
|
||
if cookie, err := c.Cookie(visitorCookieName); err == nil && cookie != "" {
|
||
return cookie
|
||
}
|
||
// 生成 16 字节随机 hex(32 字符)
|
||
b := make([]byte, 16)
|
||
if _, err := rand.Read(b); err != nil {
|
||
return ""
|
||
}
|
||
vid := hex.EncodeToString(b)
|
||
c.SetCookie(visitorCookieName, vid, visitorCookieMaxAge, "/", "", false, true)
|
||
return vid
|
||
}
|
||
|
||
// applyShortcode 处理帖子正文中的 shortcode 标记,替换为 HTML 占位符
|
||
func (ctrl *PostController) applyShortcode(post *model.Post) {
|
||
if ctrl.shortcodeSvc != nil {
|
||
result := ctrl.shortcodeSvc.Process(post.Body)
|
||
post.Body = result.ProcessedBody
|
||
}
|
||
}
|
||
|
||
// encodeWebPBinary 二分查找 WebP 质量,找 ≤ targetBytes 的最高质量(quality 1~80)
|
||
// 返回 nil 表示最低质量仍超限(几乎不可能发生),应由调用方回退原格式
|
||
func encodeWebPBinary(img image.Image, targetBytes int) []byte {
|
||
var buf bytes.Buffer
|
||
if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err != nil {
|
||
return nil
|
||
}
|
||
if buf.Len() <= targetBytes {
|
||
return buf.Bytes()
|
||
}
|
||
|
||
low, high := 1, 80
|
||
var best []byte
|
||
for low <= high {
|
||
mid := (low + high) / 2
|
||
buf.Reset()
|
||
if err := webp.Encode(&buf, img, &webp.Options{Quality: float32(mid)}); err != nil {
|
||
return nil
|
||
}
|
||
if buf.Len() <= targetBytes {
|
||
best = make([]byte, buf.Len())
|
||
copy(best, buf.Bytes())
|
||
low = mid + 1
|
||
} else {
|
||
high = mid - 1
|
||
}
|
||
}
|
||
return best
|
||
}
|