添加前端页面以及相关打包脚本和内置 web 的逻辑
This commit is contained in:
@@ -115,6 +115,20 @@ func (h *PickupHandler) Pickup(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// 生成文件下载绝对路径直链
|
||||
scheme := "http"
|
||||
if c.Request.TLS != nil || c.GetHeader("X-Forwarded-Proto") == "https" {
|
||||
scheme = "https"
|
||||
}
|
||||
host := c.Request.Host
|
||||
if forwardedHost := c.GetHeader("X-Forwarded-Host"); forwardedHost != "" {
|
||||
host = forwardedHost
|
||||
}
|
||||
|
||||
for i := range batch.FileItems {
|
||||
batch.FileItems[i].DownloadURL = fmt.Sprintf("%s://%s/api/files/%s/%s", scheme, host, batch.FileItems[i].ID, batch.FileItems[i].OriginalName)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.SuccessResponse(PickupResponse{
|
||||
Remark: batch.Remark,
|
||||
ExpireAt: batch.ExpireAt,
|
||||
@@ -129,14 +143,16 @@ func (h *PickupHandler) Pickup(c *gin.Context) {
|
||||
|
||||
// DownloadFile 下载单个文件
|
||||
// @Summary 下载单个文件
|
||||
// @Description 根据文件 ID 下载单个文件。可选提供带 pickup scope 的 API Token。
|
||||
// @Description 根据文件 ID 下载单个文件。支持直观的文件名结尾以方便下载工具识别。可选提供带 pickup scope 的 API Token。
|
||||
// @Tags Public
|
||||
// @Security APITokenAuth
|
||||
// @Param file_id path string true "文件 ID (UUID)"
|
||||
// @Param file_id path string true "文件 ID (UUID)"
|
||||
// @Param filename path string false "文件名"
|
||||
// @Produce application/octet-stream
|
||||
// @Success 200 {file} file
|
||||
// @Failure 404 {object} model.Response
|
||||
// @Failure 410 {object} model.Response
|
||||
// @Router /api/files/{file_id}/{filename} [get]
|
||||
// @Router /api/files/{file_id}/download [get]
|
||||
func (h *PickupHandler) DownloadFile(c *gin.Context) {
|
||||
fileID := c.Param("file_id")
|
||||
@@ -155,7 +171,12 @@ func (h *PickupHandler) DownloadFile(c *gin.Context) {
|
||||
|
||||
if h.batchService.IsExpired(&batch) {
|
||||
h.batchService.MarkAsExpired(&batch)
|
||||
c.JSON(http.StatusGone, model.ErrorResponse(model.CodeGone, "batch expired"))
|
||||
// 按照需求,如果不存在(已在上面处理)或达到上限,返回 404
|
||||
if batch.ExpireType == "download" && batch.DownloadCount >= batch.MaxDownloads {
|
||||
c.JSON(http.StatusNotFound, model.ErrorResponse(model.CodeNotFound, "file not found or download limit reached"))
|
||||
} else {
|
||||
c.JSON(http.StatusGone, model.ErrorResponse(model.CodeGone, "batch expired"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -14,6 +15,11 @@ type Config struct {
|
||||
Storage StorageConfig `yaml:"storage" json:"storage"` // 存储设置
|
||||
APIToken APITokenConfig `yaml:"api_token" json:"api_token"` // API Token 设置
|
||||
Database DatabaseConfig `yaml:"database" json:"database"` // 数据库设置
|
||||
Web WebConfig `yaml:"web" json:"web"` // Web 前端设置
|
||||
}
|
||||
|
||||
type WebConfig struct {
|
||||
Path string `yaml:"path" json:"path"` // Web 前端资源路径
|
||||
}
|
||||
|
||||
type SiteConfig struct {
|
||||
@@ -78,6 +84,26 @@ func LoadConfig(path string) error {
|
||||
configLock.Lock()
|
||||
defer configLock.Unlock()
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
// 创建默认配置
|
||||
cfg := GetDefaultConfig()
|
||||
data, err := yaml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 确保目录存在
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(path, data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
GlobalConfig = cfg
|
||||
ConfigPath = path
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -93,6 +119,47 @@ func LoadConfig(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Site: SiteConfig{
|
||||
Name: "文件暂存柜",
|
||||
Description: "临时文件中转服务",
|
||||
Logo: "https://www.hxuanyu.com/upload/favicon.png",
|
||||
},
|
||||
Security: SecurityConfig{
|
||||
AdminPasswordHash: "$2a$10$Bm0TEmU4uj.bVHYiIPFBheUkcdg6XHpsanLvmpoAtgU1UnKbo9.vy", // 默认密码: admin123
|
||||
PickupCodeLength: 6,
|
||||
PickupFailLimit: 5,
|
||||
JWTSecret: "file-relay-secret",
|
||||
},
|
||||
Upload: UploadConfig{
|
||||
MaxFileSizeMB: 100,
|
||||
MaxBatchFiles: 20,
|
||||
MaxRetentionDays: 30,
|
||||
RequireToken: false,
|
||||
},
|
||||
Storage: StorageConfig{
|
||||
Type: "local",
|
||||
Local: struct {
|
||||
Path string `yaml:"path" json:"path"`
|
||||
}{
|
||||
Path: "storage_data",
|
||||
},
|
||||
},
|
||||
APIToken: APITokenConfig{
|
||||
Enabled: true,
|
||||
AllowAdminAPI: true,
|
||||
MaxTokens: 20,
|
||||
},
|
||||
Database: DatabaseConfig{
|
||||
Path: "file_relay.db",
|
||||
},
|
||||
Web: WebConfig{
|
||||
Path: "web",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func SaveConfig() error {
|
||||
configLock.RLock()
|
||||
defer configLock.RUnlock()
|
||||
|
||||
@@ -11,5 +11,6 @@ type FileItem struct {
|
||||
StoragePath string `json:"storage_path"`
|
||||
Size int64 `json:"size"`
|
||||
MimeType string `json:"mime_type"`
|
||||
DownloadURL string `gorm:"-" json:"download_url,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user