65dc805eb5
- 前端: ESLint+Prettier配置、Tailwind v4配置、ErrorBoundary、全局AuthLoader优化、ReactQuery分层 - 后端: MinIO凭证移除、Docker统一为govai品牌、zerolog日志封装、错误码枚举、文件上传校验、单元测试(13项全通过) - 运维: 健康检查增强(PG/Redis ping)、Prometheus指标(/metrics端点)、多租户tenant包、RateLimit nil防御 - 移动: citation_prompt.txt → internal/assets/
80 lines
2.1 KiB
Go
80 lines
2.1 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"mime/multipart"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/enterprise-ai-platform/server/internal/config"
|
|
)
|
|
|
|
// ValidateFile checks file size, extension, and MIME type.
|
|
// Returns nil if valid, or an error message if invalid.
|
|
func ValidateFile(header *multipart.FileHeader, allowedExtensions []string, maxSize int64) string {
|
|
if header.Size > maxSize {
|
|
return fmt.Sprintf("文件大小超出限制,最大支持 %dMB", maxSize/(1024*1024))
|
|
}
|
|
|
|
ext := strings.ToLower(filepath.Ext(header.Filename))
|
|
if ext != "" {
|
|
ext = ext[1:] // strip leading "."
|
|
}
|
|
|
|
extAllowed := false
|
|
for _, e := range allowedExtensions {
|
|
if strings.EqualFold(e, ext) {
|
|
extAllowed = true
|
|
break
|
|
}
|
|
}
|
|
if !extAllowed {
|
|
return fmt.Sprintf("不支持的文件类型:.%s,仅支持:%s", ext, strings.Join(allowedExtensions, "、"))
|
|
}
|
|
|
|
// Verify MIME type matches extension
|
|
mimeType := header.Header.Get("Content-Type")
|
|
if mimeType != "" {
|
|
extMime, err := mime.ExtensionsByType(mimeType)
|
|
if err == nil && len(extMime) > 0 {
|
|
mimeAllowed := false
|
|
for _, e := range extMime {
|
|
if strings.EqualFold(strings.TrimPrefix(e, "."), ext) {
|
|
mimeAllowed = true
|
|
break
|
|
}
|
|
}
|
|
if !mimeAllowed && !strings.HasPrefix(mimeType, "text/") && !strings.HasPrefix(mimeType, "application/") {
|
|
return "文件类型与实际内容不匹配"
|
|
}
|
|
}
|
|
}
|
|
|
|
return "" // valid
|
|
}
|
|
|
|
// AllowedDocumentExtensions returns the list of allowed document file extensions.
|
|
func AllowedDocumentExtensions() []string {
|
|
return []string{"pdf", "docx", "txt", "md", "csv", "xlsx"}
|
|
}
|
|
|
|
// AllowedDocumentMaxSize returns the max file size for document uploads.
|
|
func AllowedDocumentMaxSize() int64 {
|
|
return config.MaxFileSize
|
|
}
|
|
|
|
// ReadAllWithLimit reads all content from r up to maxSize bytes.
|
|
// Returns error if content exceeds maxSize.
|
|
func ReadAllWithLimit(r io.Reader, maxSize int64) ([]byte, error) {
|
|
limited := &io.LimitedReader{R: r, N: maxSize + 1}
|
|
data, err := io.ReadAll(limited)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if limited.N == 0 {
|
|
return nil, fmt.Errorf("文件内容超出 %dMB 限制", maxSize/(1024*1024))
|
|
}
|
|
return data, nil
|
|
} |