chore: 清理敏感与开发文档,仅同步代码
- 永久忽略并从仓库移除 开发文档/ - 移除并忽略 .env 与小程序私有配置 - 同步小程序/管理端/API与脚本改动 Made-with: Cursor
This commit is contained in:
@@ -9,14 +9,16 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"soul-api/internal/config"
|
||||
"soul-api/internal/oss"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const uploadDir = "uploads"
|
||||
const maxUploadBytes = 5 * 1024 * 1024 // 5MB
|
||||
var allowedTypes = map[string]bool{"image/jpeg": true, "image/png": true, "image/gif": true, "image/webp": true}
|
||||
|
||||
// UploadPost POST /api/upload 上传图片(表单 file)
|
||||
// 若管理端已配置 OSS,优先上传到 OSS;OSS 失败或未配置时回退本地磁盘(容灾)
|
||||
func UploadPost(c *gin.Context) {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
@@ -40,16 +42,41 @@ func UploadPost(c *gin.Context) {
|
||||
if folder == "" {
|
||||
folder = "avatars"
|
||||
}
|
||||
name := fmt.Sprintf("%d_%s%s", time.Now().UnixNano(), randomStrUpload(6), ext)
|
||||
objectKey := filepath.ToSlash(filepath.Join("uploads", folder, name))
|
||||
|
||||
// 优先尝试 OSS(已配置时)
|
||||
if oss.IsEnabled() {
|
||||
f, err := file.Open()
|
||||
if err == nil {
|
||||
url, uploadErr := oss.Upload(objectKey, f)
|
||||
_ = f.Close()
|
||||
if uploadErr == nil && url != "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "url": url, "data": gin.H{"url": url, "fileName": name, "size": file.Size, "type": ct}})
|
||||
return
|
||||
}
|
||||
// OSS 失败,回退本地(容灾)
|
||||
}
|
||||
}
|
||||
|
||||
// 本地磁盘存储(OSS 未配置或失败时)
|
||||
uploadDir := config.Get().UploadDir
|
||||
if uploadDir == "" {
|
||||
uploadDir = "uploads"
|
||||
}
|
||||
dir := filepath.Join(uploadDir, folder)
|
||||
_ = os.MkdirAll(dir, 0755)
|
||||
name := fmt.Sprintf("%d_%s%s", time.Now().UnixNano(), randomStrUpload(6), ext)
|
||||
dst := filepath.Join(dir, name)
|
||||
if err := c.SaveUploadedFile(file, dst); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "保存失败"})
|
||||
return
|
||||
}
|
||||
url := "/" + filepath.ToSlash(filepath.Join(uploadDir, folder, name))
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "url": url, "data": gin.H{"url": url, "fileName": name, "size": file.Size, "type": ct}})
|
||||
relPath := "/uploads/" + filepath.ToSlash(filepath.Join(folder, name))
|
||||
fullURL := relPath
|
||||
if cfg := config.Get(); cfg != nil && cfg.BaseURL != "" {
|
||||
fullURL = cfg.BaseURLJoin(relPath)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "url": fullURL, "data": gin.H{"url": fullURL, "fileName": name, "size": file.Size, "type": ct}})
|
||||
}
|
||||
|
||||
func randomStrUpload(n int) string {
|
||||
@@ -62,17 +89,40 @@ func randomStrUpload(n int) string {
|
||||
}
|
||||
|
||||
// UploadDelete DELETE /api/upload
|
||||
// path 支持:/uploads/xxx(本地)或 https://bucket.oss-xxx.aliyuncs.com/uploads/xxx(OSS)
|
||||
func UploadDelete(c *gin.Context) {
|
||||
path := c.Query("path")
|
||||
if path == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "请指定 path"})
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(path, "/uploads/") && !strings.HasPrefix(path, "uploads/") {
|
||||
// OSS 公网 URL:从 OSS 删除
|
||||
if oss.IsOSSURL(path) {
|
||||
objectKey := oss.ParseObjectKeyFromURL(path)
|
||||
if objectKey != "" {
|
||||
if err := oss.Delete(objectKey); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "OSS 删除失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "删除成功"})
|
||||
return
|
||||
}
|
||||
}
|
||||
// 本地路径:支持 /uploads/xxx、uploads/xxx 或含 /uploads/ 的完整 URL
|
||||
if idx := strings.Index(path, "/uploads/"); idx >= 0 {
|
||||
path = path[idx+1:] // 从 uploads/ 开始
|
||||
}
|
||||
rel := strings.TrimPrefix(path, "/uploads/")
|
||||
rel = strings.TrimPrefix(rel, "uploads/")
|
||||
if rel == "" || strings.Contains(rel, "..") {
|
||||
c.JSON(http.StatusForbidden, gin.H{"success": false, "error": "无权限删除此文件"})
|
||||
return
|
||||
}
|
||||
fullPath := strings.TrimPrefix(path, "/")
|
||||
uploadDir := config.Get().UploadDir
|
||||
if uploadDir == "" {
|
||||
uploadDir = "uploads"
|
||||
}
|
||||
fullPath := filepath.Join(uploadDir, filepath.FromSlash(rel))
|
||||
if err := os.Remove(fullPath); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "文件不存在或删除失败"})
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user