Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
base:
name: FileCodeBox
name: 站点
description: 开箱即用的文件快传系统
keywords: FileCodeBox, 文件快递柜, 口令传送箱, 匿名口令分享文本, 文件
port: 12345
Expand All @@ -18,13 +18,15 @@ transfer:
upload:
openupload: 1
uploadsize: 10485760
enablechunk: 0
enablechunk: 1
chunksize: 2097152
maxsaveseconds: 0
requirelogin: 1
download:
enableconcurrentdownload: 1
maxconcurrentdownloads: 10
downloadtimeout: 300
requirelogin: 0
storage:
type: ""
storagepath: ""
Expand Down
2 changes: 2 additions & 0 deletions docs/config.new.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ transfer:
enable_chunk: 1
chunk_size: 2097152
max_save_seconds: 0
require_login: 0
download:
enable_concurrent_download: 1
max_concurrent_downloads: 10
download_timeout: 300
require_login: 0

user:
allow_user_registration: 1
Expand Down
24 changes: 24 additions & 0 deletions internal/config/transfer_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ type UploadConfig struct {
EnableChunk int `json:"enable_chunk"` // 是否启用分片上传 0-禁用 1-启用
ChunkSize int64 `json:"chunk_size"` // 分片大小(字节)
MaxSaveSeconds int `json:"max_save_seconds"` // 最大保存时间(秒)
RequireLogin int `json:"require_login"` // 上传是否强制登录 0-否 1-是
}

// DownloadConfig 下载配置
type DownloadConfig struct {
EnableConcurrentDownload int `json:"enable_concurrent_download"` // 是否启用并发下载
MaxConcurrentDownloads int `json:"max_concurrent_downloads"` // 最大并发下载数
DownloadTimeout int `json:"download_timeout"` // 下载超时时间(秒)
RequireLogin int `json:"require_login"` // 下载是否强制登录 0-否 1-是
}

// TransferConfig 文件传输配置(包含上传和下载)
Expand All @@ -36,6 +38,7 @@ func NewUploadConfig() *UploadConfig {
EnableChunk: 0,
ChunkSize: 2 * 1024 * 1024, // 2MB
MaxSaveSeconds: 0, // 0表示不限制
RequireLogin: 0,
}
}

Expand All @@ -45,6 +48,7 @@ func NewDownloadConfig() *DownloadConfig {
EnableConcurrentDownload: 1, // 默认启用
MaxConcurrentDownloads: 10, // 最大10个并发
DownloadTimeout: 300, // 5分钟超时
RequireLogin: 0,
}
}

Expand Down Expand Up @@ -86,6 +90,10 @@ func (uc *UploadConfig) Validate() error {
errors = append(errors, "最大保存时间不能为负数")
}

if uc.RequireLogin != 0 && uc.RequireLogin != 1 {
errors = append(errors, "上传登录开关只能是0或1")
}

if len(errors) > 0 {
return fmt.Errorf("上传配置验证失败: %s", strings.Join(errors, "; "))
}
Expand Down Expand Up @@ -113,6 +121,10 @@ func (dc *DownloadConfig) Validate() error {
errors = append(errors, "下载超时时间不能超过1小时")
}

if dc.RequireLogin != 0 && dc.RequireLogin != 1 {
errors = append(errors, "下载登录开关只能是0或1")
}

if len(errors) > 0 {
return fmt.Errorf("下载配置验证失败: %s", strings.Join(errors, "; "))
}
Expand All @@ -138,6 +150,11 @@ func (uc *UploadConfig) IsChunkEnabled() bool {
return uc.EnableChunk == 1
}

// IsLoginRequired 判断是否需要登录才能上传
func (uc *UploadConfig) IsLoginRequired() bool {
return uc.RequireLogin == 1
}

// GetUploadSizeMB 获取上传大小限制(MB)
func (uc *UploadConfig) GetUploadSizeMB() float64 {
return float64(uc.UploadSize) / (1024 * 1024)
Expand All @@ -161,6 +178,11 @@ func (dc *DownloadConfig) IsDownloadConcurrentEnabled() bool {
return dc.EnableConcurrentDownload == 1
}

// IsLoginRequired 判断是否需要登录才能下载
func (dc *DownloadConfig) IsLoginRequired() bool {
return dc.RequireLogin == 1
}

// GetDownloadTimeoutMinutes 获取下载超时时间(分钟)
func (dc *DownloadConfig) GetDownloadTimeoutMinutes() float64 {
return float64(dc.DownloadTimeout) / 60
Expand All @@ -174,6 +196,7 @@ func (uc *UploadConfig) Clone() *UploadConfig {
EnableChunk: uc.EnableChunk,
ChunkSize: uc.ChunkSize,
MaxSaveSeconds: uc.MaxSaveSeconds,
RequireLogin: uc.RequireLogin,
}
}

Expand All @@ -183,6 +206,7 @@ func (dc *DownloadConfig) Clone() *DownloadConfig {
EnableConcurrentDownload: dc.EnableConcurrentDownload,
MaxConcurrentDownloads: dc.MaxConcurrentDownloads,
DownloadTimeout: dc.DownloadTimeout,
RequireLogin: dc.RequireLogin,
}
}

Expand Down
1 change: 1 addition & 0 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func InitWithManager(manager *config.ConfigManager) (*gorm.DB, error) {
&models.UploadChunk{},
&models.User{},
&models.UserSession{},
&models.TransferLog{},
)
if err != nil {
return nil, fmt.Errorf("数据库自动迁移失败: %w", err)
Expand Down
65 changes: 64 additions & 1 deletion internal/handlers/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"net"
"strconv"
"strings"
"time"

"github.com/zy84338719/filecodebox/internal/common"
Expand Down Expand Up @@ -1164,7 +1165,69 @@ func tcpProbe(address string, timeout time.Duration) error {
return nil
}

// GetSystemLogs 获取系统日志
// GetTransferLogs 获取上传/下载审计日志
func (h *AdminHandler) GetTransferLogs(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page < 1 {
page = 1
}
if pageSize <= 0 {
pageSize = 20
} else if pageSize > 200 {
pageSize = 200
}

operation := strings.TrimSpace(c.DefaultQuery("operation", ""))
search := strings.TrimSpace(c.DefaultQuery("search", ""))
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling strings.TrimSpace on empty default values is unnecessary since empty strings don't have leading/trailing whitespace. Consider checking for non-empty values before trimming or moving the trimming logic into the service layer.

Suggested change
operation := strings.TrimSpace(c.DefaultQuery("operation", ""))
search := strings.TrimSpace(c.DefaultQuery("search", ""))
operation := c.DefaultQuery("operation", "")
if operation != "" {
operation = strings.TrimSpace(operation)
}
search := c.DefaultQuery("search", "")
if search != "" {
search = strings.TrimSpace(search)
}

Copilot uses AI. Check for mistakes.

logs, total, err := h.service.GetTransferLogs(page, pageSize, operation, search)
if err != nil {
common.InternalServerErrorResponse(c, "获取传输日志失败: "+err.Error())
return
}

items := make([]web.TransferLogItem, 0, len(logs))
for _, record := range logs {
item := web.TransferLogItem{
ID: record.ID,
Operation: record.Operation,
FileCode: record.FileCode,
FileName: record.FileName,
FileSize: record.FileSize,
Username: record.Username,
IP: record.IP,
DurationMs: record.DurationMs,
CreatedAt: record.CreatedAt.Format(time.RFC3339),
}
if record.UserID != nil {
id := *record.UserID
item.UserID = &id
}
items = append(items, item)
}

pages := int64(0)
if pageSize > 0 {
pages = (total + int64(pageSize) - 1) / int64(pageSize)
}
if pages == 0 {
pages = 1
}

response := web.TransferLogListResponse{
Logs: items,
Pagination: web.PaginationResponse{
Page: page,
PageSize: pageSize,
Total: total,
Pages: pages,
},
}

common.SuccessResponse(c, response)
}

func (h *AdminHandler) GetSystemLogs(c *gin.Context) {
level := c.DefaultQuery("level", "")
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "100"))
Expand Down
15 changes: 12 additions & 3 deletions internal/handlers/share.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package handlers

import (
"time"

"github.com/zy84338719/filecodebox/internal/common"
"github.com/zy84338719/filecodebox/internal/models"
"github.com/zy84338719/filecodebox/internal/models/web"
Expand Down Expand Up @@ -107,15 +109,18 @@ func (h *ShareHandler) ShareFile(c *gin.Context) {
return
}

userID := utils.GetUserIDFromContext(c)
if h.service.IsUploadLoginRequired() && userID == nil {
common.UnauthorizedResponse(c, "当前配置要求登录后才能上传文件")
return
}

// 解析文件
file, success := utils.ParseFileFromForm(c, "file")
if !success {
return
}

// 检查是否为认证用户上传
userID := utils.GetUserIDFromContext(c)

// 构建服务层请求(这里需要适配服务层的接口)
serviceReq := models.ShareFileRequest{
File: file,
Expand Down Expand Up @@ -255,6 +260,7 @@ func (h *ShareHandler) DownloadFile(c *gin.Context) {

if fileCode.Text != "" {
common.SuccessResponse(c, fileCode.Text)
h.service.RecordDownloadLog(fileCode, userID, c.ClientIP(), 0)
return
}

Expand All @@ -266,10 +272,13 @@ func (h *ShareHandler) DownloadFile(c *gin.Context) {
return
}

start := time.Now()
if err := storageService.GetFileResponse(c, fileCode); err != nil {
common.NotFoundResponse(c, "文件下载失败: "+err.Error())
return
}

h.service.RecordDownloadLog(fileCode, userID, c.ClientIP(), time.Since(start))
}

// getDisplayFileName 获取用于显示的文件名
Expand Down
12 changes: 8 additions & 4 deletions internal/handlers/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,16 @@ func (sh *StorageHandler) GetStorageInfo(c *gin.Context) {
// 尝试附加路径与使用率信息
switch storageType {
case "local":
// 本地存储使用配置中的 StoragePath
detail.StoragePath = sh.storageConfig.StoragePath
// 本地存储使用配置中的 StoragePath,如果未配置则回退到数据目录
path := sh.storageConfig.StoragePath
if path == "" {
path = sh.configManager.Base.DataPath
}
detail.StoragePath = path

// 尝试读取磁盘使用率(若可用)
if sh.storageConfig.StoragePath != "" {
if usagePercent, err := utils.GetUsagePercent(sh.storageConfig.StoragePath); err == nil {
if path != "" {
if usagePercent, err := utils.GetUsagePercent(path); err == nil {
val := int(usagePercent)
detail.UsagePercent = &val
}
Expand Down
30 changes: 30 additions & 0 deletions internal/models/db/transfer_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package db

import "gorm.io/gorm"

// TransferLog 记录上传/下载操作日志
// 当系统要求登录上传/下载时,用于追踪用户行为
// Operation: upload 或 download
// DurationMs: 针对下载记录耗时,上传默认为0
// Username保留冗余信息,便于查询,即使用户被删除仍保留原始名字
type TransferLog struct {
gorm.Model
Operation string `gorm:"size:20;index" json:"operation"`
FileCodeID uint `gorm:"index" json:"file_code_id"`
FileCode string `gorm:"size:255" json:"file_code"`
FileName string `gorm:"size:255" json:"file_name"`
FileSize int64 `json:"file_size"`
UserID *uint `gorm:"index" json:"user_id"`
Username string `gorm:"size:100" json:"username"`
IP string `gorm:"size:45" json:"ip"`
DurationMs int64 `json:"duration_ms"`
}

// TransferLogQuery 查询条件
type TransferLogQuery struct {
Operation string `json:"operation"`
UserID *uint `json:"user_id"`
Search string `json:"search"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
2 changes: 2 additions & 0 deletions internal/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type (
UploadChunk = db.UploadChunk
User = db.User
UserSession = db.UserSession
TransferLog = db.TransferLog
TransferLogQuery = db.TransferLogQuery

// 服务模型别名
BuildInfo = service.BuildInfo
Expand Down
20 changes: 20 additions & 0 deletions internal/models/web/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,26 @@ type AdminFileDetail struct {
UploadType string `json:"upload_type"`
}

// TransferLogItem 审计日志单条记录
type TransferLogItem struct {
ID uint `json:"id"`
Operation string `json:"operation"`
FileCode string `json:"file_code"`
FileName string `json:"file_name"`
FileSize int64 `json:"file_size"`
UserID *uint `json:"user_id,omitempty"`
Username string `json:"username"`
IP string `json:"ip"`
DurationMs int64 `json:"duration_ms"`
CreatedAt string `json:"created_at"`
}

// TransferLogListResponse 审计日志列表响应
type TransferLogListResponse struct {
Logs []TransferLogItem `json:"logs"`
Pagination PaginationResponse `json:"pagination"`
}

// MCPStatusResponse MCP状态响应
type MCPStatusResponse struct {
Status string `json:"status"`
Expand Down
2 changes: 2 additions & 0 deletions internal/repository/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type RepositoryManager struct {
Chunk *ChunkDAO
UserSession *UserSessionDAO
Upload *ChunkDAO
TransferLog *TransferLogDAO
}

// NewRepositoryManager 创建新的数据访问管理器
Expand All @@ -23,6 +24,7 @@ func NewRepositoryManager(db *gorm.DB) *RepositoryManager {
Chunk: NewChunkDAO(db),
UserSession: NewUserSessionDAO(db),
Upload: NewChunkDAO(db), // 别名
TransferLog: NewTransferLogDAO(db),
}
}

Expand Down
Loading