root 11fc10ba8a feat: 添加API授权中间件
为所有API接口添加授权验证,要求请求头中包含有效的API密钥才能访问。

主要变更:
- 新增授权中间件 (internal/middleware/auth.go)
  - 验证 X-API-Key 请求头
  - 密钥值为 xn001624.
  - 无效密钥返回 401 Unauthorized

- 更新路由配置 (cmd/server/main.go)
  - 使用路由组统一应用授权中间件
  - 保护所有文件和存储桶操作接口
  - Swagger 和 Web UI 保持公开访问

- 新增授权使用文档 (docs/AUTH_GUIDE.md)
  - 多语言使用示例 (cURL, JavaScript, Python)
  - 完整的错误说明和授权范围

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-05 20:22:04 +08:00

123 lines
4.5 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.1
// @description RustFS 文件存储系统 API支持分片上传、文件预览、分页查询等高级功能。
// @host localhost:8080
// @BasePath /
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)
// 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)
// 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)
// 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()
})
// API授权中间件组
api := r.Group("/")
api.Use(middleware.AuthMiddleware())
{
// 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)
// 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)
}