实现 MinIO 对象存储图片流水线(可迁移 OSS)

- 新增 S3 兼容 Storage 抽象与 MinIO 实现,compose 启动 MinIO
- 上传接口:校验 → 缩放 → WebP → 主图/缩略图入库
- 消息图片 content 改为对象 URL,拒绝 base64
- 工作台/访客端改为先上传再发消息
This commit is contained in:
yml2213
2026-07-15 12:21:31 +08:00
parent 85b407fddf
commit ec2f12c6ed
20 changed files with 907 additions and 108 deletions
+22 -12
View File
@@ -1,17 +1,24 @@
package handler
import (
"encoding/base64"
"errors"
"strings"
"unicode/utf8"
"kefu-sys/server/internal/storage"
)
const (
maxTextMessageLength = 2000
maxImageMessageSize = 5 * 1024 * 1024
)
// imagePublicBase 由 SetupRoutes 注入,用于校验图片消息 URL 归属
var imagePublicBase string
func SetImagePublicBase(base string) {
imagePublicBase = storage.NormalizePublicBase(base)
}
func validateMessageContent(messageType, content string) (string, error) {
switch messageType {
case "text":
@@ -24,19 +31,22 @@ func validateMessageContent(messageType, content string) (string, error) {
}
return content, nil
case "image":
parts := strings.SplitN(content, ",", 2)
if len(parts) != 2 {
return "", errors.New("图片格式无效")
content = strings.TrimSpace(content)
if content == "" {
return "", errors.New("图片地址不能为空")
}
if parts[0] != "data:image/jpeg;base64" && parts[0] != "data:image/png;base64" && parts[0] != "data:image/gif;base64" {
return "", errors.New("仅支持 jpg、png、gif 图片")
// 开发阶段直接切换为对象存储 URL,不再接受 base64
if strings.HasPrefix(content, "data:") {
return "", errors.New("请先上传图片,勿直接发送 base64")
}
bytes, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil || len(bytes) == 0 {
return "", errors.New("图片内容无效")
if !strings.HasPrefix(content, "http://") && !strings.HasPrefix(content, "https://") {
return "", errors.New("图片地址无效")
}
if len(bytes) > maxImageMessageSize {
return "", errors.New("图片不能超过 5 MB")
if imagePublicBase != "" && !storage.IsAllowedObjectURL(imagePublicBase, content) {
return "", errors.New("图片地址不在允许的存储域名下")
}
if len(content) > 2000 {
return "", errors.New("图片地址过长")
}
return content, nil
default: