feat: 分片上传断点续传、临时目录后台配置与清扫、宇恒云账号管理
- 管理端大文件分片上传与 sessionStorage 续传;Nginx 大请求体/超时 - .chunk-uploads 定期清扫;system_config 后台配置保留时长与扫描间隔 - 宇恒云 POST /register 对接与 yuheng_cloud_register_records 留痕;yuheng_cloud:manage 权限 Made-with: Cursor
This commit is contained in:
182
server/handlers/yuheng_cloud_register.go
Normal file
182
server/handlers/yuheng_cloud_register.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yh_web/server/config"
|
||||
"yh_web/server/models"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo/options"
|
||||
)
|
||||
|
||||
const yuhengCloudRegisterColl = "yuheng_cloud_register_records"
|
||||
|
||||
func cloudRegisterURL() string {
|
||||
u := strings.TrimSpace(os.Getenv("YH_CLOUD_REGISTER_URL"))
|
||||
if u != "" {
|
||||
return strings.TrimSuffix(u, "/")
|
||||
}
|
||||
return "http://www.cloud.yuxindazhineng.com:3001/register"
|
||||
}
|
||||
|
||||
// YuhengCloudRegisterInput 与云端 POST /register 一致;email 仅用于调用云端,不写入 Mongo
|
||||
type YuhengCloudRegisterInput struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
}
|
||||
|
||||
type cloudRegisterPayload struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func postCloudRegister(ctx context.Context, payload cloudRegisterPayload) (int, string, error) {
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, cloudRegisterURL(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := &http.Client{Timeout: 45 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
||||
return resp.StatusCode, strings.TrimSpace(string(b)), nil
|
||||
}
|
||||
|
||||
// CreateYuhengCloudRegister 调用云端注册接口,成功后在 Mongo 写入一条记录(仅 username、password)
|
||||
func CreateYuhengCloudRegister(c *gin.Context) {
|
||||
var in YuhengCloudRegisterInput
|
||||
if err := c.ShouldBindJSON(&in); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写用户名、密码与邮箱(邮箱仅提交云端)"})
|
||||
return
|
||||
}
|
||||
in.Username = strings.TrimSpace(in.Username)
|
||||
in.Password = strings.TrimSpace(in.Password)
|
||||
in.Email = strings.TrimSpace(in.Email)
|
||||
if in.Username == "" || in.Password == "" || in.Email == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "用户名、密码、邮箱不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 50*time.Second)
|
||||
defer cancel()
|
||||
|
||||
status, bodySnippet, err := postCloudRegister(ctx, cloudRegisterPayload{
|
||||
Username: in.Username,
|
||||
Password: in.Password,
|
||||
Email: in.Email,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "调用云端注册失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
if status < 200 || status >= 300 {
|
||||
msg := bodySnippet
|
||||
if len(msg) > 500 {
|
||||
msg = msg[:500] + "…"
|
||||
}
|
||||
if msg == "" {
|
||||
msg = http.StatusText(status)
|
||||
}
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("云端返回 %d: %s", status, msg)})
|
||||
return
|
||||
}
|
||||
|
||||
db := config.GetDB(config.DBName)
|
||||
if db == nil {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "数据库未连接,未写入本地记录"})
|
||||
return
|
||||
}
|
||||
insCtx, insCancel := context.WithTimeout(context.Background(), 8*time.Second)
|
||||
defer insCancel()
|
||||
doc := bson.M{
|
||||
"username": in.Username,
|
||||
"password": in.Password,
|
||||
"created_at": time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
res, err := db.Collection(yuhengCloudRegisterColl).InsertOne(insCtx, doc)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "云端已成功但本地记录失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
idHex := ""
|
||||
switch v := res.InsertedID.(type) {
|
||||
case bson.ObjectID:
|
||||
idHex = v.Hex()
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"id": idHex, "message": "已提交云端注册并写入本地记录"})
|
||||
}
|
||||
|
||||
// ListYuhengCloudRegisterRecords 分页列出本地留痕(便于管理页展示)
|
||||
func ListYuhengCloudRegisterRecords(c *gin.Context) {
|
||||
db := config.GetDB(config.DBName)
|
||||
if db == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"list": []any{}, "total": 0})
|
||||
return
|
||||
}
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
skip := int64((page - 1) * pageSize)
|
||||
limit := int64(pageSize)
|
||||
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
|
||||
defer cancel()
|
||||
coll := db.Collection(yuhengCloudRegisterColl)
|
||||
total, err := coll.CountDocuments(ctx, bson.M{})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
opts := options.Find().SetSort(bson.D{{Key: "created_at", Value: -1}}).SetSkip(skip).SetLimit(limit)
|
||||
cur, err := coll.Find(ctx, bson.M{}, opts)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
defer cur.Close(ctx)
|
||||
var list []models.YuhengCloudRegisterRecord
|
||||
for cur.Next(ctx) {
|
||||
var row struct {
|
||||
ID bson.ObjectID `bson:"_id"`
|
||||
Username string `bson:"username"`
|
||||
Password string `bson:"password"`
|
||||
CreatedAt string `bson:"created_at"`
|
||||
}
|
||||
if err := cur.Decode(&row); err != nil {
|
||||
continue
|
||||
}
|
||||
list = append(list, models.YuhengCloudRegisterRecord{
|
||||
ID: row.ID.Hex(),
|
||||
Username: row.Username,
|
||||
Password: row.Password,
|
||||
CreatedAt: row.CreatedAt,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"list": list, "total": total})
|
||||
}
|
||||
Reference in New Issue
Block a user