root d861be0d6e feat: 新增文件文本内容接口,修复Markdown预览
- 新增 GET /files/content 接口,后端直接读取S3文件文本内容返回
- Repository 新增 GetFileContent 方法
- CQRS: 新增 GetFileContentQuery / GetFileContentHandler
- 前端 Markdown 预览改为调用后端接口获取内容,用 marked.js 渲染
- 解决 presigned URL CORS 和下载头导致 MD 文件无法预览的问题
- config.go: AuthAPIKey 默认值恢复为 xn001624.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-06 18:08:42 +08:00

136 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
_ "file-system/docs" // Import generated docs
"file-system/internal/api/endpoints"
"file-system/internal/api/handlers"
"file-system/internal/api/validators"
"file-system/internal/common"
"file-system/internal/domain/repository"
"file-system/internal/infrastructure/mediator"
"file-system/internal/infrastructure/s3"
"file-system/internal/middleware"
"io"
"net/http"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
// @title RustFS File System API
// @version 1.2
// @description RustFS 文件存储系统 API支持分片上传、文件预览、分页查询等高级功能。
// @host localhost:8080
// @BasePath /
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description 在请求头中传入 API 密钥进行身份验证
func main() {
cfg := common.LoadConfig()
// Infrastructure
rustfsClient := s3.NewRustFSClient(cfg)
s3Repo := s3.NewS3FileRepository(rustfsClient)
m := mediator.NewMediator()
// Handlers
uploadHandler := handlers.NewUploadFileHandler(s3Repo)
downloadHandler := handlers.NewDownloadFileHandler(s3Repo)
createBucketHandler := handlers.NewCreateBucketHandler(s3Repo)
listBucketsHandler := handlers.NewListBucketsHandler(s3Repo)
deleteBucketHandler := handlers.NewDeleteBucketHandler(s3Repo)
// New Handlers
listFilesHandler := handlers.NewListFilesHandler(s3Repo)
previewHandler := handlers.NewGetFilePreviewHandler(s3Repo)
initMultipartHandler := handlers.NewInitMultipartHandler(s3Repo)
uploadPartHandler := handlers.NewUploadPartHandler(s3Repo)
completeMultipartHandler := handlers.NewCompleteMultipartHandler(s3Repo)
deleteFileHandler := handlers.NewDeleteFileHandler(s3Repo)
fileContentHandler := handlers.NewGetFileContentHandler(s3Repo)
loginHandler := handlers.NewLoginHandler(cfg.AuthAPIKey)
// Register Handlers
mediator.Register[handlers.UploadFileCommand, string](m, uploadHandler)
mediator.Register[handlers.DownloadFileQuery, io.ReadCloser](m, downloadHandler)
mediator.Register[handlers.CreateBucketCommand, string](m, createBucketHandler)
mediator.Register[handlers.ListBucketsQuery, []string](m, listBucketsHandler)
mediator.Register[handlers.DeleteBucketCommand, string](m, deleteBucketHandler)
// New Registrations
mediator.Register[handlers.ListFilesQuery, *repository.ListFilesResult](m, listFilesHandler)
mediator.Register[handlers.GetFilePreviewQuery, string](m, previewHandler)
mediator.Register[handlers.InitMultipartCommand, string](m, initMultipartHandler)
mediator.Register[handlers.UploadPartCommand, string](m, uploadPartHandler)
mediator.Register[handlers.CompleteMultipartCommand, string](m, completeMultipartHandler)
mediator.Register[handlers.DeleteFileCommand, string](m, deleteFileHandler)
mediator.Register[handlers.GetFileContentQuery, string](m, fileContentHandler)
mediator.Register[handlers.LoginQuery, handlers.LoginResult](m, loginHandler)
// Validators
uploadValidator := validators.NewUploadFileValidator()
downloadValidator := validators.NewDownloadFileValidator()
createBucketValidator := validators.NewCreateBucketValidator()
newFeaturesValidator := validators.NewNewFeaturesValidator()
// Endpoints
fileEndpoint := endpoints.NewFileEndpoint(m, uploadValidator, downloadValidator, newFeaturesValidator)
bucketEndpoint := endpoints.NewBucketEndpoint(m, createBucketValidator)
authEndpoint := endpoints.NewAuthEndpoint(m)
// Router
r := gin.Default()
// CORS
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
// 公开接口(无需授权)
r.POST("/auth/login", authEndpoint.Login)
// API授权中间件组
api := r.Group("/")
api.Use(middleware.AuthMiddleware(cfg.AuthAPIKey))
{
// File operations
api.POST("/files/upload", fileEndpoint.UploadFile)
api.GET("/files/download", fileEndpoint.DownloadFile)
api.GET("/files/list", fileEndpoint.ListFiles)
api.GET("/files/preview", fileEndpoint.GetPreviewURL)
api.GET("/files/content", fileEndpoint.GetFileContent)
// Delete file
api.DELETE("/files/delete", fileEndpoint.DeleteFile)
// Multipart Upload
api.POST("/files/multipart/init", fileEndpoint.InitMultipart)
api.PUT("/files/multipart/part", fileEndpoint.UploadPart)
api.POST("/files/multipart/complete", fileEndpoint.CompleteMultipart)
// Bucket operations
api.POST("/buckets", bucketEndpoint.CreateBucket)
api.GET("/buckets", bucketEndpoint.ListBuckets)
api.DELETE("/buckets", bucketEndpoint.DeleteBucket)
}
// Swagger
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// Web UI
r.Static("/web", "./web")
r.GET("/", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/web")
})
r.Run(":" + cfg.ServerPort)
}