新增配置管理功能及多存储支持
- 添加管理员端 API,用于获取和更新完整配置。 - 添加公共端 API,用于获取非敏感配置信息。 - 增加本地存储(LocalStorage)、S3(S3Storage)、和 WebDAV(WebDAVStorage)存储类型的实现。 - 支持热更新存储配置和保存配置文件至磁盘。 - 更新 Swagger 文档以包含新接口定义。
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -43,7 +43,6 @@
|
|||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
data/
|
data/
|
||||||
storage/
|
|
||||||
tmp/
|
tmp/
|
||||||
|
|
||||||
# 环境变量与配置(按需:如果你会提交示例配置,建议仅忽略真实配置文件)
|
# 环境变量与配置(按需:如果你会提交示例配置,建议仅忽略真实配置文件)
|
||||||
|
|||||||
268
docs/docs.go
268
docs/docs.go
@@ -426,6 +426,80 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/config": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"AdminAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取系统的完整配置文件内容(仅管理员)",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "获取完整配置",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/config.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"AdminAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新系统的配置文件内容(仅管理员)",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "更新配置",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "新配置内容",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/config.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/login": {
|
"/admin/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "通过密码换取 JWT Token",
|
"description": "通过密码换取 JWT Token",
|
||||||
@@ -698,6 +772,38 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/config": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取前端展示所需的非敏感配置数据",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Public"
|
||||||
|
],
|
||||||
|
"summary": "获取公共配置",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/public.PublicConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/files/{file_id}/download": {
|
"/api/files/{file_id}/download": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据文件 ID 下载单个文件",
|
"description": "根据文件 ID 下载单个文件",
|
||||||
@@ -831,6 +937,149 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"config.APITokenConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"allowAdminAPI": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"maxTokens": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.Config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"apitoken": {
|
||||||
|
"$ref": "#/definitions/config.APITokenConfig"
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"$ref": "#/definitions/config.DatabaseConfig"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"$ref": "#/definitions/config.SecurityConfig"
|
||||||
|
},
|
||||||
|
"site": {
|
||||||
|
"$ref": "#/definitions/config.SiteConfig"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"$ref": "#/definitions/config.StorageConfig"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"$ref": "#/definitions/config.UploadConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.DatabaseConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.SecurityConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"adminPasswordHash": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"jwtsecret": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pickupCodeLength": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"pickupFailLimit": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.SiteConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.StorageConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"local": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"s3": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"accessKey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"bucket": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"endpoint": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"secretKey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"useSSL": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"webDAV": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.UploadConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"maxBatchFiles": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"maxFileSizeMB": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"maxRetentionDays": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"model.APIToken": {
|
"model.APIToken": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -979,6 +1228,25 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"public.PublicConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"api_token": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"site": {
|
||||||
|
"$ref": "#/definitions/config.SiteConfig"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"$ref": "#/definitions/config.UploadConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"public.UploadResponse": {
|
"public.UploadResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -419,6 +419,80 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/config": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"AdminAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取系统的完整配置文件内容(仅管理员)",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "获取完整配置",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/config.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"AdminAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新系统的配置文件内容(仅管理员)",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "更新配置",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "新配置内容",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/config.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/login": {
|
"/admin/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "通过密码换取 JWT Token",
|
"description": "通过密码换取 JWT Token",
|
||||||
@@ -691,6 +765,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/config": {
|
||||||
|
"get": {
|
||||||
|
"description": "获取前端展示所需的非敏感配置数据",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Public"
|
||||||
|
],
|
||||||
|
"summary": "获取公共配置",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/model.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/public.PublicConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/files/{file_id}/download": {
|
"/api/files/{file_id}/download": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据文件 ID 下载单个文件",
|
"description": "根据文件 ID 下载单个文件",
|
||||||
@@ -824,6 +930,149 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"config.APITokenConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"allowAdminAPI": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"maxTokens": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.Config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"apitoken": {
|
||||||
|
"$ref": "#/definitions/config.APITokenConfig"
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"$ref": "#/definitions/config.DatabaseConfig"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"$ref": "#/definitions/config.SecurityConfig"
|
||||||
|
},
|
||||||
|
"site": {
|
||||||
|
"$ref": "#/definitions/config.SiteConfig"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"$ref": "#/definitions/config.StorageConfig"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"$ref": "#/definitions/config.UploadConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.DatabaseConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.SecurityConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"adminPasswordHash": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"jwtsecret": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pickupCodeLength": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"pickupFailLimit": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.SiteConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.StorageConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"local": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"s3": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"accessKey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"bucket": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"endpoint": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"secretKey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"useSSL": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"webDAV": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config.UploadConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"maxBatchFiles": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"maxFileSizeMB": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"maxRetentionDays": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"model.APIToken": {
|
"model.APIToken": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -972,6 +1221,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"public.PublicConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"api_token": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"site": {
|
||||||
|
"$ref": "#/definitions/config.SiteConfig"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"$ref": "#/definitions/config.UploadConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"public.UploadResponse": {
|
"public.UploadResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -59,6 +59,98 @@ definitions:
|
|||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
config.APITokenConfig:
|
||||||
|
properties:
|
||||||
|
allowAdminAPI:
|
||||||
|
type: boolean
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
maxTokens:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
config.Config:
|
||||||
|
properties:
|
||||||
|
apitoken:
|
||||||
|
$ref: '#/definitions/config.APITokenConfig'
|
||||||
|
database:
|
||||||
|
$ref: '#/definitions/config.DatabaseConfig'
|
||||||
|
security:
|
||||||
|
$ref: '#/definitions/config.SecurityConfig'
|
||||||
|
site:
|
||||||
|
$ref: '#/definitions/config.SiteConfig'
|
||||||
|
storage:
|
||||||
|
$ref: '#/definitions/config.StorageConfig'
|
||||||
|
upload:
|
||||||
|
$ref: '#/definitions/config.UploadConfig'
|
||||||
|
type: object
|
||||||
|
config.DatabaseConfig:
|
||||||
|
properties:
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
config.SecurityConfig:
|
||||||
|
properties:
|
||||||
|
adminPasswordHash:
|
||||||
|
type: string
|
||||||
|
jwtsecret:
|
||||||
|
type: string
|
||||||
|
pickupCodeLength:
|
||||||
|
type: integer
|
||||||
|
pickupFailLimit:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
config.SiteConfig:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
config.StorageConfig:
|
||||||
|
properties:
|
||||||
|
local:
|
||||||
|
properties:
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
s3:
|
||||||
|
properties:
|
||||||
|
accessKey:
|
||||||
|
type: string
|
||||||
|
bucket:
|
||||||
|
type: string
|
||||||
|
endpoint:
|
||||||
|
type: string
|
||||||
|
region:
|
||||||
|
type: string
|
||||||
|
secretKey:
|
||||||
|
type: string
|
||||||
|
useSSL:
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
webDAV:
|
||||||
|
properties:
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
root:
|
||||||
|
type: string
|
||||||
|
url:
|
||||||
|
type: string
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
config.UploadConfig:
|
||||||
|
properties:
|
||||||
|
maxBatchFiles:
|
||||||
|
type: integer
|
||||||
|
maxFileSizeMB:
|
||||||
|
type: integer
|
||||||
|
maxRetentionDays:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
model.APIToken:
|
model.APIToken:
|
||||||
properties:
|
properties:
|
||||||
created_at:
|
created_at:
|
||||||
@@ -158,6 +250,18 @@ definitions:
|
|||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
public.PublicConfig:
|
||||||
|
properties:
|
||||||
|
api_token:
|
||||||
|
properties:
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
|
site:
|
||||||
|
$ref: '#/definitions/config.SiteConfig'
|
||||||
|
upload:
|
||||||
|
$ref: '#/definitions/config.UploadConfig'
|
||||||
|
type: object
|
||||||
public.UploadResponse:
|
public.UploadResponse:
|
||||||
properties:
|
properties:
|
||||||
batch_id:
|
batch_id:
|
||||||
@@ -440,6 +544,52 @@ paths:
|
|||||||
summary: 修改批次信息
|
summary: 修改批次信息
|
||||||
tags:
|
tags:
|
||||||
- Admin
|
- Admin
|
||||||
|
/admin/config:
|
||||||
|
get:
|
||||||
|
description: 获取系统的完整配置文件内容(仅管理员)
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/config.Config'
|
||||||
|
security:
|
||||||
|
- AdminAuth: []
|
||||||
|
summary: 获取完整配置
|
||||||
|
tags:
|
||||||
|
- Admin
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 更新系统的配置文件内容(仅管理员)
|
||||||
|
parameters:
|
||||||
|
- description: 新配置内容
|
||||||
|
in: body
|
||||||
|
name: config
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/config.Config'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.Response'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.Response'
|
||||||
|
security:
|
||||||
|
- AdminAuth: []
|
||||||
|
summary: 更新配置
|
||||||
|
tags:
|
||||||
|
- Admin
|
||||||
/admin/login:
|
/admin/login:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -607,6 +757,24 @@ paths:
|
|||||||
summary: 发送长文本
|
summary: 发送长文本
|
||||||
tags:
|
tags:
|
||||||
- Public
|
- Public
|
||||||
|
/api/config:
|
||||||
|
get:
|
||||||
|
description: 获取前端展示所需的非敏感配置数据
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/model.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/public.PublicConfig'
|
||||||
|
type: object
|
||||||
|
summary: 获取公共配置
|
||||||
|
tags:
|
||||||
|
- Public
|
||||||
/api/files/{file_id}/download:
|
/api/files/{file_id}/download:
|
||||||
get:
|
get:
|
||||||
description: 根据文件 ID 下载单个文件
|
description: 根据文件 ID 下载单个文件
|
||||||
|
|||||||
70
internal/api/admin/config.go
Normal file
70
internal/api/admin/config.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"FileRelay/internal/bootstrap"
|
||||||
|
"FileRelay/internal/config"
|
||||||
|
"FileRelay/internal/model"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigHandler struct{}
|
||||||
|
|
||||||
|
func NewConfigHandler() *ConfigHandler {
|
||||||
|
return &ConfigHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfig 获取当前完整配置
|
||||||
|
// @Summary 获取完整配置
|
||||||
|
// @Description 获取系统的完整配置文件内容(仅管理员)
|
||||||
|
// @Tags Admin
|
||||||
|
// @Security AdminAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} config.Config
|
||||||
|
// @Router /admin/config [get]
|
||||||
|
func (h *ConfigHandler) GetConfig(c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, config.GlobalConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig 更新配置
|
||||||
|
// @Summary 更新配置
|
||||||
|
// @Description 更新系统的配置文件内容(仅管理员)
|
||||||
|
// @Tags Admin
|
||||||
|
// @Security AdminAuth
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param config body config.Config true "新配置内容"
|
||||||
|
// @Success 200 {object} model.Response
|
||||||
|
// @Failure 400 {object} model.Response
|
||||||
|
// @Failure 500 {object} model.Response
|
||||||
|
// @Router /admin/config [put]
|
||||||
|
func (h *ConfigHandler) UpdateConfig(c *gin.Context) {
|
||||||
|
var newConfig config.Config
|
||||||
|
if err := c.ShouldBindJSON(&newConfig); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, model.ErrorResponse(model.CodeBadRequest, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的校验,防止数据库路径被改空等关键错误
|
||||||
|
if newConfig.Database.Path == "" {
|
||||||
|
newConfig.Database.Path = config.GlobalConfig.Database.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新内存配置
|
||||||
|
config.UpdateGlobalConfig(&newConfig)
|
||||||
|
|
||||||
|
// 重新初始化存储(热更新业务逻辑)
|
||||||
|
if err := bootstrap.ReloadStorage(); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, model.ErrorResponse(model.CodeInternalError, "Failed to reload storage: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到文件
|
||||||
|
if err := config.SaveConfig(); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, model.ErrorResponse(model.CodeInternalError, "Failed to save config: "+err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, model.SuccessResponse("Config updated successfully and hot-reloaded"))
|
||||||
|
}
|
||||||
41
internal/api/public/config.go
Normal file
41
internal/api/public/config.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package public
|
||||||
|
|
||||||
|
import (
|
||||||
|
"FileRelay/internal/config"
|
||||||
|
"FileRelay/internal/model"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigHandler struct{}
|
||||||
|
|
||||||
|
func NewConfigHandler() *ConfigHandler {
|
||||||
|
return &ConfigHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicConfig 公开配置结构
|
||||||
|
type PublicConfig struct {
|
||||||
|
Site config.SiteConfig `json:"site"`
|
||||||
|
Upload config.UploadConfig `json:"upload"`
|
||||||
|
APIToken struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
} `json:"api_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicConfig 获取非敏感配置
|
||||||
|
// @Summary 获取公共配置
|
||||||
|
// @Description 获取前端展示所需的非敏感配置数据
|
||||||
|
// @Tags Public
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} model.Response{data=PublicConfig}
|
||||||
|
// @Router /api/config [get]
|
||||||
|
func (h *ConfigHandler) GetPublicConfig(c *gin.Context) {
|
||||||
|
pub := PublicConfig{
|
||||||
|
Site: config.GlobalConfig.Site,
|
||||||
|
Upload: config.GlobalConfig.Upload,
|
||||||
|
}
|
||||||
|
pub.APIToken.Enabled = config.GlobalConfig.APIToken.Enabled
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, model.SuccessResponse(pub))
|
||||||
|
}
|
||||||
@@ -43,13 +43,15 @@ func InitDB() {
|
|||||||
fmt.Println("Database initialized and migrated.")
|
fmt.Println("Database initialized and migrated.")
|
||||||
|
|
||||||
// 初始化存储
|
// 初始化存储
|
||||||
initStorage()
|
if err := ReloadStorage(); err != nil {
|
||||||
|
log.Fatalf("Failed to initialize storage: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化管理员 (如果不存在)
|
// 初始化管理员 (如果不存在)
|
||||||
initAdmin()
|
initAdmin()
|
||||||
}
|
}
|
||||||
|
|
||||||
func initStorage() {
|
func ReloadStorage() error {
|
||||||
storageType := config.GlobalConfig.Storage.Type
|
storageType := config.GlobalConfig.Storage.Type
|
||||||
switch storageType {
|
switch storageType {
|
||||||
case "local":
|
case "local":
|
||||||
@@ -61,13 +63,14 @@ func initStorage() {
|
|||||||
cfg := config.GlobalConfig.Storage.S3
|
cfg := config.GlobalConfig.Storage.S3
|
||||||
s3Storage, err := storage.NewS3Storage(context.Background(), cfg.Endpoint, cfg.Region, cfg.AccessKey, cfg.SecretKey, cfg.Bucket, cfg.UseSSL)
|
s3Storage, err := storage.NewS3Storage(context.Background(), cfg.Endpoint, cfg.Region, cfg.AccessKey, cfg.SecretKey, cfg.Bucket, cfg.UseSSL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to initialize S3 storage: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
storage.GlobalStorage = s3Storage
|
storage.GlobalStorage = s3Storage
|
||||||
default:
|
default:
|
||||||
log.Fatalf("Unsupported storage type: %s", storageType)
|
return fmt.Errorf("unsupported storage type: %s", storageType)
|
||||||
}
|
}
|
||||||
fmt.Printf("Storage initialized with type: %s\n", storageType)
|
fmt.Printf("Storage initialized with type: %s\n", storageType)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initAdmin() {
|
func initAdmin() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
@@ -64,9 +65,16 @@ type DatabaseConfig struct {
|
|||||||
Path string `yaml:"path"`
|
Path string `yaml:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var GlobalConfig *Config
|
var (
|
||||||
|
GlobalConfig *Config
|
||||||
|
ConfigPath string
|
||||||
|
configLock sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
func LoadConfig(path string) error {
|
func LoadConfig(path string) error {
|
||||||
|
configLock.Lock()
|
||||||
|
defer configLock.Unlock()
|
||||||
|
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -78,5 +86,24 @@ func LoadConfig(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GlobalConfig = &cfg
|
GlobalConfig = &cfg
|
||||||
|
ConfigPath = path
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SaveConfig() error {
|
||||||
|
configLock.RLock()
|
||||||
|
defer configLock.RUnlock()
|
||||||
|
|
||||||
|
data, err := yaml.Marshal(GlobalConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(ConfigPath, data, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateGlobalConfig(cfg *Config) {
|
||||||
|
configLock.Lock()
|
||||||
|
defer configLock.Unlock()
|
||||||
|
GlobalConfig = cfg
|
||||||
|
}
|
||||||
|
|||||||
47
internal/storage/local.go
Normal file
47
internal/storage/local.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LocalStorage struct {
|
||||||
|
RootPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalStorage(rootPath string) *LocalStorage {
|
||||||
|
// 确保根目录存在
|
||||||
|
if _, err := os.Stat(rootPath); os.IsNotExist(err) {
|
||||||
|
os.MkdirAll(rootPath, 0755)
|
||||||
|
}
|
||||||
|
return &LocalStorage{RootPath: rootPath}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalStorage) Save(ctx context.Context, path string, reader io.Reader) error {
|
||||||
|
fullPath := filepath.Join(s.RootPath, path)
|
||||||
|
dir := filepath.Dir(fullPath)
|
||||||
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
|
os.MkdirAll(dir, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(file, reader)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalStorage) Open(ctx context.Context, path string) (io.ReadCloser, error) {
|
||||||
|
fullPath := filepath.Join(s.RootPath, path)
|
||||||
|
return os.Open(fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalStorage) Delete(ctx context.Context, path string) error {
|
||||||
|
fullPath := filepath.Join(s.RootPath, path)
|
||||||
|
return os.Remove(fullPath)
|
||||||
|
}
|
||||||
72
internal/storage/s3.go
Normal file
72
internal/storage/s3.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type S3Storage struct {
|
||||||
|
client *s3.Client
|
||||||
|
bucket string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewS3Storage(ctx context.Context, endpoint, region, accessKey, secretKey, bucket string, useSSL bool) (*S3Storage, error) {
|
||||||
|
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
||||||
|
if endpoint != "" {
|
||||||
|
return aws.Endpoint{
|
||||||
|
URL: endpoint,
|
||||||
|
SigningRegion: region,
|
||||||
|
HostnameImmutable: true,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
|
||||||
|
})
|
||||||
|
|
||||||
|
cfg, err := config.LoadDefaultConfig(ctx,
|
||||||
|
config.WithRegion(region),
|
||||||
|
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
|
||||||
|
config.WithEndpointResolverWithOptions(customResolver),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := s3.NewFromConfig(cfg)
|
||||||
|
return &S3Storage{
|
||||||
|
client: client,
|
||||||
|
bucket: bucket,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *S3Storage) Save(ctx context.Context, path string, reader io.Reader) error {
|
||||||
|
_, err := s.client.PutObject(ctx, &s3.PutObjectInput{
|
||||||
|
Bucket: aws.String(s.bucket),
|
||||||
|
Key: aws.String(path),
|
||||||
|
Body: reader,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *S3Storage) Open(ctx context.Context, path string) (io.ReadCloser, error) {
|
||||||
|
output, err := s.client.GetObject(ctx, &s3.GetObjectInput{
|
||||||
|
Bucket: aws.String(s.bucket),
|
||||||
|
Key: aws.String(path),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return output.Body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *S3Storage) Delete(ctx context.Context, path string) error {
|
||||||
|
_, err := s.client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||||
|
Bucket: aws.String(s.bucket),
|
||||||
|
Key: aws.String(path),
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
14
internal/storage/storage.go
Normal file
14
internal/storage/storage.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Storage interface {
|
||||||
|
Save(ctx context.Context, path string, reader io.Reader) error
|
||||||
|
Open(ctx context.Context, path string) (io.ReadCloser, error)
|
||||||
|
Delete(ctx context.Context, path string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var GlobalStorage Storage
|
||||||
54
internal/storage/webdav.go
Normal file
54
internal/storage/webdav.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/studio-b12/gowebdav"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebDAVStorage struct {
|
||||||
|
client *gowebdav.Client
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebDAVStorage(url, username, password, root string) *WebDAVStorage {
|
||||||
|
client := gowebdav.NewClient(url, username, password)
|
||||||
|
return &WebDAVStorage{
|
||||||
|
client: client,
|
||||||
|
root: root,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WebDAVStorage) getFullPath(path string) string {
|
||||||
|
return filepath.ToSlash(filepath.Join(s.root, path))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WebDAVStorage) Save(ctx context.Context, path string, reader io.Reader) error {
|
||||||
|
fullPath := s.getFullPath(path)
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
dir := filepath.Dir(fullPath)
|
||||||
|
if dir != "." && dir != "/" {
|
||||||
|
parts := strings.Split(strings.Trim(dir, "/"), "/")
|
||||||
|
current := ""
|
||||||
|
for _, part := range parts {
|
||||||
|
current += "/" + part
|
||||||
|
_ = s.client.Mkdir(current, 0755)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.WriteStream(fullPath, reader, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WebDAVStorage) Open(ctx context.Context, path string) (io.ReadCloser, error) {
|
||||||
|
fullPath := s.getFullPath(path)
|
||||||
|
return s.client.ReadStream(fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WebDAVStorage) Delete(ctx context.Context, path string) error {
|
||||||
|
fullPath := s.getFullPath(path)
|
||||||
|
return s.client.Remove(fullPath)
|
||||||
|
}
|
||||||
6
main.go
6
main.go
@@ -66,9 +66,11 @@ func main() {
|
|||||||
// 公共接口
|
// 公共接口
|
||||||
uploadHandler := public.NewUploadHandler()
|
uploadHandler := public.NewUploadHandler()
|
||||||
pickupHandler := public.NewPickupHandler()
|
pickupHandler := public.NewPickupHandler()
|
||||||
|
publicConfigHandler := public.NewConfigHandler()
|
||||||
|
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
{
|
{
|
||||||
|
api.GET("/config", publicConfigHandler.GetPublicConfig)
|
||||||
// 统一使用 /batches 作为资源路径
|
// 统一使用 /batches 作为资源路径
|
||||||
api.POST("/batches", uploadHandler.Upload)
|
api.POST("/batches", uploadHandler.Upload)
|
||||||
api.POST("/batches/text", uploadHandler.UploadText)
|
api.POST("/batches/text", uploadHandler.UploadText)
|
||||||
@@ -85,12 +87,16 @@ func main() {
|
|||||||
authHandler := admin.NewAuthHandler()
|
authHandler := admin.NewAuthHandler()
|
||||||
batchHandler := admin.NewBatchHandler()
|
batchHandler := admin.NewBatchHandler()
|
||||||
tokenHandler := admin.NewTokenHandler()
|
tokenHandler := admin.NewTokenHandler()
|
||||||
|
configHandler := admin.NewConfigHandler()
|
||||||
|
|
||||||
r.POST("/admin/login", authHandler.Login)
|
r.POST("/admin/login", authHandler.Login)
|
||||||
|
|
||||||
adm := r.Group("/admin")
|
adm := r.Group("/admin")
|
||||||
adm.Use(middleware.AdminAuth())
|
adm.Use(middleware.AdminAuth())
|
||||||
{
|
{
|
||||||
|
adm.GET("/config", configHandler.GetConfig)
|
||||||
|
adm.PUT("/config", configHandler.UpdateConfig)
|
||||||
|
|
||||||
adm.GET("/batches", batchHandler.ListBatches)
|
adm.GET("/batches", batchHandler.ListBatches)
|
||||||
adm.GET("/batches/:batch_id", batchHandler.GetBatch)
|
adm.GET("/batches/:batch_id", batchHandler.GetBatch)
|
||||||
adm.PUT("/batches/:batch_id", batchHandler.UpdateBatch)
|
adm.PUT("/batches/:batch_id", batchHandler.UpdateBatch)
|
||||||
|
|||||||
Reference in New Issue
Block a user