文章编辑器问题
This commit is contained in:
@@ -49,6 +49,9 @@ type Config struct {
|
||||
|
||||
// 订单对账定时任务间隔(分钟),0 表示不启动内置定时任务
|
||||
SyncOrdersIntervalMinutes int
|
||||
|
||||
// 上传目录(绝对路径,air 运行时避免相对路径解析错误)
|
||||
UploadDir string
|
||||
}
|
||||
|
||||
// BaseURLJoin 将路径拼接到 BaseURL,path 应以 / 开头
|
||||
@@ -239,6 +242,14 @@ func Load() (*Config, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 上传目录:优先 UPLOAD_DIR 环境变量,否则用项目根下的 uploads
|
||||
uploadDir := strings.TrimSpace(os.Getenv("UPLOAD_DIR"))
|
||||
if uploadDir == "" {
|
||||
uploadDir = resolveUploadDir(workDir, execDir)
|
||||
} else if !filepath.IsAbs(uploadDir) {
|
||||
uploadDir, _ = filepath.Abs(filepath.Join(workDir, uploadDir))
|
||||
}
|
||||
|
||||
return &Config{
|
||||
Port: port,
|
||||
Mode: mode,
|
||||
@@ -265,5 +276,21 @@ func Load() (*Config, error) {
|
||||
AdminPassword: adminPassword,
|
||||
AdminSessionSecret: adminSessionSecret,
|
||||
SyncOrdersIntervalMinutes: syncOrdersInterval,
|
||||
UploadDir: uploadDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// resolveUploadDir 解析上传目录绝对路径(air 运行时 exe 在 tmp/,需用项目根)
|
||||
func resolveUploadDir(workDir, execDir string) string {
|
||||
root := workDir
|
||||
if execDir != "" {
|
||||
base := filepath.Base(execDir)
|
||||
if base == "tmp" {
|
||||
root = filepath.Dir(execDir)
|
||||
} else {
|
||||
root = execDir
|
||||
}
|
||||
}
|
||||
abs, _ := filepath.Abs(filepath.Join(root, "uploads"))
|
||||
return abs
|
||||
}
|
||||
|
||||
@@ -306,8 +306,47 @@ func DBBookAction(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "sections": sections, "total": len(sections)})
|
||||
return
|
||||
case "read":
|
||||
midStr := c.Query("mid")
|
||||
if midStr != "" {
|
||||
// 优先用 mid 获取(管理端编辑、小程序跳转推荐)
|
||||
mid, err := strconv.Atoi(midStr)
|
||||
if err != nil || mid < 1 {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "mid 必须为正整数"})
|
||||
return
|
||||
}
|
||||
var ch model.Chapter
|
||||
if err := db.Where("mid = ?", mid).First(&ch).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "章节不存在"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
price := 1.0
|
||||
if ch.Price != nil {
|
||||
price = *ch.Price
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"section": gin.H{
|
||||
"id": ch.ID,
|
||||
"title": ch.SectionTitle,
|
||||
"price": price,
|
||||
"content": ch.Content,
|
||||
"isNew": ch.IsNew,
|
||||
"partId": ch.PartID,
|
||||
"partTitle": ch.PartTitle,
|
||||
"chapterId": ch.ChapterID,
|
||||
"chapterTitle": ch.ChapterTitle,
|
||||
"editionStandard": ch.EditionStandard,
|
||||
"editionPremium": ch.EditionPremium,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
if id == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 id"})
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 id 或 mid"})
|
||||
return
|
||||
}
|
||||
var ch model.Chapter
|
||||
|
||||
@@ -71,7 +71,7 @@ func SearchGet(c *gin.Context) {
|
||||
price = *ch.Price
|
||||
}
|
||||
results = append(results, gin.H{
|
||||
"id": ch.ID, "title": ch.SectionTitle, "partTitle": ch.PartTitle, "chapterTitle": ch.ChapterTitle,
|
||||
"id": ch.ID, "mid": ch.MID, "title": ch.SectionTitle, "partTitle": ch.PartTitle, "chapterTitle": ch.ChapterTitle,
|
||||
"price": price, "isFree": ch.IsFree, "matchType": matchType, "score": score, "snippet": snippet,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"soul-api/internal/config"
|
||||
|
||||
"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}
|
||||
|
||||
@@ -40,6 +40,10 @@ func UploadPost(c *gin.Context) {
|
||||
if folder == "" {
|
||||
folder = "avatars"
|
||||
}
|
||||
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)
|
||||
@@ -48,8 +52,12 @@ func UploadPost(c *gin.Context) {
|
||||
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 {
|
||||
@@ -72,7 +80,13 @@ func UploadDelete(c *gin.Context) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"success": false, "error": "无权限删除此文件"})
|
||||
return
|
||||
}
|
||||
fullPath := strings.TrimPrefix(path, "/")
|
||||
rel := strings.TrimPrefix(path, "/uploads/")
|
||||
rel = strings.TrimPrefix(rel, "uploads/")
|
||||
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
|
||||
|
||||
@@ -28,7 +28,11 @@ func Setup(cfg *config.Config) *gin.Engine {
|
||||
rateLimiter := middleware.NewRateLimiter(100, 200)
|
||||
r.Use(rateLimiter.Middleware())
|
||||
|
||||
r.Static("/uploads", "./uploads")
|
||||
uploadDir := cfg.UploadDir
|
||||
if uploadDir == "" {
|
||||
uploadDir = "./uploads"
|
||||
}
|
||||
r.Static("/uploads", uploadDir)
|
||||
|
||||
api := r.Group("/api")
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user