提交
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"seeyon-filesystem/config"
|
||||
"seeyon-filesystem/dto"
|
||||
"seeyon-filesystem/model"
|
||||
"seeyon-filesystem/service"
|
||||
@@ -24,6 +26,99 @@ func NewAdminController() *AdminController {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 授权管理 ==========
|
||||
|
||||
// GetLicense 获取当前授权信息
|
||||
// GET /api/admin/license
|
||||
func (c *AdminController) GetLicense(ctx *gin.Context) {
|
||||
licensePath := config.AppConfig.License.FilePath
|
||||
if licensePath == "" {
|
||||
licensePath = "license.json"
|
||||
}
|
||||
|
||||
resp, err := utils.ReadAndVerifyLicense(licensePath, config.AppConfig.License.VerifyURL, "seeyon-filesystem", "1.0")
|
||||
if err != nil {
|
||||
utils.Success(ctx, gin.H{
|
||||
"status": "invalid",
|
||||
"message": err.Error(),
|
||||
"machine_id": utils.GetMachineID(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
utils.Success(ctx, gin.H{
|
||||
"status": map[bool]string{true: "valid", false: "invalid"}[resp.Valid],
|
||||
"message": resp.Message,
|
||||
"machine_id": utils.GetMachineID(),
|
||||
"product": resp.Product,
|
||||
"customer_id": resp.CustomerID,
|
||||
"customer": resp.Customer,
|
||||
"contact": resp.Contact,
|
||||
"phone": resp.Phone,
|
||||
"expire_at": resp.ExpireAt,
|
||||
"max_users": resp.MaxUsers,
|
||||
"max_files": resp.MaxFiles,
|
||||
"features": resp.Features,
|
||||
"expire_days": resp.ExpireDays,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateLicense 生成授权码
|
||||
// POST /api/admin/license/generate
|
||||
func (c *AdminController) GenerateLicense(ctx *gin.Context) {
|
||||
var req struct {
|
||||
TargetMachineID string `json:"target_machine_id"`
|
||||
CustomerID string `json:"customer_id"`
|
||||
Customer string `json:"customer"`
|
||||
Contact string `json:"contact"`
|
||||
Phone string `json:"phone"`
|
||||
MaxUsers int `json:"max_users"`
|
||||
MaxFiles int `json:"max_files"`
|
||||
Features string `json:"features"`
|
||||
ExpireAt string `json:"expire_at"` // RFC3339格式, 空=永久
|
||||
Version string `json:"version"`
|
||||
}
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
utils.BadRequest(ctx, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if req.Features == "" {
|
||||
req.Features = "*"
|
||||
}
|
||||
if req.Version == "" {
|
||||
req.Version = "1.0"
|
||||
}
|
||||
|
||||
license := &utils.LicenseInfo{
|
||||
Product: "seeyon-filesystem",
|
||||
CustomerID: req.CustomerID,
|
||||
Customer: req.Customer,
|
||||
Contact: req.Contact,
|
||||
Phone: req.Phone,
|
||||
MachineID: req.TargetMachineID,
|
||||
MaxUsers: req.MaxUsers,
|
||||
MaxFiles: req.MaxFiles,
|
||||
Features: req.Features,
|
||||
ExpireAt: req.ExpireAt,
|
||||
}
|
||||
utils.SignLicense(license)
|
||||
|
||||
// 返回标准格式
|
||||
licenseFile := map[string]interface{}{
|
||||
"productType": "seeyon-filesystem",
|
||||
"version": req.Version,
|
||||
"licenseStr": license.LicenseStr,
|
||||
}
|
||||
fileJSON, _ := json.Marshal(licenseFile)
|
||||
|
||||
utils.Success(ctx, gin.H{
|
||||
"license_file": string(fileJSON), // 保存为 license.json
|
||||
"license": license,
|
||||
"machine_id": utils.GetMachineID(),
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 用户管理 ==========
|
||||
|
||||
// ListUsers 用户列表
|
||||
|
||||
@@ -116,3 +116,32 @@ func (c *AuthController) ChangePassword(ctx *gin.Context) {
|
||||
|
||||
utils.SuccessWithMessage(ctx, "密码修改成功", nil)
|
||||
}
|
||||
|
||||
// Logout 退出登录
|
||||
// POST /api/auth/logout
|
||||
func (c *AuthController) Logout(ctx *gin.Context) {
|
||||
userID := middleware.GetCurrentUserID(ctx)
|
||||
token := ctx.GetHeader("token")
|
||||
if token == "" {
|
||||
authHeader := ctx.GetHeader("Authorization")
|
||||
if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
|
||||
token = authHeader[7:]
|
||||
}
|
||||
}
|
||||
if userID > 0 && token != "" {
|
||||
c.authService.Logout(userID, token)
|
||||
}
|
||||
utils.SuccessWithMessage(ctx, "已退出登录", nil)
|
||||
}
|
||||
|
||||
// GetSessions 获取当前用户的登录会话列表
|
||||
// GET /api/auth/sessions
|
||||
func (c *AuthController) GetSessions(ctx *gin.Context) {
|
||||
userID := middleware.GetCurrentUserID(ctx)
|
||||
if userID == 0 {
|
||||
utils.Unauthorized(ctx, "未登录")
|
||||
return
|
||||
}
|
||||
sessions := c.authService.GetSessions(userID)
|
||||
utils.Success(ctx, sessions)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -57,8 +59,14 @@ func (c *FileController) Upload(ctx *gin.Context) {
|
||||
}
|
||||
storagePolicyID, _ := strconv.ParseUint(policyIDStr, 10, 64)
|
||||
|
||||
// 获取业务ID(可选)
|
||||
bizID := ctx.DefaultQuery("biz_id", "")
|
||||
if bizID == "" {
|
||||
bizID = ctx.PostForm("biz_id")
|
||||
}
|
||||
|
||||
// 调用服务层
|
||||
result, err := c.fileService.Upload(fileHeader, folderID, ownerID, storagePolicyID)
|
||||
result, err := c.fileService.Upload(fileHeader, folderID, ownerID, storagePolicyID, bizID)
|
||||
if err != nil {
|
||||
utils.ServerError(ctx, err.Error())
|
||||
return
|
||||
@@ -150,7 +158,7 @@ func (c *FileController) GetPreviewURL(ctx *gin.Context) {
|
||||
utils.Success(ctx, gin.H{"preview_url": previewURL})
|
||||
}
|
||||
|
||||
// Preview 文件预览(直接返回文件流, 浏览器内联展示)
|
||||
// Preview 文件预览(直接返回文件流, 浏览器内联展示, 支持Range请求)
|
||||
// GET /api/file/:id/preview
|
||||
func (c *FileController) Preview(ctx *gin.Context) {
|
||||
ownerID := middleware.GetCurrentUserID(ctx)
|
||||
@@ -168,9 +176,58 @@ func (c *FileController) Preview(ctx *gin.Context) {
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// 内联展示而非下载
|
||||
ctx.Header("Content-Disposition", "inline; filename=\""+file.Name+"\"")
|
||||
ctx.Header("Content-Type", file.MimeType)
|
||||
ctx.Header("Accept-Ranges", "bytes")
|
||||
|
||||
// Range请求: 视频/音频需要分段加载才能内联播放
|
||||
rangeHeader := ctx.GetHeader("Range")
|
||||
if rangeHeader != "" {
|
||||
fileSize := file.Size
|
||||
var start, end int64
|
||||
_, err := fmt.Sscanf(rangeHeader, "bytes=%d-%d", &start, &end)
|
||||
if err != nil {
|
||||
// 只有start没有end
|
||||
fmt.Sscanf(rangeHeader, "bytes=%d-", &start)
|
||||
end = fileSize - 1
|
||||
}
|
||||
if start >= fileSize || start < 0 {
|
||||
ctx.Status(http.StatusRequestedRangeNotSatisfiable)
|
||||
return
|
||||
}
|
||||
if end >= fileSize {
|
||||
end = fileSize - 1
|
||||
}
|
||||
length := end - start + 1
|
||||
|
||||
// 跳到起始位置
|
||||
if seeker, ok := reader.(io.Seeker); ok {
|
||||
seeker.Seek(start, io.SeekStart)
|
||||
}
|
||||
|
||||
ctx.Header("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize))
|
||||
ctx.Header("Content-Length", fmt.Sprintf("%d", length))
|
||||
ctx.Status(http.StatusPartialContent)
|
||||
|
||||
buf := make([]byte, 32*1024)
|
||||
remaining := length
|
||||
for remaining > 0 {
|
||||
toRead := int64(len(buf))
|
||||
if toRead > remaining {
|
||||
toRead = remaining
|
||||
}
|
||||
n, readErr := reader.Read(buf[:toRead])
|
||||
if n > 0 {
|
||||
ctx.Writer.Write(buf[:n])
|
||||
remaining -= int64(n)
|
||||
}
|
||||
if readErr != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.DataFromReader(http.StatusOK, file.Size, file.MimeType, reader, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,11 @@ func (c *OpenAPIController) BindUsers(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.openService.BindUsers(id, req.UserIDs); err != nil {
|
||||
userIDs := make([]uint64, len(req.UserIDs))
|
||||
for i, uid := range req.UserIDs {
|
||||
userIDs[i] = uint64(uid)
|
||||
}
|
||||
if err := c.openService.BindUsers(id, userIDs); err != nil {
|
||||
utils.BadRequest(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func (c *PermissionController) ListRoles(ctx *gin.Context) {
|
||||
perms := make([]dto.PermissionVO, 0, len(r.Permissions))
|
||||
for _, p := range r.Permissions {
|
||||
perms = append(perms, dto.PermissionVO{
|
||||
ID: p.ID,
|
||||
ID: dto.StringID(p.ID),
|
||||
Code: p.Code,
|
||||
Name: p.Name,
|
||||
Resource: p.Resource,
|
||||
@@ -48,7 +48,7 @@ func (c *PermissionController) ListRoles(ctx *gin.Context) {
|
||||
})
|
||||
}
|
||||
vos = append(vos, dto.RoleVO{
|
||||
ID: r.ID,
|
||||
ID: dto.StringID(r.ID),
|
||||
Name: r.Name,
|
||||
DisplayName: r.DisplayName,
|
||||
Description: r.Description,
|
||||
@@ -145,7 +145,11 @@ func (c *PermissionController) SetRolePermissions(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.permService.SetRolePermissions(roleID, req.PermissionIDs); err != nil {
|
||||
permIDs := make([]uint64, len(req.PermissionIDs))
|
||||
for i, id := range req.PermissionIDs {
|
||||
permIDs[i] = uint64(id)
|
||||
}
|
||||
if err := c.permService.SetRolePermissions(roleID, permIDs); err != nil {
|
||||
utils.BadRequest(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -167,7 +171,7 @@ func (c *PermissionController) ListPermissions(ctx *gin.Context) {
|
||||
vos := make([]dto.PermissionVO, 0, len(perms))
|
||||
for _, p := range perms {
|
||||
vos = append(vos, dto.PermissionVO{
|
||||
ID: p.ID,
|
||||
ID: dto.StringID(p.ID),
|
||||
Code: p.Code,
|
||||
Name: p.Name,
|
||||
Resource: p.Resource,
|
||||
@@ -238,7 +242,11 @@ func (c *PermissionController) SetUserRoles(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.permService.SetUserRoles(userID, req.RoleIDs); err != nil {
|
||||
roleIDs := make([]uint64, len(req.RoleIDs))
|
||||
for i, id := range req.RoleIDs {
|
||||
roleIDs[i] = uint64(id)
|
||||
}
|
||||
if err := c.permService.SetUserRoles(userID, roleIDs); err != nil {
|
||||
utils.BadRequest(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -272,7 +280,7 @@ func (c *PermissionController) GetUserPermissions(ctx *gin.Context) {
|
||||
roleVOs := make([]dto.RoleVO, 0, len(roles))
|
||||
for _, r := range roles {
|
||||
roleVOs = append(roleVOs, dto.RoleVO{
|
||||
ID: r.ID,
|
||||
ID: dto.StringID(r.ID),
|
||||
Name: r.Name,
|
||||
DisplayName: r.DisplayName,
|
||||
})
|
||||
|
||||
@@ -151,6 +151,7 @@ func (c *SSOController) AdminCreateProvider(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
provider := &model.SSOProvider{
|
||||
ID: utils.GenID(),
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
DisplayName: req.DisplayName,
|
||||
|
||||
Reference in New Issue
Block a user