Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c8f72de
chore(config): remove InitWithDB usage and add PR draft; prepare for …
zy84338719 Sep 16, 2025
f1a2994
refactor(config): remove DB-based config functions and update reload/…
zy84338719 Sep 16, 2025
f413a46
refactor(repo): remove KeyValue DAO (migrated to config.yaml)
zy84338719 Sep 16, 2025
ba5f413
refactor(models): migrate KeyValue into config.Extra and remove DB mo…
zy84338719 Sep 16, 2025
6a64f0d
feat(config): migrate KeyValue table into config.Extra and remove DB …
zy84338719 Sep 16, 2025
360a133
chore(config): add initial config.yaml provided by user
zy84338719 Sep 16, 2025
4857993
chore(legacy): move key_value export scripts to scripts/legacy and ad…
zy84338719 Sep 17, 2025
ac89ea8
chore(config): remove key_value export top-level scripts; keep legacy…
zy84338719 Sep 17, 2025
719ddcd
chore: remove top-level legacy export scripts and KeyValue model clea…
zy84338719 Sep 17, 2025
f0195b4
refactor(config): remove unused DTO placeholders and cleanup MCP help…
zy84338719 Sep 17, 2025
154a628
docs: remove DTO descriptions; refactor middleware into per-file impl…
zy84338719 Sep 17, 2025
998ae18
docs: clean up DTO references in changelogs and config summary
zy84338719 Sep 17, 2025
babb011
refactor(static): centralize static asset registration and serve func…
zy84338719 Sep 17, 2025
e6fed8b
feat(middleware): add CombinedAdminAuth middleware and tests
zy84338719 Sep 17, 2025
f200919
chore: misc fixes from refactor (tests, handlers, scripts)
zy84338719 Sep 17, 2025
22b7d6f
static: deprecate RegisterAdminStaticRoutes to avoid accidental publi…
zy84338719 Sep 19, 2025
4172471
static: remove RegisterAdminStaticRoutes; admin assets must be served…
zy84338719 Sep 19, 2025
7fe638a
routes: ensure HEAD requests for admin static routes are protected (a…
zy84338719 Sep 19, 2025
499801b
docs: add admin static protection docs and README note
zy84338719 Sep 19, 2025
f026e38
middleware: allow public admin entry/static paths in CombinedAdminAut…
zy84338719 Sep 20, 2025
4be6eba
修复问题
zy84338719 Sep 21, 2025
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: 3 additions & 3 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ FileCodeBox 是一个高性能的文件快传系统的 Go 实现,基于现代
```go
// 通过 ConfigManager 统一管理所有配置
manager := config.InitManager()
manager.InitWithDB(db) // 数据库驱动的动态配置
manager.SetDB(db) // 注入数据库连接(配置读取现在以 config.yaml 和 环境变量为准)
```

配置分为多个模块:`BaseConfig`, `DatabaseConfig`, `StorageConfig`, `UserSystemConfig`, `MCPConfig`

支持环境变量优先级覆盖、数据库持久化存储、热重载机制:
- **环境变量优先级**:PORT、ADMIN_TOKEN 等关键配置始终优先使用环境变量
- **环境变量优先级**:PORT、DATA_PATH 等关键配置始终优先使用环境变量
- **数据库持久化**:配置自动保存到 key_value 表,支持动态更新
- **热重载机制**:通过 ReloadConfig() 方法实现运行时配置更新
- **配置验证**:每个配置模块都有独立的验证方法
- **分层映射**:ToMap() 和 FromMap() 方法支持配置的序列化和反序列化
- **类型安全配置**:使用结构体 Clone() 方法和直接字段访问,避免 map 转换开销

### Route Architecture
完全模块化的路由系统 (`internal/routes/`):
Expand Down
76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
# FileCodeBox (Go)

轻量且高性能的文件/文本分享服务,使用 Go 实现,支持分片上传、秒传、断点续传与多种存储后端。

核心目标是提供一个易部署、易扩展的文件快传系统,适合自托管与容器化部署场景。

## 主要特性

- 高性能:基于 Go 的并发能力构建,低延迟与内存占用
- 文件/文本分享:支持短链接分享文本和文件
- 分片上传:大文件分片、断点续传、上传校验与秒传支持
- 管理后台:内置管理控制台,可管理文件、配置与用户
- 多存储后端:支持本地、S3、WebDAV、OneDrive(可扩展)
- 容器友好:提供 Docker 与 docker-compose 支持
- 主题系统:前端主题可替换与定制

## 环境要求

开发推荐使用 Go 1.25+。项目默认使用 SQLite 作为开发环境的轻量数据库。生产环境请按需选择存储与资源配置。

## 部署建议(简要)

- 推荐使用 Docker + 反向代理(Nginx)启用 HTTPS
- 将 `data/` 目录做定期备份
- 将服务放入进程管理(systemd / 容器重启策略)

## 开发与扩展

- 新增存储:实现 `storage.StorageInterface` 并在 `storage.NewStorageManager` 注册
- 新增接口:在 `internal/services` 实现业务逻辑,并在 `internal/handlers` 与 `internal/routes` 添加路由

运行测试与示例脚本请查看 `tests/` 目录。

---

## 常见问题与排查(示例)

- 检查端口占用:

```bash
lsof -ti:12345
```

- 如果数据库被锁或服务异常,尝试重启服务或检查 `data/` 下的 sqlite 文件权限。

---

## 许可证

MIT

---

如需我继续:

- 将 README 翻译为英文
- 自动生成或更新 Swagger 文档
- 补全详细部署示例(Kubernetes / systemd)

请告诉我接下来要做哪个扩展。
<div align="center">
<img src="assets/images/logos/logo.svg" alt="FileCodeBox Logo" width="200"/>

Expand Down Expand Up @@ -72,7 +132,7 @@ docker-compose up -d
- `name`: 站点名称
- `upload_size`: 最大上传大小
- `file_storage`: 存储类型(local/s3/webdav/onedrive)
- `admin_token`: 管理员访问令牌
- 管理员认证改为使用管理员用户名/密码登录并通过 `Authorization: Bearer <token>` 使用 JWT(不再使用静态管理员令牌配置)

## 管理员后台

Expand All @@ -93,6 +153,20 @@ docker-compose up -d
3. 设置合适的上传大小限制
4. 配置站点名称和描述信息

### 管理后台静态资源的安全说明

管理后台使用了一组专用静态资源(位于 `themes/<theme>/admin/`),这些文件包含管理界面的 JavaScript、CSS 与模板。

为了避免未授权用户直接访问管理后台页面并读取敏感前端逻辑,服务已将管理专用静态资源改为仅在管理员认证后提供:

- 管理前端入口 `GET /admin/` 需要有效的管理员 JWT(通过 `Authorization: Bearer <token>` 方式传递)。
- 管理专用静态路径(例如 `/admin/js/*`, `/admin/css/*`, `/admin/templates/*`, `/admin/assets/*`, `/admin/components/*`)也仅在认证通过后提供。
- 公共资源(用户前端和通用资源)仍通过 `/js/*`, `/css/*`, `/assets/*`, `/components/*` 对外公开,以支持用户页面和登录流程。

部署注意:如果你的生产环境在前面放了 Nginx、CDN 或其它反向代理,请确保对 `/admin/*` 不做缓存并`不要`将 admin 静态事先缓存到代理上,否则可能绕过后端认证。建议在代理上为 `/admin/*` 添加 `Cache-Control: no-store` 或根据代理文档设置不缓存规则。

如果需要把少量引导或 favicon 等资产在未认证时可用,请将这些文件放入主题的 `assets/` 目录(由 `/assets/*` 提供),不要将管理专用目录下的文件放到公共路径。

## API接口

### 分享文本
Expand Down
119 changes: 119 additions & 0 deletions admin-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理员API测试</title>
</head>
<body>
<h1>管理员API测试</h1>

<div>
<h2>1. 登录测试</h2>
<button onclick="testLogin()">测试登录</button>
<div id="login-result"></div>
</div>

<div>
<h2>2. 仪表板API测试</h2>
<button onclick="testDashboard()">测试仪表板</button>
<div id="dashboard-result"></div>
</div>

<div>
<h2>3. 用户列表API测试</h2>
<button onclick="testUsers()">测试用户列表</button>
<div id="users-result"></div>
</div>

<script>
let authToken = null;

async function testLogin() {
try {
const response = await fetch('/admin/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'testadmin',
password: 'admin123'
})
});

const result = await response.json();

if (result.code === 200) {
authToken = result.data.token;
localStorage.setItem('user_token', authToken);
document.getElementById('login-result').innerHTML =
'<span style="color: green;">登录成功!Token: ' + authToken.substring(0, 50) + '...</span>';
} else {
document.getElementById('login-result').innerHTML =
'<span style="color: red;">登录失败: ' + result.message + '</span>';
}
} catch (error) {
document.getElementById('login-result').innerHTML =
'<span style="color: red;">请求失败: ' + error.message + '</span>';
}
}

async function testDashboard() {
if (!authToken) {
authToken = localStorage.getItem('user_token');
}

if (!authToken) {
document.getElementById('dashboard-result').innerHTML =
'<span style="color: red;">请先登录</span>';
return;
}

try {
const response = await fetch('/admin/dashboard', {
headers: {
'Authorization': 'Bearer ' + authToken
}
});

const result = await response.json();

document.getElementById('dashboard-result').innerHTML =
'<pre>' + JSON.stringify(result, null, 2) + '</pre>';
} catch (error) {
document.getElementById('dashboard-result').innerHTML =
'<span style="color: red;">请求失败: ' + error.message + '</span>';
}
}

async function testUsers() {
if (!authToken) {
authToken = localStorage.getItem('user_token');
}

if (!authToken) {
document.getElementById('users-result').innerHTML =
'<span style="color: red;">请先登录</span>';
return;
}

try {
const response = await fetch('/admin/users', {
headers: {
'Authorization': 'Bearer ' + authToken
}
});

const result = await response.json();

document.getElementById('users-result').innerHTML =
'<pre>' + JSON.stringify(result, null, 2) + '</pre>';
} catch (error) {
document.getElementById('users-result').innerHTML =
'<span style="color: red;">请求失败: ' + error.message + '</span>';
}
}
</script>
</body>
</html>
54 changes: 54 additions & 0 deletions config.generated.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
base:
data_path: /tmp/filecodebox_test
description: 开箱即用的文件快传系统
host: 0.0.0.0
name: FileCodeBox
port: 12346
production: false
database:
host: ""
name: ./data/filecodebox.db
port: 0
ssl: disable
type: sqlite
user: ""
mcp:
host: 0.0.0.0
port: 8081
storage: {}
ui:
allow_user_registration: 0
background: ""
chunk_size: 2097152
download_timeout: 300
enable_chunk: 0
enable_concurrent_download: 1
enable_mcp_server: 0
error_count: 1
error_minute: 1
file_storage: local
jwt_secret: FileCodeBox2025JWT
keywords: FileCodeBox, 文件快递柜, 口令传送箱, 匿名口令分享文本, 文件
max_concurrent_downloads: 10
max_save_seconds: 0
max_sessions_per_user: 5
notify_content: "欢迎使用 FileCodeBox,本程序开源于 <a href=\"https://github.com/zy84338719/FileCodeBox\" target=\"_blank\">Github</a> ,欢迎Star和Fork。"
notify_title: 系统通知
opacity: 0
open_upload: 1
page_explain: "请勿上传或分享违法内容。根据《中华人民共和国网络安全法》、《中华人民共和国刑法》、《中华人民共和国治安管理处罚法》等相关规定。 传播或存储违法、违规内容,会受到相关处罚,严重者将承担刑事责任。本站坚决配合相关部门,确保网络内容的安全,和谐,打造绿色网络环境。"
require_email_verify: 0
robots_text: |-
User-agent: *
Disallow: /
session_expiry_hours: 168
show_admin_address: 0
storage_path: ""
sys_start: 1757914992279
themes_select: themes/2025
upload_count: 10
upload_minute: 1
upload_size: 100
user_storage_quota: 1073741824
user_upload_size: 52428800
user: {}
71 changes: 71 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
base:
name: FileCodeBox
description: 开箱即用的文件快传系统
keywords: ""
port: 12346
host: 0.0.0.0
datapath: /Users/zhangyi/zy/FileCodeBox/data
production: false
database:
type: sqlite
host: ""
port: 0
name: ./data/filecodebox.db
user: ""
pass: ""
ssl: disable
transfer:
upload:
openupload: 1
uploadsize: 10485760
enablechunk: 0
chunksize: 2097152
maxsaveseconds: 0
download:
enableconcurrentdownload: 1
maxconcurrentdownloads: 10
downloadtimeout: 300
storage:
type: ""
storagepath: ""
s3: null
webdav: null
onedrive: null
nfs: null
user:
allowuserregistration: 1
requireemailverify: 0
useruploadsize: 52428800
userstoragequota: 1073741824
sessionexpiryhours: 168
maxsessionsperuser: 5
jwtsecret: FileCodeBox2025JWT
mcp:
enablemcpserver: 0
mcpport: ""
mcphost: ""
notifytitle: ""
notifycontent: ""
ui:
themes_select: themes/2025
background: ""
page_explain: 请勿上传或分享违法内容。根据《中华人民共和国网络安全法》、《中华人民共和国刑法》、《中华人民共和国治安管理处罚法》等相关规定。 传播或存储违法、违规内容,会受到相关处罚,严重者将承担刑事责任。本站坚决配合相关部门,确保网络内容的安全,和谐,打造绿色网络环境。
robots_text: |-
User-agent: *
Disallow: /
show_admin_addr: 0
opacity: 0
themes_select: themes/2025
robots_text: |-
User-agent: *
Disallow: /
page_explain: 请勿上传或分享违法内容。根据《中华人民共和国网络安全法》、《中华人民共和国刑法》、《中华人民共和国治安管理处罚法》等相关规定。 传播或存储违法、违规内容,会受到相关处罚,严重者将承担刑事责任。本站坚决配合相关部门,确保网络内容的安全,和谐,打造绿色网络环境。
show_admin_addr: 0
opacity: 0
background: ""
sys_start: ""
upload_minute: 0
upload_count: 0
error_minute: 0
error_count: 0
expire_style: []
4 changes: 2 additions & 2 deletions docs/ADMIN_AUTH_401_FIX_REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ combinedAuthMiddleware := func(c *gin.Context) {
if tokenParts[1] == cfg.AdminToken {
c.Set("is_admin", true)
c.Set("role", "admin")
c.Set("auth_type", "admin_token")
c.Set("auth_type", "jwt")
c.Next()
return
}
Expand Down Expand Up @@ -97,7 +97,7 @@ combinedAuthMiddleware := func(c *gin.Context) {
**管理员Token认证**:
- `is_admin`: true
- `role`: "admin"
- `auth_type`: "admin_token"
- `auth_type`: "jwt"

## 测试验证

Expand Down
Loading
Loading