更新前后端代码
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/repository"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// AdminController 系统管理控制器
|
||||
type AdminController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewAdminController 创建系统管理控制器
|
||||
func NewAdminController(b *Base) *AdminController { return &AdminController{Base: b} }
|
||||
|
||||
// ---- 用户管理 ----
|
||||
|
||||
// ListUsers 用户列表
|
||||
func (ctl *AdminController) ListUsers(c *gin.Context) {
|
||||
page, pageSize := utils.ParsePage(c)
|
||||
users, total, err := ctl.svc.User.AdminListUsers(
|
||||
c.Query("keyword"), c.Query("role_id"), c.Query("status"), page, pageSize)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OKPage(c, users, total, page, pageSize)
|
||||
}
|
||||
|
||||
// GetUser 用户详情
|
||||
func (ctl *AdminController) GetUser(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
u, err := ctl.svc.User.AdminGetUser(id)
|
||||
if err != nil {
|
||||
utils.NotFound(c, "用户不存在")
|
||||
return
|
||||
}
|
||||
utils.OK(c, u)
|
||||
}
|
||||
|
||||
type adminCreateUserReq struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
RoleID uint `json:"role_id"`
|
||||
StorageLimit int64 `json:"storage_limit"`
|
||||
}
|
||||
|
||||
// CreateUser 创建用户
|
||||
func (ctl *AdminController) CreateUser(c *gin.Context) {
|
||||
var req adminCreateUserReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
u, err := ctl.svc.User.AdminCreateUser(req.Username, req.Email, req.Password, req.RoleID, req.StorageLimit)
|
||||
if err != nil {
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", 0, req.Username, false, err.Error())
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", u.ID, u.Username, true, "")
|
||||
utils.OK(c, u)
|
||||
}
|
||||
|
||||
type adminUpdateUserReq struct {
|
||||
Email string `json:"email"`
|
||||
RoleID uint `json:"role_id"`
|
||||
StorageLimit int64 `json:"storage_limit"`
|
||||
}
|
||||
|
||||
// UpdateUser 编辑用户
|
||||
func (ctl *AdminController) UpdateUser(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req adminUpdateUserReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
u, err := ctl.svc.User.AdminUpdateUser(id, req.Email, req.RoleID, req.StorageLimit)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, u.Username, true, "")
|
||||
utils.OK(c, u)
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
func (ctl *AdminController) DeleteUser(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.User.AdminDeleteUser(id); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "user", id, "", true, "")
|
||||
utils.OKMsg(c, "用户已删除")
|
||||
}
|
||||
|
||||
type setUserStatusReq struct {
|
||||
Status int8 `json:"status"` // 1启用 0禁用(不加required,0为合法值)
|
||||
}
|
||||
|
||||
// SetUserStatus 启用/禁用用户
|
||||
func (ctl *AdminController) SetUserStatus(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req setUserStatusReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if req.Status != 0 && req.Status != 1 {
|
||||
utils.FailMsg(c, "status仅支持0或1")
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.User.AdminSetUserStatus(id, req.Status); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, "status", true, "")
|
||||
utils.OKMsg(c, "状态已更新")
|
||||
}
|
||||
|
||||
type assignRoleReq struct {
|
||||
RoleID uint `json:"role_id" binding:"required"`
|
||||
}
|
||||
|
||||
// AssignRole 修改用户角色
|
||||
func (ctl *AdminController) AssignRole(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req assignRoleReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.User.AdminAssignRole(id, req.RoleID); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, "role", true, "")
|
||||
utils.OKMsg(c, "角色已更新")
|
||||
}
|
||||
|
||||
type resetPasswordReq struct {
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// ResetPassword 重置用户密码
|
||||
func (ctl *AdminController) ResetPassword(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req resetPasswordReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.User.AdminResetPassword(id, req.Password); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, "password", true, "")
|
||||
utils.OKMsg(c, "密码已重置")
|
||||
}
|
||||
|
||||
// ---- 系统设置 ----
|
||||
|
||||
// GetSettings 获取全部设置
|
||||
func (ctl *AdminController) GetSettings(c *gin.Context) {
|
||||
groups, err := ctl.svc.Admin.GetSettings()
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, groups)
|
||||
}
|
||||
|
||||
// GetSettingsByGroup 按分组获取设置
|
||||
func (ctl *AdminController) GetSettingsByGroup(c *gin.Context) {
|
||||
group := c.Param("group")
|
||||
settings, err := ctl.svc.Admin.GetSettingsByGroup(group)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, settings)
|
||||
}
|
||||
|
||||
// UpdateSettings 批量更新设置
|
||||
func (ctl *AdminController) UpdateSettings(c *gin.Context) {
|
||||
var req map[string]string
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.Admin.UpdateSettings(req); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "system", 0, "settings", true, "")
|
||||
utils.OKMsg(c, "设置已更新")
|
||||
}
|
||||
|
||||
// UpdateSettingsByGroup 按分组更新设置
|
||||
func (ctl *AdminController) UpdateSettingsByGroup(c *gin.Context) {
|
||||
var req map[string]string
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.Admin.UpdateSettings(req); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "system", 0, "settings:"+c.Param("group"), true, "")
|
||||
utils.OKMsg(c, "设置已更新")
|
||||
}
|
||||
|
||||
// ---- 日志管理 ----
|
||||
|
||||
// ListOperationLogs 操作日志列表
|
||||
func (ctl *AdminController) ListOperationLogs(c *gin.Context) {
|
||||
page, pageSize := utils.ParsePage(c)
|
||||
f := repository.OpLogFilter{
|
||||
Username: c.Query("username"),
|
||||
Action: c.Query("action"),
|
||||
Status: c.Query("status"),
|
||||
Days: parseIntQuery(c, "days", 0),
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}
|
||||
if uid := parseUintQuery(c, "user_id"); uid > 0 {
|
||||
f.UserID = &uid
|
||||
}
|
||||
logs, total, err := ctl.svc.Admin.ListOperationLogs(f)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OKPage(c, logs, total, page, pageSize)
|
||||
}
|
||||
|
||||
// GetOperationLog 操作日志详情
|
||||
func (ctl *AdminController) GetOperationLog(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
l, err := ctl.svc.Admin.GetOperationLog(id)
|
||||
if err != nil {
|
||||
utils.NotFound(c, "日志不存在")
|
||||
return
|
||||
}
|
||||
utils.OK(c, l)
|
||||
}
|
||||
|
||||
// ListSystemLogs 系统日志列表
|
||||
func (ctl *AdminController) ListSystemLogs(c *gin.Context) {
|
||||
page, pageSize := utils.ParsePage(c)
|
||||
f := repository.SysLogFilter{
|
||||
Level: c.Query("level"),
|
||||
Module: c.Query("module"),
|
||||
Days: parseIntQuery(c, "days", 0),
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}
|
||||
logs, total, err := ctl.svc.Admin.ListSystemLogs(f)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OKPage(c, logs, total, page, pageSize)
|
||||
}
|
||||
|
||||
// GetSystemLog 系统日志详情
|
||||
func (ctl *AdminController) GetSystemLog(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
l, err := ctl.svc.Admin.GetSystemLog(id)
|
||||
if err != nil {
|
||||
utils.NotFound(c, "日志不存在")
|
||||
return
|
||||
}
|
||||
utils.OK(c, l)
|
||||
}
|
||||
|
||||
// CleanupLogs 清理历史日志
|
||||
func (ctl *AdminController) CleanupLogs(c *gin.Context) {
|
||||
days := parseIntQuery(c, "days", 30)
|
||||
opDeleted, sysDeleted, err := ctl.svc.Admin.CleanupLogs(days)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "system", 0, "logs", true, "")
|
||||
utils.OK(c, gin.H{
|
||||
"days": days,
|
||||
"operation_logs_deleted": opDeleted,
|
||||
"system_logs_deleted": sysDeleted,
|
||||
})
|
||||
}
|
||||
|
||||
// ---- 数据备份 ----
|
||||
|
||||
// Backup 手动备份数据库
|
||||
func (ctl *AdminController) Backup(c *gin.Context) {
|
||||
name, err := ctl.svc.Admin.BackupDatabase()
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "system", 0, "backup:"+name, true, "")
|
||||
utils.OK(c, gin.H{"name": name})
|
||||
}
|
||||
|
||||
// ListBackups 备份列表
|
||||
func (ctl *AdminController) ListBackups(c *gin.Context) {
|
||||
list, err := ctl.svc.Admin.ListBackups()
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, list)
|
||||
}
|
||||
|
||||
// DownloadBackup 下载备份文件
|
||||
func (ctl *AdminController) DownloadBackup(c *gin.Context) {
|
||||
name := c.Query("name")
|
||||
path, err := ctl.svc.Admin.BackupPath(name)
|
||||
if err != nil {
|
||||
utils.NotFound(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "download", "system", 0, "backup:"+name, true, "")
|
||||
c.FileAttachment(path, name)
|
||||
}
|
||||
|
||||
// SiteStatus 公开站点状态接口
|
||||
func (ctl *AdminController) SiteStatus(c *gin.Context) {
|
||||
utils.OK(c, ctl.svc.Admin.SiteStatus())
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/middleware"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// AuthController 认证控制器
|
||||
type AuthController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewAuthController 创建认证控制器
|
||||
func NewAuthController(b *Base) *AuthController { return &AuthController{Base: b} }
|
||||
|
||||
type registerReq struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// Register 用户注册
|
||||
func (ctl *AuthController) Register(c *gin.Context) {
|
||||
var req registerReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
u, err := ctl.svc.Auth.Register(req.Username, req.Email, req.Password)
|
||||
if err != nil {
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", 0, req.Username, false, err.Error())
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", u.ID, u.Username, true, "")
|
||||
utils.OK(c, gin.H{"id": u.ID, "username": u.Username, "email": u.Email})
|
||||
}
|
||||
|
||||
type loginReq struct {
|
||||
Account string `json:"account" binding:"required"` // 用户名或邮箱
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
func (ctl *AuthController) Login(c *gin.Context) {
|
||||
var req loginReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
ip := utils.ClientIP(c)
|
||||
u, token, err := ctl.svc.Auth.Login(req.Account, req.Password, ip)
|
||||
if err != nil {
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "login", "user", 0, req.Account, false, err.Error())
|
||||
utils.Unauthorized(c, err.Error())
|
||||
return
|
||||
}
|
||||
oc := ctl.opCtx(c)
|
||||
uid := u.ID
|
||||
oc.UserID = &uid
|
||||
oc.Username = u.Username
|
||||
ctl.svc.RecordOpLog(oc, "login", "user", u.ID, u.Username, true, "")
|
||||
|
||||
utils.OK(c, gin.H{
|
||||
"token": token,
|
||||
"user": gin.H{
|
||||
"id": u.ID,
|
||||
"username": u.Username,
|
||||
"email": u.Email,
|
||||
"role": u.Role,
|
||||
"storage_used": u.StorageUsed,
|
||||
"storage_limit": u.StorageLimit,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type createAPIKeyReq struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// CreateAPIKey 生成API密钥
|
||||
func (ctl *AuthController) CreateAPIKey(c *gin.Context) {
|
||||
var req createAPIKeyReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
key, secret, err := ctl.svc.Auth.CreateAPIKey(userID, req.Name)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "apikey", key.ID, key.Name, true, "")
|
||||
utils.OK(c, gin.H{
|
||||
"id": key.ID,
|
||||
"access_key": key.AccessKey,
|
||||
"secret_key": secret, // 仅创建时返回一次
|
||||
"name": key.Name,
|
||||
"notice": "secret_key仅在创建时展示,请妥善保存",
|
||||
})
|
||||
}
|
||||
|
||||
// ListAPIKeys 密钥列表
|
||||
func (ctl *AuthController) ListAPIKeys(c *gin.Context) {
|
||||
userID := middleware.CurrentUserID(c)
|
||||
keys, err := ctl.svc.Auth.ListAPIKeys(userID)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, keys)
|
||||
}
|
||||
|
||||
// DeleteAPIKey 删除密钥
|
||||
func (ctl *AuthController) DeleteAPIKey(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
if err := ctl.svc.Auth.DeleteAPIKey(userID, id); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "apikey", id, "", true, "")
|
||||
utils.OKMsg(c, "密钥已删除")
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/middleware"
|
||||
"filestoragesystem/internal/service"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// Base 控制器基类
|
||||
type Base struct {
|
||||
svc *service.Services
|
||||
}
|
||||
|
||||
// NewBase 创建基类
|
||||
func NewBase(svc *service.Services) *Base { return &Base{svc: svc} }
|
||||
|
||||
// opCtx 从请求上下文提取操作日志上下文
|
||||
func (b *Base) opCtx(c *gin.Context) *service.OpLogContext {
|
||||
ctx := &service.OpLogContext{
|
||||
IP: utils.ClientIP(c),
|
||||
UserAgent: c.GetHeader("User-Agent"),
|
||||
Path: c.Request.URL.Path,
|
||||
Method: c.Request.Method,
|
||||
Username: c.GetString(middleware.CtxUsername),
|
||||
}
|
||||
if uid := middleware.CurrentUserID(c); uid > 0 {
|
||||
ctx.UserID = &uid
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
// bindJSON 绑定JSON请求体
|
||||
func bindJSON(c *gin.Context, obj interface{}) bool {
|
||||
if err := c.ShouldBindJSON(obj); err != nil {
|
||||
utils.FailMsg(c, "参数错误: "+err.Error())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// paramID 解析路径参数ID
|
||||
func paramID(c *gin.Context) (uint, bool) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
utils.FailMsg(c, "无效的资源ID")
|
||||
return 0, false
|
||||
}
|
||||
return uint(id), true
|
||||
}
|
||||
|
||||
// parseUintQuery 解析整型query参数
|
||||
func parseUintQuery(c *gin.Context, key string) uint {
|
||||
v, err := strconv.ParseUint(c.Query(key), 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return uint(v)
|
||||
}
|
||||
|
||||
// parseIntQuery 解析整型query参数(可为负)
|
||||
func parseIntQuery(c *gin.Context, key string, def int) int {
|
||||
n, err := strconv.Atoi(c.Query(key))
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
return n
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/middleware"
|
||||
"filestoragesystem/internal/repository"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// FileController 文件控制器
|
||||
type FileController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewFileController 创建文件控制器
|
||||
func NewFileController(b *Base) *FileController { return &FileController{Base: b} }
|
||||
|
||||
// Upload 上传文件
|
||||
func (ctl *FileController) Upload(c *gin.Context) {
|
||||
fh, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
utils.FailMsg(c, "缺少上传文件字段file")
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
f, err := ctl.svc.File.Upload(c, userID, fh)
|
||||
if err != nil {
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "upload", "file", 0, fh.Filename, false, err.Error())
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "upload", "file", f.ID, f.Filename, true, "")
|
||||
utils.OK(c, f)
|
||||
}
|
||||
|
||||
// List 文件列表(支持project_id/filename/prefix/trash过滤)
|
||||
func (ctl *FileController) List(c *gin.Context) {
|
||||
userID := middleware.CurrentUserID(c)
|
||||
page, pageSize := utils.ParsePage(c)
|
||||
filter := repository.FileListFilter{
|
||||
ProjectID: parseUintQuery(c, "project_id"),
|
||||
Filename: c.Query("filename"),
|
||||
Prefix: utils.SanitizePath(c.Query("prefix")),
|
||||
Trashed: c.Query("trash") == "true",
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}
|
||||
if v := parseUintQuery(c, "visibility"); v > 0 {
|
||||
filter.Visibility = int8(v)
|
||||
}
|
||||
files, total, err := ctl.svc.File.List(userID, middleware.IsAdmin(c), filter)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OKPage(c, files, total, page, pageSize)
|
||||
}
|
||||
|
||||
// GetInfo 获取文件信息
|
||||
func (ctl *FileController) GetInfo(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
f, err := ctl.svc.File.GetInfo(userID, id, middleware.IsAdmin(c))
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, f)
|
||||
}
|
||||
|
||||
// Download 下载文件
|
||||
func (ctl *FileController) Download(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
f, fp, err := ctl.svc.File.Open(c, userID, id, middleware.IsAdmin(c))
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
ctl.svc.File.RecordDownload(c, f)
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "download", "file", f.ID, f.Filename, true, "")
|
||||
|
||||
c.Header("Content-Disposition",
|
||||
fmt.Sprintf(`attachment; filename*=UTF-8''%s`, url.PathEscape(filepath.Base(f.Filename))))
|
||||
c.Header("Content-Type", f.MimeType)
|
||||
http.ServeContent(c.Writer, c.Request, filepath.Base(f.Filename), f.UpdatedAt, fp)
|
||||
}
|
||||
|
||||
// Delete 删除文件(permanent=true硬删除,否则进入回收站)
|
||||
func (ctl *FileController) Delete(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
permanent := c.Query("permanent") == "true"
|
||||
f, _ := ctl.svc.File.GetInfo(userID, id, middleware.IsAdmin(c))
|
||||
if err := ctl.svc.File.Delete(userID, id, middleware.IsAdmin(c), permanent); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
name := ""
|
||||
if f != nil {
|
||||
name = f.Filename
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "file", id, name, true, "")
|
||||
if permanent {
|
||||
utils.OKMsg(c, "文件已彻底删除")
|
||||
} else {
|
||||
utils.OKMsg(c, "文件已移入回收站")
|
||||
}
|
||||
}
|
||||
|
||||
// Restore 恢复回收站文件
|
||||
func (ctl *FileController) Restore(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
if err := ctl.svc.File.Restore(userID, id, middleware.IsAdmin(c)); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "file", id, "restore", true, "")
|
||||
utils.OKMsg(c, "文件已恢复")
|
||||
}
|
||||
|
||||
type tempLinkReq struct {
|
||||
Password string `json:"password"`
|
||||
MaxCount int `json:"max_count"`
|
||||
ExpiresIn int64 `json:"expires_in"` // 秒,0使用默认
|
||||
}
|
||||
|
||||
// CreateTempLink 生成临时访问链接
|
||||
func (ctl *FileController) CreateTempLink(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req tempLinkReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
link, err := ctl.svc.File.CreateTempLink(userID, id, middleware.IsAdmin(c), req.Password, req.MaxCount, req.ExpiresIn)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "file", id, "temp_link", true, "")
|
||||
utils.OK(c, gin.H{
|
||||
"token": link.Token,
|
||||
"url": "/api/v1/files/temp/" + link.Token,
|
||||
"expires_at": link.ExpiresAt,
|
||||
"has_password": link.HasPwd,
|
||||
"max_count": link.MaxCount,
|
||||
})
|
||||
}
|
||||
|
||||
// TempAccess 通过临时链接访问文件(公开接口,支持password参数)
|
||||
func (ctl *FileController) TempAccess(c *gin.Context) {
|
||||
token := c.Param("token")
|
||||
password := c.Query("password")
|
||||
if password == "" {
|
||||
password = c.GetHeader("X-Link-Password")
|
||||
}
|
||||
f, fp, err := ctl.svc.File.ResolveTempLink(token, password)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
ctl.svc.File.RecordDownload(c, f)
|
||||
c.Header("Content-Disposition",
|
||||
fmt.Sprintf(`inline; filename*=UTF-8''%s`, url.PathEscape(filepath.Base(f.Filename))))
|
||||
c.Header("Content-Type", f.MimeType)
|
||||
http.ServeContent(c.Writer, c.Request, filepath.Base(f.Filename), f.UpdatedAt, fp)
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/middleware"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// ProjectController 项目控制器
|
||||
type ProjectController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewProjectController 创建项目控制器
|
||||
func NewProjectController(b *Base) *ProjectController { return &ProjectController{Base: b} }
|
||||
|
||||
type createProjectReq struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
StorageLimit int64 `json:"storage_limit"` // 0使用默认
|
||||
}
|
||||
|
||||
// Create 创建项目
|
||||
func (ctl *ProjectController) Create(c *gin.Context) {
|
||||
var req createProjectReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
p, err := ctl.svc.Project.Create(userID, req.Name, req.Description, req.StorageLimit)
|
||||
if err != nil {
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "project", 0, req.Name, false, err.Error())
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "project", p.ID, p.Name, true, "")
|
||||
utils.OK(c, p)
|
||||
}
|
||||
|
||||
// List 项目列表
|
||||
func (ctl *ProjectController) List(c *gin.Context) {
|
||||
userID := middleware.CurrentUserID(c)
|
||||
page, pageSize := utils.ParsePage(c)
|
||||
all := c.Query("all") == "true" && middleware.IsAdmin(c)
|
||||
projects, total, err := ctl.svc.Project.List(userID, c.Query("keyword"), all, page, pageSize)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OKPage(c, projects, total, page, pageSize)
|
||||
}
|
||||
|
||||
// Get 项目详情
|
||||
func (ctl *ProjectController) Get(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
p, err := ctl.svc.Project.Get(userID, id, middleware.IsAdmin(c))
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, p)
|
||||
}
|
||||
|
||||
type updateProjectReq struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
StorageLimit int64 `json:"storage_limit"`
|
||||
}
|
||||
|
||||
// Update 更新项目
|
||||
func (ctl *ProjectController) Update(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req updateProjectReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
p, err := ctl.svc.Project.Update(userID, id, middleware.IsAdmin(c), req.Name, req.Description, req.StorageLimit)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "project", id, p.Name, true, "")
|
||||
utils.OK(c, p)
|
||||
}
|
||||
|
||||
// Delete 删除项目
|
||||
func (ctl *ProjectController) Delete(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
p, _ := ctl.svc.Project.Get(userID, id, middleware.IsAdmin(c))
|
||||
if err := ctl.svc.Project.Delete(userID, id, middleware.IsAdmin(c)); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
name := ""
|
||||
if p != nil {
|
||||
name = p.Name
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "project", id, name, true, "")
|
||||
utils.OKMsg(c, "项目已删除")
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// RoleController 角色权限控制器
|
||||
type RoleController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewRoleController 创建角色控制器
|
||||
func NewRoleController(b *Base) *RoleController { return &RoleController{Base: b} }
|
||||
|
||||
// ListRoles 角色列表
|
||||
func (ctl *RoleController) ListRoles(c *gin.Context) {
|
||||
roles, err := ctl.svc.Role.ListRoles()
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, roles)
|
||||
}
|
||||
|
||||
// GetRole 角色详情
|
||||
func (ctl *RoleController) GetRole(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
role, err := ctl.svc.Role.GetRole(id)
|
||||
if err != nil {
|
||||
utils.NotFound(c, "角色不存在")
|
||||
return
|
||||
}
|
||||
utils.OK(c, role)
|
||||
}
|
||||
|
||||
type createRoleReq struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Code string `json:"code" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Permissions []uint `json:"permissions"`
|
||||
}
|
||||
|
||||
// CreateRole 创建角色
|
||||
func (ctl *RoleController) CreateRole(c *gin.Context) {
|
||||
var req createRoleReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
role, err := ctl.svc.Role.CreateRole(req.Name, req.Code, req.Description, req.Permissions)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "role", role.ID, role.Name, true, "")
|
||||
utils.OK(c, role)
|
||||
}
|
||||
|
||||
type updateRoleReq struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Permissions []uint `json:"permissions"`
|
||||
}
|
||||
|
||||
// UpdateRole 编辑角色
|
||||
func (ctl *RoleController) UpdateRole(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req updateRoleReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
role, err := ctl.svc.Role.UpdateRole(id, req.Name, req.Description, req.Permissions)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "role", id, role.Name, true, "")
|
||||
utils.OK(c, role)
|
||||
}
|
||||
|
||||
// DeleteRole 删除角色
|
||||
func (ctl *RoleController) DeleteRole(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.Role.DeleteRole(id); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "role", id, "", true, "")
|
||||
utils.OKMsg(c, "角色已删除")
|
||||
}
|
||||
|
||||
type setPermissionsReq struct {
|
||||
Permissions []uint `json:"permissions"`
|
||||
}
|
||||
|
||||
// SetPermissions 配置角色权限
|
||||
func (ctl *RoleController) SetPermissions(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req setPermissionsReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := ctl.svc.Role.SetPermissions(id, req.Permissions); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "role", id, "permissions", true, "")
|
||||
utils.OKMsg(c, "角色权限已更新")
|
||||
}
|
||||
|
||||
// ListPermissions 权限列表(按模块分组)
|
||||
func (ctl *RoleController) ListPermissions(c *gin.Context) {
|
||||
groups, order, err := ctl.svc.Role.PermissionsGrouped()
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
result := make([]gin.H, 0, len(order))
|
||||
for _, module := range order {
|
||||
result = append(result, gin.H{"module": module, "permissions": groups[module]})
|
||||
}
|
||||
utils.OK(c, result)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/middleware"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// StatsController 统计控制器
|
||||
type StatsController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewStatsController 创建统计控制器
|
||||
func NewStatsController(b *Base) *StatsController { return &StatsController{Base: b} }
|
||||
|
||||
// UserStats 用户存储统计
|
||||
func (ctl *StatsController) UserStats(c *gin.Context) {
|
||||
userID := middleware.CurrentUserID(c)
|
||||
stats, err := ctl.svc.Stats.UserStats(userID)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, stats)
|
||||
}
|
||||
|
||||
// ProjectStats 项目存储统计
|
||||
func (ctl *StatsController) ProjectStats(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
stats, err := ctl.svc.Stats.ProjectStats(userID, id, middleware.IsAdmin(c))
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, stats)
|
||||
}
|
||||
|
||||
// TrafficStats 流量统计
|
||||
func (ctl *StatsController) TrafficStats(c *gin.Context) {
|
||||
userID := middleware.CurrentUserID(c)
|
||||
days := parseIntQuery(c, "days", 30)
|
||||
stats, err := ctl.svc.Stats.TrafficStats(userID, days)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, stats)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/middleware"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// UserController 用户管理控制器(个人资料)
|
||||
type UserController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewUserController 创建用户控制器
|
||||
func NewUserController(b *Base) *UserController { return &UserController{Base: b} }
|
||||
|
||||
// GetProfile 获取个人资料
|
||||
func (ctl *UserController) GetProfile(c *gin.Context) {
|
||||
userID := middleware.CurrentUserID(c)
|
||||
u, err := ctl.svc.User.GetProfile(userID)
|
||||
if err != nil {
|
||||
utils.NotFound(c, "用户不存在")
|
||||
return
|
||||
}
|
||||
utils.OK(c, u)
|
||||
}
|
||||
|
||||
type updateProfileReq struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// UpdateProfile 更新个人资料
|
||||
func (ctl *UserController) UpdateProfile(c *gin.Context) {
|
||||
var req updateProfileReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
if err := ctl.svc.User.UpdateProfile(userID, req.Email); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", userID, "", true, "")
|
||||
utils.OKMsg(c, "资料已更新")
|
||||
}
|
||||
|
||||
type changePasswordReq struct {
|
||||
OldPassword string `json:"old_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required"`
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
func (ctl *UserController) ChangePassword(c *gin.Context) {
|
||||
var req changePasswordReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
if err := ctl.svc.User.ChangePassword(userID, req.OldPassword, req.NewPassword); err != nil {
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", userID, "password", false, err.Error())
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", userID, "password", true, "")
|
||||
utils.OKMsg(c, "密码已修改")
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filestoragesystem/internal/middleware"
|
||||
"filestoragesystem/internal/utils"
|
||||
)
|
||||
|
||||
// WebhookController Webhook控制器
|
||||
type WebhookController struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
// NewWebhookController 创建Webhook控制器
|
||||
func NewWebhookController(b *Base) *WebhookController { return &WebhookController{Base: b} }
|
||||
|
||||
type createWebhookReq struct {
|
||||
URL string `json:"url" binding:"required"`
|
||||
ProjectID *uint `json:"project_id"`
|
||||
Secret string `json:"secret"`
|
||||
Events string `json:"events" binding:"required"` // 如 file.upload,file.delete
|
||||
Status int8 `json:"status"`
|
||||
}
|
||||
|
||||
// Create 创建webhook
|
||||
func (ctl *WebhookController) Create(c *gin.Context) {
|
||||
var req createWebhookReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
w, err := ctl.svc.Webhook.Create(userID, req.ProjectID, req.URL, req.Secret, req.Events, req.Status)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "webhook", w.ID, w.URL, true, "")
|
||||
utils.OK(c, w)
|
||||
}
|
||||
|
||||
// List webhook列表
|
||||
func (ctl *WebhookController) List(c *gin.Context) {
|
||||
userID := middleware.CurrentUserID(c)
|
||||
hooks, err := ctl.svc.Webhook.List(userID)
|
||||
if err != nil {
|
||||
utils.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
utils.OK(c, hooks)
|
||||
}
|
||||
|
||||
type updateWebhookReq struct {
|
||||
URL string `json:"url"`
|
||||
Events string `json:"events"`
|
||||
Status int8 `json:"status"`
|
||||
}
|
||||
|
||||
// Update 更新webhook
|
||||
func (ctl *WebhookController) Update(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req updateWebhookReq
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
w, err := ctl.svc.Webhook.Update(userID, id, req.URL, req.Events, req.Status)
|
||||
if err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "webhook", id, w.URL, true, "")
|
||||
utils.OK(c, w)
|
||||
}
|
||||
|
||||
// Delete 删除webhook
|
||||
func (ctl *WebhookController) Delete(c *gin.Context) {
|
||||
id, ok := paramID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userID := middleware.CurrentUserID(c)
|
||||
if err := ctl.svc.Webhook.Delete(userID, id); err != nil {
|
||||
utils.FailMsg(c, err.Error())
|
||||
return
|
||||
}
|
||||
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "webhook", id, "", true, "")
|
||||
utils.OKMsg(c, "webhook已删除")
|
||||
}
|
||||
Reference in New Issue
Block a user