为 Web UI 添加完整的登录验证系统,遵循 CQRS 架构模式。 主要功能: - 创建登录页面 UI (web/login.html) - 美观的渐变背景设计 - 密钥输入和验证 - 错误提示和加载状态 - 实现登录验证 (遵循 CQRS) - 新增 LoginQuery 和 LoginHandler (internal/api/handlers/auth_handlers.go) - 新增 AuthEndpoint (internal/api/endpoints/auth_endpoints.go) - 注册登录接口 /auth/login (无需授权) - 更新主页面 (web/index.html) - 添加登录状态检查 - 未登录显示提示信息 - 所有 API 请求自动携带 X-API-Key 头 - 添加退出登录功能 - 401 错误自动跳转登录页 - 更新路由配置 (cmd/server/main.go) - 添加 /auth/login 公开路由 - 注册登录处理器和端点 - 新增登录文档 (docs/LOGIN_GUIDE.md) - 完整的使用说明 - 技术实现细节 - API 接口说明 安全特性: - 密钥存储在 localStorage - 自动处理登录过期 - 支持主动退出登录 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
129 lines
4.8 KiB
Go
129 lines
4.8 KiB
Go
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)
|
||
loginHandler := handlers.NewLoginHandler(middleware.API_KEY_VALUE)
|
||
|
||
// 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.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())
|
||
{
|
||
// 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)
|
||
}
|