This commit is contained in:
2024-02-25 08:30:34 +08:00
commit 4947f39e74
273 changed files with 45396 additions and 0 deletions

View File

@ -0,0 +1,596 @@
package controllers
import (
// "io"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/aria2"
"github.com/cloudreve/Cloudreve/v3/pkg/cluster"
"github.com/cloudreve/Cloudreve/v3/pkg/email"
"github.com/cloudreve/Cloudreve/v3/pkg/mq"
// "github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/wopi"
"github.com/cloudreve/Cloudreve/v3/service/admin"
"github.com/gin-gonic/gin"
)
// AdminSummary 获取管理站点概况
func AdminSummary(c *gin.Context) {
var service admin.NoParamService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Summary()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminNews 获取社区新闻
// func AdminNews(c *gin.Context) {
// tag := "announcements"
// if c.Query("tag") != "" {
// tag = c.Query("tag")
// }
// r := request.NewClient()
// res := r.Request("GET", "https://forum.cloudreve.org/api/discussions?include=startUser%2ClastUser%2CstartPost%2Ctags&filter%5Bq%5D=%20tag%3A"+tag+"&sort=-startTime&page%5Blimit%5D=10", nil)
// if res.Err == nil {
// io.Copy(c.Writer, res.Response.Body)
// }
// }
// AdminChangeSetting 获取站点设定项
func AdminChangeSetting(c *gin.Context) {
var service admin.BatchSettingChangeService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Change()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminGetSetting 获取站点设置
func AdminGetSetting(c *gin.Context) {
var service admin.BatchSettingGet
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Get()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminGetGroups 获取用户组列表
func AdminGetGroups(c *gin.Context) {
var service admin.NoParamService
if err := c.ShouldBindUri(&service); err == nil {
res := service.GroupList()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminReloadService 重新加载子服务
func AdminReloadService(c *gin.Context) {
service := c.Param("service")
switch service {
case "email":
email.Init()
case "aria2":
aria2.Init(true, cluster.Default, mq.GlobalMQ)
case "wopi":
wopi.Init()
}
c.JSON(200, serializer.Response{})
}
// AdminSendTestMail 发送测试邮件
func AdminSendTestMail(c *gin.Context) {
var service admin.MailTestService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Send()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminTestThumbGenerator Tests thumb generator
func AdminTestThumbGenerator(c *gin.Context) {
var service admin.ThumbGeneratorTestService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Test(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListRedeems 列出激活码
func AdminListRedeems(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Redeems()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminGenerateRedeems 生成激活码
func AdminGenerateRedeems(c *gin.Context) {
var service admin.GenerateRedeemsService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Generate()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteRedeem 删除激活码
func AdminDeleteRedeem(c *gin.Context) {
var service admin.SingleIDService
if err := c.ShouldBindUri(&service); err == nil {
res := service.DeleteRedeem()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminTestAria2 测试aria2连接
func AdminTestAria2(c *gin.Context) {
var service admin.Aria2TestService
if err := c.ShouldBindJSON(&service); err == nil {
var res serializer.Response
if service.Type == model.MasterNodeType {
res = service.TestMaster()
} else {
res = service.TestSlave()
}
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListPolicy 列出存储策略
func AdminListPolicy(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Policies()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminTestPath 测试本地路径可用性
func AdminTestPath(c *gin.Context) {
var service admin.PathTestService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Test()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminTestSlave 测试从机可用性
func AdminTestSlave(c *gin.Context) {
var service admin.SlaveTestService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Test()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminAddPolicy 新建存储策略
func AdminAddPolicy(c *gin.Context) {
var service admin.AddPolicyService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Add()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminAddCORS 创建跨域策略
func AdminAddCORS(c *gin.Context) {
var service admin.PolicyService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.AddCORS()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminAddSCF 创建回调函数
func AdminAddSCF(c *gin.Context) {
var service admin.PolicyService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.AddSCF()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminOAuthURL 获取 OneDrive OAuth URL
func AdminOAuthURL(policyType string) gin.HandlerFunc {
return func(c *gin.Context) {
var service admin.PolicyService
if err := c.ShouldBindUri(&service); err == nil {
res := service.GetOAuth(c, policyType)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
}
// AdminGetPolicy 获取存储策略详情
func AdminGetPolicy(c *gin.Context) {
var service admin.PolicyService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Get()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeletePolicy 删除存储策略
func AdminDeletePolicy(c *gin.Context) {
var service admin.PolicyService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListGroup 列出用户组
func AdminListGroup(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Groups()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminAddGroup 新建用户组
func AdminAddGroup(c *gin.Context) {
var service admin.AddGroupService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Add()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteGroup 删除用户组
func AdminDeleteGroup(c *gin.Context) {
var service admin.GroupService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminGetGroup 获取用户组详情
func AdminGetGroup(c *gin.Context) {
var service admin.GroupService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Get()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListUser 列出用户
func AdminListUser(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Users()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminAddUser 新建用户组
func AdminAddUser(c *gin.Context) {
var service admin.AddUserService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Add()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminGetUser 获取用户详情
func AdminGetUser(c *gin.Context) {
var service admin.UserService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Get()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteUser 批量删除用户
func AdminDeleteUser(c *gin.Context) {
var service admin.UserBatchService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminBanUser 封禁/解封用户
func AdminBanUser(c *gin.Context) {
var service admin.UserService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Ban()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListFile 列出文件
func AdminListFile(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Files()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminGetFile 获取文件
func AdminGetFile(c *gin.Context) {
var service admin.FileService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Get(c)
// 是否需要重定向
if res.Code == -301 {
c.Redirect(302, res.Data.(string))
return
}
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteFile 批量删除文件
func AdminDeleteFile(c *gin.Context) {
var service admin.FileBatchService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListShare 列出分享
func AdminListShare(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Shares()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteShare 批量删除分享
func AdminDeleteShare(c *gin.Context) {
var service admin.ShareBatchService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListOrder 列出订单
func AdminListOrder(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Orders()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteOrder 批量删除订单
func AdminDeleteOrder(c *gin.Context) {
var service admin.OrderBatchService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListDownload 列出离线下载任务
func AdminListDownload(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Downloads()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteDownload 批量删除任务
func AdminDeleteDownload(c *gin.Context) {
var service admin.TaskBatchService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListTask 列出任务
func AdminListTask(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Tasks()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteTask 批量删除任务
func AdminDeleteTask(c *gin.Context) {
var service admin.TaskBatchService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.DeleteGeneral(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminCreateImportTask 新建文件导入任务
func AdminCreateImportTask(c *gin.Context) {
var service admin.ImportTaskService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListFolders 列出用户或外部文件系统目录
func AdminListFolders(c *gin.Context) {
var service admin.ListFolderService
if err := c.ShouldBindUri(&service); err == nil {
res := service.List(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListReport 列出未处理举报
func AdminListReport(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Reports()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteTask 批量删除举报
func AdminDeleteReport(c *gin.Context) {
var service admin.ReportBatchService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminListNodes 列出从机节点
func AdminListNodes(c *gin.Context) {
var service admin.AdminListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Nodes()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminAddNode 新建节点
func AdminAddNode(c *gin.Context) {
var service admin.AddNodeService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Add()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminToggleNode 启用/暂停节点
func AdminToggleNode(c *gin.Context) {
var service admin.ToggleNodeService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Toggle()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminDeleteGroup 删除用户组
func AdminDeleteNode(c *gin.Context) {
var service admin.NodeService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminGetNode 获取节点详情
func AdminGetNode(c *gin.Context) {
var service admin.NodeService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Get()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AdminSyncVol 同步VOL授权
func AdminSyncVol(c *gin.Context) {
var service admin.VolService
res := service.Sync()
c.JSON(200, res)
}

View File

@ -0,0 +1,97 @@
package controllers
import (
"context"
"github.com/cloudreve/Cloudreve/v3/pkg/aria2/common"
"github.com/cloudreve/Cloudreve/v3/service/aria2"
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/gin-gonic/gin"
)
// AddAria2URL 添加离线下载URL
func AddAria2URL(c *gin.Context) {
var addService aria2.BatchAddURLService
if err := c.ShouldBindJSON(&addService); err == nil {
res := addService.Add(c, common.URLTask)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SelectAria2File 选择多文件离线下载中要下载的文件
func SelectAria2File(c *gin.Context) {
var selectService aria2.SelectFileService
if err := c.ShouldBindJSON(&selectService); err == nil {
res := selectService.Select(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AddAria2Torrent 添加离线下载种子
func AddAria2Torrent(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileIDService
if err := c.ShouldBindUri(&service); err == nil {
// 获取种子内容的下载地址
res := service.CreateDownloadSession(ctx, c)
if res.Code != 0 {
c.JSON(200, res)
return
}
// 创建下载任务
var addService aria2.AddURLService
addService.URL = res.Data.(string)
if err := c.ShouldBindJSON(&addService); err == nil {
addService.URL = res.Data.(string)
res := addService.Add(c, nil, common.URLTask)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// CancelAria2Download 取消或删除aria2离线下载任务
func CancelAria2Download(c *gin.Context) {
var selectService aria2.DownloadTaskService
if err := c.ShouldBindUri(&selectService); err == nil {
res := selectService.Delete(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// ListDownloading 获取正在下载中的任务
func ListDownloading(c *gin.Context) {
var service aria2.DownloadListService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.Downloading(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// ListFinished 获取已完成的任务
func ListFinished(c *gin.Context) {
var service aria2.DownloadListService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.Finished(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

View File

@ -0,0 +1,140 @@
package controllers
import (
model "github.com/cloudreve/Cloudreve/v3/models"
"path"
"strconv"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/cloudreve/Cloudreve/v3/service/callback"
"github.com/gin-gonic/gin"
)
// RemoteCallback 远程上传回调
func RemoteCallback(c *gin.Context) {
var callbackBody callback.RemoteUploadCallbackService
if err := c.ShouldBindJSON(&callbackBody); err == nil {
res := callback.ProcessCallback(callbackBody, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// QiniuCallback 七牛上传回调
func QiniuCallback(c *gin.Context) {
var callbackBody callback.UploadCallbackService
if err := c.ShouldBindJSON(&callbackBody); err == nil {
res := callback.ProcessCallback(callbackBody, c)
if res.Code != 0 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: res.Msg})
} else {
c.JSON(200, res)
}
} else {
c.JSON(401, ErrorResponse(err))
}
}
// OSSCallback 阿里云OSS上传回调
func OSSCallback(c *gin.Context) {
var callbackBody callback.UploadCallbackService
if err := c.ShouldBindJSON(&callbackBody); err == nil {
if callbackBody.PicInfo == "," {
callbackBody.PicInfo = ""
}
res := callback.ProcessCallback(callbackBody, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UpyunCallback 又拍云上传回调
func UpyunCallback(c *gin.Context) {
var callbackBody callback.UpyunCallbackService
if err := c.ShouldBind(&callbackBody); err == nil {
if callbackBody.Code != 200 {
util.Log().Debug(
"Upload callback returned error code:%d, message: %s",
callbackBody.Code,
callbackBody.Message,
)
return
}
res := callback.ProcessCallback(callbackBody, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// OneDriveCallback OneDrive上传完成客户端回调
func OneDriveCallback(c *gin.Context) {
var callbackBody callback.OneDriveCallback
if err := c.ShouldBindJSON(&callbackBody); err == nil {
res := callbackBody.PreProcess(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// OneDriveOAuth OneDrive 授权回调
func OneDriveOAuth(c *gin.Context) {
var callbackBody callback.OauthService
if err := c.ShouldBindQuery(&callbackBody); err == nil {
res := callbackBody.OdAuth(c)
redirect := model.GetSiteURL()
redirect.Path = path.Join(redirect.Path, "/admin/policy")
queries := redirect.Query()
queries.Add("code", strconv.Itoa(res.Code))
queries.Add("msg", res.Msg)
queries.Add("err", res.Error)
redirect.RawQuery = queries.Encode()
c.Redirect(303, redirect.String())
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GoogleDriveOAuth Google Drive 授权回调
func GoogleDriveOAuth(c *gin.Context) {
var callbackBody callback.OauthService
if err := c.ShouldBindQuery(&callbackBody); err == nil {
res := callbackBody.GDriveAuth(c)
redirect := model.GetSiteURL()
redirect.Path = path.Join(redirect.Path, "/admin/policy")
queries := redirect.Query()
queries.Add("code", strconv.Itoa(res.Code))
queries.Add("msg", res.Msg)
queries.Add("err", res.Error)
redirect.RawQuery = queries.Encode()
c.Redirect(303, redirect.String())
} else {
c.JSON(200, ErrorResponse(err))
}
}
// COSCallback COS上传完成客户端回调
func COSCallback(c *gin.Context) {
var callbackBody callback.COSCallback
if err := c.ShouldBindQuery(&callbackBody); err == nil {
res := callbackBody.PreProcess(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// S3Callback S3上传完成客户端回调
func S3Callback(c *gin.Context) {
var callbackBody callback.S3Callback
if err := c.ShouldBindQuery(&callbackBody); err == nil {
res := callbackBody.PreProcess(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

View File

@ -0,0 +1,28 @@
package controllers
import (
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/gin-gonic/gin"
)
// CreateDirectory 创建目录
func CreateDirectory(c *gin.Context) {
var service explorer.DirectoryService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.CreateDirectory(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// ListDirectory 列出目录下内容
func ListDirectory(c *gin.Context) {
var service explorer.DirectoryService
if err := c.ShouldBindUri(&service); err == nil {
res := service.ListDirectory(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

387
routers/controllers/file.go Normal file
View File

@ -0,0 +1,387 @@
package controllers
import (
"context"
"fmt"
"net/http"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/gin-gonic/gin"
)
func DownloadArchive(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ArchiveService
if err := c.ShouldBindUri(&service); err == nil {
service.DownloadArchived(ctx, c)
} else {
c.JSON(200, ErrorResponse(err))
}
}
func Archive(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ItemIDService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Archive(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Compress 创建文件压缩任务
func Compress(c *gin.Context) {
var service explorer.ItemCompressService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.CreateCompressTask(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Relocate 创建文件转移任务
func Relocate(c *gin.Context) {
var service explorer.ItemRelocateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.CreateRelocateTask(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Decompress 创建文件解压缩任务
func Decompress(c *gin.Context) {
var service explorer.ItemDecompressService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.CreateDecompressTask(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AnonymousGetContent 匿名获取文件资源
func AnonymousGetContent(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileAnonymousGetService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Download(ctx, c)
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AnonymousPermLink Deprecated 文件签名后的永久链接
func AnonymousPermLinkDeprecated(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileAnonymousGetService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Source(ctx, c)
// 是否需要重定向
if res.Code == -302 {
c.Redirect(302, res.Data.(string))
return
}
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AnonymousPermLink 文件中转后的永久直链接
func AnonymousPermLink(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sourceLinkRaw, ok := c.Get("source_link")
if !ok {
c.JSON(200, serializer.Err(serializer.CodeFileNotFound, "", nil))
return
}
sourceLink := sourceLinkRaw.(*model.SourceLink)
service := &explorer.FileAnonymousGetService{
ID: sourceLink.FileID,
Name: sourceLink.File.Name,
}
res := service.Source(ctx, c)
// 是否需要重定向
if res.Code == -302 {
c.Redirect(302, res.Data.(string))
return
}
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
}
// GetSource 获取文件的外链地址
func GetSource(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ItemIDService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Sources(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Thumb 获取文件缩略图
func Thumb(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fs, err := filesystem.NewFileSystemFromContext(c)
if err != nil {
c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err))
return
}
defer fs.Recycle()
// 获取文件ID
fileID, ok := c.Get("object_id")
if !ok {
c.JSON(200, serializer.Err(serializer.CodeFileNotFound, "", err))
return
}
// 获取缩略图
resp, err := fs.GetThumb(ctx, fileID.(uint))
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeNotSet, "Failed to get thumbnail", err))
return
}
if resp.Redirect {
c.Header("Cache-Control", fmt.Sprintf("max-age=%d", resp.MaxAge))
c.Redirect(http.StatusMovedPermanently, resp.URL)
return
}
defer resp.Content.Close()
http.ServeContent(c.Writer, c.Request, "thumb."+model.GetSettingByNameWithDefault("thumb_encode_method", "jpg"), fs.FileTarget[0].UpdatedAt, resp.Content)
}
// Preview 预览文件
func Preview(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileIDService
if err := c.ShouldBindUri(&service); err == nil {
res := service.PreviewContent(ctx, c, false)
// 是否需要重定向
if res.Code == -301 {
c.Redirect(302, res.Data.(string))
return
}
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// PreviewText 预览文本文件
func PreviewText(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileIDService
if err := c.ShouldBindUri(&service); err == nil {
res := service.PreviewContent(ctx, c, true)
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetDocPreview 获取DOC文件预览地址
func GetDocPreview(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileIDService
if err := c.ShouldBindUri(&service); err == nil {
res := service.CreateDocPreviewSession(ctx, c, true)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// CreateDownloadSession 创建文件下载会话
func CreateDownloadSession(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileIDService
if err := c.ShouldBindUri(&service); err == nil {
res := service.CreateDownloadSession(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Download 文件下载
func Download(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.DownloadService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Download(ctx, c)
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// PutContent 更新文件内容
func PutContent(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.FileIDService
if err := c.ShouldBindUri(&service); err == nil {
res := service.PutContent(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// FileUpload 本地策略文件上传
func FileUpload(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.UploadService
if err := c.ShouldBindUri(&service); err == nil {
res := service.LocalUpload(ctx, c)
c.JSON(200, res)
request.BlackHole(c.Request.Body)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DeleteUploadSession 删除上传会话
func DeleteUploadSession(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.UploadSessionService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DeleteAllUploadSession 删除全部上传会话
func DeleteAllUploadSession(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
res := explorer.DeleteAllUploadSession(ctx, c)
c.JSON(200, res)
}
// GetUploadSession 创建上传会话
func GetUploadSession(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.CreateUploadSessionService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SearchFile 搜索文件
func SearchFile(c *gin.Context) {
var service explorer.ItemSearchService
if err := c.ShouldBindUri(&service); err != nil {
c.JSON(200, ErrorResponse(err))
return
}
if err := c.ShouldBindQuery(&service); err != nil {
c.JSON(200, ErrorResponse(err))
return
}
res := service.Search(c)
c.JSON(200, res)
}
// CreateFile 创建空白文件
func CreateFile(c *gin.Context) {
var service explorer.SingleFileService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

View File

@ -0,0 +1,69 @@
package controllers
import (
"encoding/json"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
// ParamErrorMsg 根据Validator返回的错误信息给出错误提示
func ParamErrorMsg(filed string, tag string) string {
// 未通过验证的表单域与中文对应
fieldMap := map[string]string{
"UserName": "Email",
"Password": "Password",
"Path": "Path",
"SourceID": "Source resource",
"URL": "URL",
"Nick": "Nickname",
}
// 未通过的规则与中文对应
tagMap := map[string]string{
"required": "cannot be empty",
"min": "too short",
"max": "too long",
"email": "format error",
}
fieldVal, findField := fieldMap[filed]
if !findField {
fieldVal = filed
}
tagVal, findTag := tagMap[tag]
if findTag {
// 返回拼接出来的错误信息
return fieldVal + " " + tagVal
}
return ""
}
// ErrorResponse 返回错误消息
func ErrorResponse(err error) serializer.Response {
// 处理 Validator 产生的错误
if ve, ok := err.(validator.ValidationErrors); ok {
for _, e := range ve {
return serializer.ParamErr(
ParamErrorMsg(e.Field(), e.Tag()),
err,
)
}
}
if _, ok := err.(*json.UnmarshalTypeError); ok {
return serializer.ParamErr("JSON marshall error", err)
}
return serializer.ParamErr("Parameter error", err)
}
// CurrentUser 获取当前用户
func CurrentUser(c *gin.Context) *model.User {
if user, _ := c.Get("user"); user != nil {
if u, ok := user.(*model.User); ok {
return u
}
}
return nil
}

View File

@ -0,0 +1,84 @@
package controllers
import (
"context"
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/gin-gonic/gin"
)
// Delete 删除文件或目录
func Delete(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ItemIDService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Move 移动文件或目录
func Move(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ItemMoveService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Move(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Copy 复制文件或目录
func Copy(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ItemMoveService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Copy(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Rename 重命名文件或目录
func Rename(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ItemRenameService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Rename(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// Rename 重命名文件或目录
func GetProperty(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.ItemPropertyService
service.ID = c.Param("id")
if err := c.ShouldBindQuery(&service); err == nil {
res := service.GetProperty(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

View File

@ -0,0 +1,259 @@
package controllers
import (
"context"
"path"
"strings"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/cloudreve/Cloudreve/v3/service/share"
"github.com/gin-gonic/gin"
)
// CreateShare 创建分享
func CreateShare(c *gin.Context) {
var service share.ShareCreateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetShare 查看分享
func GetShare(c *gin.Context) {
var service share.ShareGetService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.Get(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// ListShare 列出分享
func ListShare(c *gin.Context) {
var service share.ShareListService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.List(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SearchShare 搜索分享
func SearchShare(c *gin.Context) {
var service share.ShareListService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.Search(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UpdateShare 更新分享属性
func UpdateShare(c *gin.Context) {
var service share.ShareUpdateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Update(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DeleteShare 删除分享
func DeleteShare(c *gin.Context) {
var service share.Service
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetShareDownload 创建分享下载会话
func GetShareDownload(c *gin.Context) {
var service share.Service
if err := c.ShouldBindQuery(&service); err == nil {
res := service.CreateDownloadSession(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// PreviewShare 预览分享文件内容
func PreviewShare(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service share.Service
if err := c.ShouldBindQuery(&service); err == nil {
res := service.PreviewContent(ctx, c, false)
// 是否需要重定向
if res.Code == -301 {
c.Redirect(302, res.Data.(string))
return
}
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// PreviewShareText 预览文本文件
func PreviewShareText(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service share.Service
if err := c.ShouldBindQuery(&service); err == nil {
res := service.PreviewContent(ctx, c, true)
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// PreviewShareReadme 预览文本自述文件
func PreviewShareReadme(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service share.Service
if err := c.ShouldBindQuery(&service); err == nil {
// 自述文件名限制
allowFileName := []string{"readme.txt", "readme.md"}
fileName := strings.ToLower(path.Base(service.Path))
if !util.ContainsString(allowFileName, fileName) {
c.JSON(200, serializer.ParamErr("Not a README file", nil))
}
// 必须是目录分享
if shareCtx, ok := c.Get("share"); ok {
if !shareCtx.(*model.Share).IsDir {
c.JSON(200, serializer.ParamErr("This share has no README file", nil))
}
}
res := service.PreviewContent(ctx, c, true)
// 是否有错误发生
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetShareDocPreview 创建分享Office文档预览地址
func GetShareDocPreview(c *gin.Context) {
var service share.Service
if err := c.ShouldBindQuery(&service); err == nil {
res := service.CreateDocPreviewSession(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SaveShare 转存他人分享
func SaveShare(c *gin.Context) {
var service share.Service
if err := c.ShouldBindJSON(&service); err == nil {
res := service.SaveToMyFile(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// ListSharedFolder 列出分享的目录下的对象
func ListSharedFolder(c *gin.Context) {
var service share.Service
if err := c.ShouldBindUri(&service); err == nil {
res := service.List(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SearchSharedFolder 搜索分享的目录下的对象
func SearchSharedFolder(c *gin.Context) {
var service share.SearchService
if err := c.ShouldBindUri(&service); err != nil {
c.JSON(200, ErrorResponse(err))
return
}
if err := c.ShouldBindQuery(&service); err != nil {
c.JSON(200, ErrorResponse(err))
return
}
res := service.Search(c)
c.JSON(200, res)
}
// ArchiveShare 打包要下载的分享
func ArchiveShare(c *gin.Context) {
var service share.ArchiveService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Archive(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// ShareThumb 获取分享目录下文件的缩略图
func ShareThumb(c *gin.Context) {
var service share.Service
if err := c.ShouldBindQuery(&service); err == nil {
res := service.Thumb(c)
if res.Code >= 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetUserShare 查看给定用户的分享
func GetUserShare(c *gin.Context) {
var service share.ShareUserGetService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.Get(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// ReportShare 举报分享
func ReportShare(c *gin.Context) {
var service share.ShareReportService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Report(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

167
routers/controllers/site.go Normal file
View File

@ -0,0 +1,167 @@
package controllers
import (
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/cloudreve/Cloudreve/v3/pkg/wopi"
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
)
// SiteConfig 获取站点全局配置
func SiteConfig(c *gin.Context) {
siteConfig := model.GetSettingByNames(
"siteName",
"siteNotice",
"login_captcha",
"qq_login",
"reg_captcha",
"email_active",
"forget_captcha",
// "email_active",
"themes",
"defaultTheme",
"score_enabled",
"share_score_rate",
"home_view_method",
"share_view_method",
"authn_enabled",
"captcha_ReCaptchaKey",
"captcha_type",
"captcha_TCaptcha_CaptchaAppId",
"register_enabled",
"report_enabled",
"show_app_promotion",
"app_forum_link",
"app_feedback_link",
)
var wopiExts []string
if wopi.Default != nil {
wopiExts = wopi.Default.AvailableExts()
}
// 如果已登录,则同时返回用户信息和标签
user, _ := c.Get("user")
if user, ok := user.(*model.User); ok {
c.JSON(200, serializer.BuildSiteConfig(siteConfig, user, wopiExts))
return
}
c.JSON(200, serializer.BuildSiteConfig(siteConfig, nil, wopiExts))
}
// Ping 状态检查页面
func Ping(c *gin.Context) {
version := conf.BackendVersion
if conf.IsPlus == "true" {
version += "-plus"
}
c.JSON(200, serializer.Response{
Code: 0,
Data: version,
})
}
// Captcha 获取验证码
func Captcha(c *gin.Context) {
options := model.GetSettingByNames(
"captcha_IsShowHollowLine",
"captcha_IsShowNoiseDot",
"captcha_IsShowNoiseText",
"captcha_IsShowSlimeLine",
"captcha_IsShowSineLine",
)
// 验证码配置
var configD = base64Captcha.ConfigCharacter{
Height: model.GetIntSetting("captcha_height", 60),
Width: model.GetIntSetting("captcha_width", 240),
//const CaptchaModeNumber:数字,CaptchaModeAlphabet:字母,CaptchaModeArithmetic:算术,CaptchaModeNumberAlphabet:数字字母混合.
Mode: model.GetIntSetting("captcha_mode", 3),
ComplexOfNoiseText: model.GetIntSetting("captcha_ComplexOfNoiseText", 0),
ComplexOfNoiseDot: model.GetIntSetting("captcha_ComplexOfNoiseDot", 0),
IsShowHollowLine: model.IsTrueVal(options["captcha_IsShowHollowLine"]),
IsShowNoiseDot: model.IsTrueVal(options["captcha_IsShowNoiseDot"]),
IsShowNoiseText: model.IsTrueVal(options["captcha_IsShowNoiseText"]),
IsShowSlimeLine: model.IsTrueVal(options["captcha_IsShowSlimeLine"]),
IsShowSineLine: model.IsTrueVal(options["captcha_IsShowSineLine"]),
CaptchaLen: model.GetIntSetting("captcha_CaptchaLen", 6),
}
// 生成验证码
idKeyD, capD := base64Captcha.GenerateCaptcha("", configD)
// 将验证码UID存入Session以便后续验证
util.SetSession(c, map[string]interface{}{
"captchaID": idKeyD,
})
// 将验证码图像编码为Base64
base64stringD := base64Captcha.CaptchaWriteToBase64Encoding(capD)
c.JSON(200, serializer.Response{
Code: 0,
Data: base64stringD,
})
}
// Manifest 获取manifest.json
func Manifest(c *gin.Context) {
options := model.GetSettingByNames(
"siteName",
"siteTitle",
"pwa_small_icon",
"pwa_medium_icon",
"pwa_large_icon",
"pwa_display",
"pwa_theme_color",
"pwa_background_color",
)
c.JSON(200, map[string]interface{}{
"short_name": options["siteName"],
"name": options["siteTitle"],
"icons": []map[string]string{
{
"src": options["pwa_small_icon"],
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon",
},
{
"src": options["pwa_medium_icon"],
"type": "image/png",
"sizes": "192x192",
},
{
"src": options["pwa_large_icon"],
"type": "image/png",
"sizes": "512x512",
},
},
"start_url": ".",
"display": options["pwa_display"],
"theme_color": options["pwa_theme_color"],
"background_color": options["pwa_background_color"],
})
}
// GetVolSecret 获取 VOL 密钥
func GetVolSecret(c *gin.Context) {
vol := model.GetSettingByNames("vol_content", "vol_signature")
if vol["vol_signature"] == "" {
c.JSON(200, serializer.Response{
Code: serializer.CodeNotFound,
})
return
}
c.JSON(200, serializer.Response{
Data: serializer.VolResponse{
Signature: vol["vol_signature"],
Content: vol["vol_content"],
},
})
}

View File

@ -0,0 +1,246 @@
package controllers
import (
"context"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/service/admin"
"github.com/cloudreve/Cloudreve/v3/service/aria2"
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/cloudreve/Cloudreve/v3/service/node"
"github.com/gin-gonic/gin"
)
// SlaveUpload 从机文件上传
func SlaveUpload(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.UploadService
if err := c.ShouldBindUri(&service); err == nil {
res := service.SlaveUpload(ctx, c)
c.JSON(200, res)
request.BlackHole(c.Request.Body)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveGetUploadSession 从机创建上传会话
func SlaveGetUploadSession(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.SlaveCreateUploadSessionService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveDeleteUploadSession 从机删除上传会话
func SlaveDeleteUploadSession(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.UploadSessionService
if err := c.ShouldBindUri(&service); err == nil {
res := service.SlaveDelete(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveDownload 从机文件下载,此请求返回的HTTP状态码不全为200
func SlaveDownload(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.SlaveDownloadService
if err := c.ShouldBindUri(&service); err == nil {
res := service.ServeFile(ctx, c, true)
if res.Code != 0 {
c.JSON(400, res)
}
} else {
c.JSON(400, ErrorResponse(err))
}
}
// SlavePreview 从机文件预览
func SlavePreview(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.SlaveDownloadService
if err := c.ShouldBindUri(&service); err == nil {
res := service.ServeFile(ctx, c, false)
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveThumb 从机文件缩略图
func SlaveThumb(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.SlaveFileService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Thumb(ctx, c)
if res.Code != 0 {
c.JSON(200, res)
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveDelete 从机删除
func SlaveDelete(c *gin.Context) {
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var service explorer.SlaveFilesService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Delete(ctx, c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlavePing 从机测试
func SlavePing(c *gin.Context) {
var service admin.SlavePingService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Test()
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveList 从机列出文件
func SlaveList(c *gin.Context) {
var service explorer.SlaveListService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.List(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveHeartbeat 接受主机心跳包
func SlaveHeartbeat(c *gin.Context) {
var service serializer.NodePingReq
if err := c.ShouldBindJSON(&service); err == nil {
res := node.HandleMasterHeartbeat(&service)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveAria2Create 创建 Aria2 任务
func SlaveAria2Create(c *gin.Context) {
var service serializer.SlaveAria2Call
if err := c.ShouldBindJSON(&service); err == nil {
res := aria2.Add(c, &service)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveAria2Status 查询从机 Aria2 任务状态
func SlaveAria2Status(c *gin.Context) {
var service serializer.SlaveAria2Call
if err := c.ShouldBindJSON(&service); err == nil {
res := aria2.SlaveStatus(c, &service)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveCancelAria2Task 取消从机离线下载任务
func SlaveCancelAria2Task(c *gin.Context) {
var service serializer.SlaveAria2Call
if err := c.ShouldBindJSON(&service); err == nil {
res := aria2.SlaveCancel(c, &service)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveSelectTask 从机选取离线下载文件
func SlaveSelectTask(c *gin.Context) {
var service serializer.SlaveAria2Call
if err := c.ShouldBindJSON(&service); err == nil {
res := aria2.SlaveSelect(c, &service)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveCreateTransferTask 从机创建中转任务
func SlaveCreateTransferTask(c *gin.Context) {
var service serializer.SlaveTransferReq
if err := c.ShouldBindJSON(&service); err == nil {
res := explorer.CreateTransferTask(c, &service)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveNotificationPush 处理从机发送的消息推送
func SlaveNotificationPush(c *gin.Context) {
var service node.SlaveNotificationService
if err := c.ShouldBindUri(&service); err == nil {
res := service.HandleSlaveNotificationPush(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveGetOauthCredential 从机获取主机的OneDrive存储策略凭证
func SlaveGetOauthCredential(c *gin.Context) {
var service node.OauthCredentialService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Get(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SlaveSelectTask 从机删除离线下载临时文件
func SlaveDeleteTempFile(c *gin.Context) {
var service serializer.SlaveAria2Call
if err := c.ShouldBindJSON(&service); err == nil {
res := aria2.SlaveDeleteTemp(c, &service)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

View File

@ -0,0 +1,39 @@
package controllers
import (
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/gin-gonic/gin"
)
// CreateFilterTag 创建文件分类标签
func CreateFilterTag(c *gin.Context) {
var service explorer.FilterTagCreateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// CreateLinkTag 创建目录快捷方式标签
func CreateLinkTag(c *gin.Context) {
var service explorer.LinkTagCreateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DeleteTag 删除标签
func DeleteTag(c *gin.Context) {
var service explorer.TagService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

445
routers/controllers/user.go Normal file
View File

@ -0,0 +1,445 @@
package controllers
import (
"encoding/json"
"fmt"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/authn"
"github.com/cloudreve/Cloudreve/v3/pkg/qq"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/thumb"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/cloudreve/Cloudreve/v3/service/user"
"github.com/duo-labs/webauthn/webauthn"
"github.com/gin-gonic/gin"
)
// StartLoginAuthn 开始注册WebAuthn登录
func StartLoginAuthn(c *gin.Context) {
userName := c.Param("username")
expectedUser, err := model.GetActiveUserByEmail(userName)
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeUserNotFound, "", err))
return
}
instance, err := authn.NewAuthnInstance()
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeInitializeAuthn, "Cannot initialize authn", err))
return
}
options, sessionData, err := instance.BeginLogin(expectedUser)
if err != nil {
c.JSON(200, ErrorResponse(err))
return
}
val, err := json.Marshal(sessionData)
if err != nil {
c.JSON(200, ErrorResponse(err))
return
}
util.SetSession(c, map[string]interface{}{
"registration-session": val,
})
c.JSON(200, serializer.Response{Code: 0, Data: options})
}
// FinishLoginAuthn 完成注册WebAuthn登录
func FinishLoginAuthn(c *gin.Context) {
userName := c.Param("username")
expectedUser, err := model.GetActiveUserByEmail(userName)
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeUserNotFound, "", err))
return
}
sessionDataJSON := util.GetSession(c, "registration-session").([]byte)
var sessionData webauthn.SessionData
err = json.Unmarshal(sessionDataJSON, &sessionData)
instance, err := authn.NewAuthnInstance()
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeInitializeAuthn, "Cannot initialize authn", err))
return
}
_, err = instance.FinishLogin(expectedUser, sessionData, c.Request)
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeWebAuthnCredentialError, "Verification failed", err))
return
}
util.SetSession(c, map[string]interface{}{
"user_id": expectedUser.ID,
})
c.JSON(200, serializer.BuildUserResponse(expectedUser))
}
// StartRegAuthn 开始注册WebAuthn信息
func StartRegAuthn(c *gin.Context) {
currUser := CurrentUser(c)
instance, err := authn.NewAuthnInstance()
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeInitializeAuthn, "Cannot initialize authn", err))
return
}
options, sessionData, err := instance.BeginRegistration(currUser)
if err != nil {
c.JSON(200, ErrorResponse(err))
return
}
val, err := json.Marshal(sessionData)
if err != nil {
c.JSON(200, ErrorResponse(err))
return
}
util.SetSession(c, map[string]interface{}{
"registration-session": val,
})
c.JSON(200, serializer.Response{Code: 0, Data: options})
}
// FinishRegAuthn 完成注册WebAuthn信息
func FinishRegAuthn(c *gin.Context) {
currUser := CurrentUser(c)
sessionDataJSON := util.GetSession(c, "registration-session").([]byte)
var sessionData webauthn.SessionData
err := json.Unmarshal(sessionDataJSON, &sessionData)
instance, err := authn.NewAuthnInstance()
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeInitializeAuthn, "Cannot initialize authn", err))
return
}
credential, err := instance.FinishRegistration(currUser, sessionData, c.Request)
if err != nil {
c.JSON(200, ErrorResponse(err))
return
}
err = currUser.RegisterAuthn(credential)
if err != nil {
c.JSON(200, ErrorResponse(err))
return
}
c.JSON(200, serializer.Response{
Code: 0,
Data: map[string]interface{}{
"id": credential.ID,
"fingerprint": fmt.Sprintf("% X", credential.Authenticator.AAGUID),
},
})
}
// UserLogin 用户登录
func UserLogin(c *gin.Context) {
var service user.UserLoginService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Login(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserRegister 用户注册
func UserRegister(c *gin.Context) {
var service user.UserRegisterService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Register(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// User2FALogin 用户二步验证登录
func User2FALogin(c *gin.Context) {
var service user.Enable2FA
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Login(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserSendReset 发送密码重设邮件
func UserSendReset(c *gin.Context) {
var service user.UserResetEmailService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Reset(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserReset 重设密码
func UserReset(c *gin.Context) {
var service user.UserResetService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Reset(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserActivate 用户激活
func UserActivate(c *gin.Context) {
var service user.SettingService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Activate(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserQQLogin 初始化QQ登录
func UserQQLogin(c *gin.Context) {
// 新建绑定
res, err := qq.NewLoginRequest()
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeNotSet, "无法使用QQ登录", err))
return
}
// 设定QQ登录会话Secret
util.SetSession(c, map[string]interface{}{"qq_login_secret": res.SecretKey})
c.JSON(200, serializer.Response{
Data: res.URL,
})
}
// UserSignOut 用户退出登录
func UserSignOut(c *gin.Context) {
util.DeleteSession(c, "user_id")
c.JSON(200, serializer.Response{})
}
// UserMe 获取当前登录的用户
func UserMe(c *gin.Context) {
currUser := CurrentUser(c)
res := serializer.BuildUserResponse(*currUser)
c.JSON(200, res)
}
// UserStorage 获取用户的存储信息
func UserStorage(c *gin.Context) {
currUser := CurrentUser(c)
res := serializer.BuildUserStorageResponse(*currUser)
c.JSON(200, res)
}
// UserAvailablePolicies 用户存储策略设置
func UserAvailablePolicies(c *gin.Context) {
var service user.SettingService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Policy(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserAvailableNodes 用户可选节点
func UserAvailableNodes(c *gin.Context) {
var service user.SettingService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Nodes(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserTasks 获取任务队列
func UserTasks(c *gin.Context) {
var service user.SettingListService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.ListTasks(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserSetting 获取用户设定
func UserSetting(c *gin.Context) {
var service user.SettingService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Settings(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UseGravatar 设定头像使用全球通用
func UseGravatar(c *gin.Context) {
u := CurrentUser(c)
if err := u.Update(map[string]interface{}{"avatar": "gravatar"}); err != nil {
c.JSON(200, serializer.Err(serializer.CodeDBError, "无法更新头像", err))
return
}
c.JSON(200, serializer.Response{})
}
// UploadAvatar 从文件上传头像
func UploadAvatar(c *gin.Context) {
// 取得头像上传大小限制
maxSize := model.GetIntSetting("avatar_size", 2097152)
if c.Request.ContentLength == -1 || c.Request.ContentLength > int64(maxSize) {
request.BlackHole(c.Request.Body)
c.JSON(200, serializer.Err(serializer.CodeFileTooLarge, "", nil))
return
}
// 取得上传的文件
file, err := c.FormFile("avatar")
if err != nil {
c.JSON(200, serializer.ParamErr("Failed to read avatar file data", err))
return
}
// 初始化头像
r, err := file.Open()
if err != nil {
c.JSON(200, serializer.ParamErr("Failed to read avatar file data", err))
return
}
avatar, err := thumb.NewThumbFromFile(r, file.Filename)
if err != nil {
c.JSON(200, serializer.ParamErr("Invalid image", err))
return
}
// 创建头像
u := CurrentUser(c)
err = avatar.CreateAvatar(u.ID)
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeIOFailed, "Failed to create avatar file", err))
return
}
// 保存头像标记
if err := u.Update(map[string]interface{}{
"avatar": "file",
}); err != nil {
c.JSON(200, serializer.DBErr("Failed to update avatar attribute", err))
return
}
c.JSON(200, serializer.Response{})
}
// GetUserAvatar 获取用户头像
func GetUserAvatar(c *gin.Context) {
var service user.AvatarService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Get(c)
if res.Code == -301 {
// 重定向到gravatar
c.Redirect(301, res.Data.(string))
}
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UpdateOption 更改用户设定
func UpdateOption(c *gin.Context) {
var service user.SettingUpdateService
if err := c.ShouldBindUri(&service); err == nil {
var (
subService user.OptionsChangeHandler
subErr error
)
switch service.Option {
case "nick":
subService = &user.ChangerNick{}
case "vip":
subService = &user.VIPUnsubscribe{}
case "qq":
subService = &user.QQBind{}
case "policy":
subService = &user.PolicyChange{}
case "homepage":
subService = &user.HomePage{}
case "password":
subService = &user.PasswordChange{}
case "2fa":
subService = &user.Enable2FA{}
case "authn":
subService = &user.DeleteWebAuthn{}
case "theme":
subService = &user.ThemeChose{}
default:
subService = &user.ChangerNick{}
}
subErr = c.ShouldBindJSON(subService)
if subErr != nil {
c.JSON(200, ErrorResponse(subErr))
return
}
res := subService.Update(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserInit2FA 初始化二步验证
func UserInit2FA(c *gin.Context) {
var service user.SettingService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Init2FA(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserPrepareCopySession generates URL for copy session
func UserPrepareCopySession(c *gin.Context) {
var service user.CopySessionService
res := service.Prepare(c, CurrentUser(c))
c.JSON(200, res)
}
// UserPerformCopySession copy to create new session or refresh current session
func UserPerformCopySession(c *gin.Context) {
var service user.CopySessionService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Copy(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

214
routers/controllers/vas.go Normal file
View File

@ -0,0 +1,214 @@
package controllers
import (
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
"github.com/cloudreve/Cloudreve/v3/pkg/payment"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/cloudreve/Cloudreve/v3/service/vas"
"github.com/gin-gonic/gin"
"github.com/iGoogle-ink/gopay"
"github.com/iGoogle-ink/gopay/wechat/v3"
"github.com/qingwg/payjs/notify"
"github.com/smartwalle/alipay/v3"
"net/http"
)
// GetQuota 获取容量配额信息
func GetQuota(c *gin.Context) {
var service vas.GeneralVASService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Quota(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetProduct 获取商品信息
func GetProduct(c *gin.Context) {
var service vas.GeneralVASService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Products(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// NewOrder 新建支付订单
func NewOrder(c *gin.Context) {
var service vas.CreateOrderService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// OrderStatus 查询订单状态
func OrderStatus(c *gin.Context) {
var service vas.OrderService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Status(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetRedeemInfo 获取兑换码信息
func GetRedeemInfo(c *gin.Context) {
var service vas.RedeemService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Query(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DoRedeem 获取兑换码信息
func DoRedeem(c *gin.Context) {
var service vas.RedeemService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Redeem(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// AlipayCallback 支付宝回调
func AlipayCallback(c *gin.Context) {
pay, err := payment.NewPaymentInstance("alipay")
if err != nil {
util.Log().Debug("[Alipay callback] Failed to create alipay client, %s", err)
c.Status(400)
return
}
res, err := pay.(*payment.Alipay).Client.GetTradeNotification(c.Request)
if err != nil {
util.Log().Debug("[Alipay callback] Failed to validate callback request, %s", err)
c.Status(403)
return
}
if res != nil && res.TradeStatus == "TRADE_SUCCESS" {
// 支付成功
if err := payment.OrderPaid(res.OutTradeNo); err != nil {
util.Log().Debug("[Alipay callback] Failed to process payment, %s", err)
}
}
// 确认收到通知消息
alipay.AckNotification(c.Writer)
}
// WechatCallback 微信扫码支付回调
func WechatCallback(c *gin.Context) {
pay, err := payment.NewPaymentInstance("wechat")
if err != nil {
util.Log().Debug("[Wechat pay callback] Failed to create alipay client, %s", err)
c.JSON(500, &wechat.V3NotifyRsp{Code: gopay.FAIL, Message: "Failed to create alipay client"})
return
}
notifyReq, err := wechat.V3ParseNotify(c.Request)
if err != nil {
util.Log().Debug("[Wechat pay callback] Failed to parse callback content, %s", err)
c.JSON(500, &wechat.V3NotifyRsp{Code: gopay.FAIL, Message: "Failed to parse callback content"})
return
}
err = notifyReq.VerifySign(pay.(*payment.Wechat).GetPlatformCert())
if err != nil {
util.Log().Debug("[Wechat pay callback] Failed to verify callback signature, %s", err)
c.JSON(403, &wechat.V3NotifyRsp{Code: gopay.FAIL, Message: "Failed to verify callback signature"})
return
}
// 解密回调正文
result, err := notifyReq.DecryptCipherText(pay.(*payment.Wechat).ApiV3Key)
if result != nil && result.TradeState == "SUCCESS" {
// 支付成功
if err := payment.OrderPaid(result.OutTradeNo); err != nil {
util.Log().Debug("[Wechat pay callback] Failed to process payment, %s", err)
}
}
// 确认收到通知消息
c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "Success"})
}
// PayJSCallback PayJS回调
func PayJSCallback(c *gin.Context) {
pay, err := payment.NewPaymentInstance("payjs")
if err != nil {
util.Log().Debug("[PayJS callback] Failed to initialize payment client, %s", err)
c.Status(400)
return
}
payNotify := pay.(*payment.PayJSClient).Client.GetNotify(c.Request, c.Writer)
//设置接收消息的处理方法
payNotify.SetMessageHandler(func(msg notify.Message) {
if err := payment.OrderPaid(msg.OutTradeNo); err != nil {
util.Log().Debug("[PayJS callback] Failed to process payment, %s", err)
}
})
//处理消息接收以及回复
err = payNotify.Serve()
if err != nil {
util.Log().Debug("[PayJS callback] Failed to process payment, %s", err)
return
}
//发送回复的消息
payNotify.SendResponseMsg()
}
// QQCallback QQ互联回调
func QQCallback(c *gin.Context) {
var service vas.QQCallbackService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Callback(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// CustomCallback PayJS回调
func CustomCallback(c *gin.Context) {
orderNo := c.Param("orderno")
sessionID := c.Param("id")
sessionRaw, exist := cache.Get(payment.CallbackSessionPrefix + sessionID)
if !exist {
util.Log().Debug("[Custom callback] Failed to process payment, session not found")
c.JSON(200, serializer.Err(serializer.CodeNotFound, "session not found", nil))
return
}
expectedID := sessionRaw.(string)
if expectedID != orderNo {
util.Log().Debug("[Custom callback] Failed to process payment, session mismatch")
c.JSON(200, serializer.Err(serializer.CodeInternalSetting, "session mismatch", nil))
return
}
cache.Deletes([]string{sessionID}, payment.CallbackSessionPrefix)
if err := payment.OrderPaid(orderNo); err != nil {
c.JSON(200, serializer.Err(serializer.CodeInternalSetting, "failed to fulfill payment", err))
util.Log().Debug("[Custom callback] Failed to process payment, %s", err)
return
}
c.JSON(200, serializer.Response{})
}

View File

@ -0,0 +1,138 @@
package controllers
import (
"context"
"net/http"
"sync"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/cloudreve/Cloudreve/v3/pkg/webdav"
"github.com/cloudreve/Cloudreve/v3/service/setting"
"github.com/gin-gonic/gin"
)
var handler *webdav.Handler
func init() {
handler = &webdav.Handler{
Prefix: "/dav",
LockSystem: make(map[uint]webdav.LockSystem),
Mutex: &sync.Mutex{},
}
}
// ServeWebDAV 处理WebDAV相关请求
func ServeWebDAV(c *gin.Context) {
fs, err := filesystem.NewFileSystemFromContext(c)
if err != nil {
util.Log().Warning("Failed to initialize filesystem for WebDAV%s", err)
return
}
if webdavCtx, ok := c.Get("webdav"); ok {
application := webdavCtx.(*model.Webdav)
// 重定根目录
if application.Root != "/" {
if exist, root := fs.IsPathExist(application.Root); exist {
root.Position = ""
root.Name = "/"
fs.Root = root
}
}
// 检查是否只读
if application.Readonly {
switch c.Request.Method {
case "DELETE", "PUT", "MKCOL", "COPY", "MOVE":
c.Status(http.StatusForbidden)
return
}
}
// 更新Context
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), fsctx.WebDAVCtx, application))
}
handler.ServeHTTP(c.Writer, c.Request, fs)
}
// GetWebDAVAccounts 获取webdav账号列表
func GetWebDAVAccounts(c *gin.Context) {
var service setting.WebDAVListService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Accounts(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DeleteWebDAVAccounts 删除WebDAV账户
func DeleteWebDAVAccounts(c *gin.Context) {
var service setting.WebDAVAccountService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UpdateWebDAVAccounts 更改WebDAV账户只读性和是否使用代理服务
func UpdateWebDAVAccounts(c *gin.Context) {
var service setting.WebDAVAccountUpdateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Update(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DeleteWebDAVMounts 删除WebDAV挂载
func DeleteWebDAVMounts(c *gin.Context) {
var service setting.WebDAVListService
if err := c.ShouldBindUri(&service); err == nil {
res := service.Unmount(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UpdateWebDAVAccountsReadonly 更改WebDAV账户只读性
func UpdateWebDAVAccountsReadonly(c *gin.Context) {
var service setting.WebDAVAccountUpdateReadonlyService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Update(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// CreateWebDAVAccounts 创建WebDAV账户
func CreateWebDAVAccounts(c *gin.Context) {
var service setting.WebDAVAccountCreateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// CreateWebDAVMounts 创建WebDAV目录挂载
func CreateWebDAVMounts(c *gin.Context) {
var service setting.WebDAVMountCreateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Create(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}

View File

@ -0,0 +1,77 @@
package controllers
import (
"context"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/wopi"
"github.com/cloudreve/Cloudreve/v3/service/explorer"
"github.com/gin-gonic/gin"
"net/http"
)
// CheckFileInfo Get file info
func CheckFileInfo(c *gin.Context) {
var service explorer.WopiService
res, err := service.FileInfo(c)
if err != nil {
c.Status(http.StatusInternalServerError)
c.Header(wopi.ServerErrorHeader, err.Error())
return
}
c.JSON(200, res)
}
// GetFile Get file content
func GetFile(c *gin.Context) {
var service explorer.WopiService
err := service.GetFile(c)
if err != nil {
c.Status(http.StatusInternalServerError)
c.Header(wopi.ServerErrorHeader, err.Error())
return
}
}
// PutFile Puts file content
func PutFile(c *gin.Context) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
service := &explorer.FileIDService{}
res := service.PutContent(ctx, c)
switch res.Code {
case serializer.CodeFileTooLarge:
c.Status(http.StatusRequestEntityTooLarge)
c.Header(wopi.ServerErrorHeader, res.Error)
case serializer.CodeNotFound:
c.Status(http.StatusNotFound)
c.Header(wopi.ServerErrorHeader, res.Error)
case 0:
c.Status(http.StatusOK)
default:
c.Status(http.StatusInternalServerError)
c.Header(wopi.ServerErrorHeader, res.Error)
}
}
// ModifyFile Modify file properties
func ModifyFile(c *gin.Context) {
action := c.GetHeader(wopi.OverwriteHeader)
switch action {
case wopi.MethodLock, wopi.MethodRefreshLock, wopi.MethodUnlock:
c.Status(http.StatusOK)
return
case wopi.MethodRename:
var service explorer.WopiService
err := service.Rename(c)
if err != nil {
c.Status(http.StatusInternalServerError)
c.Header(wopi.ServerErrorHeader, err.Error())
return
}
default:
c.Status(http.StatusNotImplemented)
return
}
}

864
routers/router.go Normal file
View File

@ -0,0 +1,864 @@
package routers
import (
// "github.com/abslant/gzip"
"github.com/cloudreve/Cloudreve/v3/bootstrap"
"github.com/cloudreve/Cloudreve/v3/middleware"
"github.com/cloudreve/Cloudreve/v3/pkg/auth"
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
"github.com/cloudreve/Cloudreve/v3/pkg/cluster"
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
"github.com/cloudreve/Cloudreve/v3/pkg/hashid"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
wopi2 "github.com/cloudreve/Cloudreve/v3/pkg/wopi"
"github.com/cloudreve/Cloudreve/v3/routers/controllers"
"github.com/gin-contrib/cors"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
)
// InitRouter 初始化路由
func InitRouter() *gin.Engine {
if conf.SystemConfig.Mode == "master" {
util.Log().Info("Current running mode: Master.")
return InitMasterRouter()
}
util.Log().Info("Current running mode: Slave.")
return InitSlaveRouter()
}
// InitSlaveRouter 初始化从机模式路由
func InitSlaveRouter() *gin.Engine {
r := gin.Default()
// 跨域相关
InitCORS(r)
v3 := r.Group("/api/v3/slave")
// 鉴权中间件
v3.Use(middleware.SignRequired(auth.General))
// 主机信息解析
v3.Use(middleware.MasterMetadata())
// 禁止缓存
v3.Use(middleware.CacheControl())
/*
路由
*/
{
// Ping
v3.POST("ping", controllers.SlavePing)
// 测试 Aria2 RPC 连接
v3.POST("ping/aria2", controllers.AdminTestAria2)
// 接收主机心跳包
v3.POST("heartbeat", controllers.SlaveHeartbeat)
// 上传
upload := v3.Group("upload")
{
// 上传分片
upload.POST(":sessionId", controllers.SlaveUpload)
// 创建上传会话上传
upload.PUT("", controllers.SlaveGetUploadSession)
// 删除上传会话
upload.DELETE(":sessionId", controllers.SlaveDeleteUploadSession)
}
// 下载
v3.GET("download/:speed/:path/:name", controllers.SlaveDownload)
// 预览 / 外链
v3.GET("source/:speed/:path/:name", controllers.SlavePreview)
// 缩略图
v3.GET("thumb/:path/:ext", controllers.SlaveThumb)
// 删除文件
v3.POST("delete", controllers.SlaveDelete)
// 列出文件
v3.POST("list", controllers.SlaveList)
// 离线下载
aria2 := v3.Group("aria2")
aria2.Use(middleware.UseSlaveAria2Instance(cluster.DefaultController))
{
// 创建离线下载任务
aria2.POST("task", controllers.SlaveAria2Create)
// 获取任务状态
aria2.POST("status", controllers.SlaveAria2Status)
// 取消离线下载任务
aria2.POST("cancel", controllers.SlaveCancelAria2Task)
// 选取任务文件
aria2.POST("select", controllers.SlaveSelectTask)
// 删除任务临时文件
aria2.POST("delete", controllers.SlaveDeleteTempFile)
}
// 异步任务
task := v3.Group("task")
{
task.PUT("transfer", controllers.SlaveCreateTransferTask)
}
}
return r
}
// InitCORS 初始化跨域配置
func InitCORS(router *gin.Engine) {
if conf.CORSConfig.AllowOrigins[0] != "UNSET" {
router.Use(cors.New(cors.Config{
AllowOrigins: conf.CORSConfig.AllowOrigins,
AllowMethods: conf.CORSConfig.AllowMethods,
AllowHeaders: conf.CORSConfig.AllowHeaders,
AllowCredentials: conf.CORSConfig.AllowCredentials,
ExposeHeaders: conf.CORSConfig.ExposeHeaders,
}))
return
}
// slave模式下未启动跨域的警告
if conf.SystemConfig.Mode == "slave" {
util.Log().Warning("You are running Cloudreve as slave node, if you are using slave storage policy, please enable CORS feature in config file, otherwise file cannot be uploaded from Master site.")
}
}
// InitMasterRouter 初始化主机模式路由
func InitMasterRouter() *gin.Engine {
r := gin.Default()
bootstrap.InitCustomRoute(r.Group("/api/v3"))
// bootstrap.InitCustomRoute(r.Group("/custom"))
/*
静态资源
*/
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{"/api/"})))
// r.Use(gzip.GzipHandler())
r.Use(middleware.FrontendFileHandler())
r.GET("manifest.json", controllers.Manifest)
v3 := r.Group("/api/v3")
/*
中间件
*/
v3.Use(middleware.Session(conf.SystemConfig.SessionSecret))
// 跨域相关
InitCORS(r)
// 测试模式加入Mock助手中间件
if gin.Mode() == gin.TestMode {
v3.Use(middleware.MockHelper())
}
// 用户会话
v3.Use(middleware.CurrentUser())
// 禁止缓存
v3.Use(middleware.CacheControl())
/*
路由
*/
{
// Redirect file source link
source := r.Group("f")
{
source.GET(":id/:name",
middleware.HashID(hashid.SourceLinkID),
middleware.ValidateSourceLink(),
controllers.AnonymousPermLink)
}
// 全局设置相关
site := v3.Group("site")
{
// 测试用路由
site.GET("ping", controllers.Ping)
// 验证码
site.GET("captcha", controllers.Captcha)
// 站点全局配置
site.GET("config", middleware.CSRFInit(), controllers.SiteConfig)
// VOL 密钥
site.GET("vol", controllers.GetVolSecret)
}
// 用户相关路由
user := v3.Group("user")
{
// 用户登录
user.POST("session", middleware.CaptchaRequired("login_captcha"), controllers.UserLogin)
// 用户注册
user.POST("",
middleware.IsFunctionEnabled("register_enabled"),
middleware.CaptchaRequired("reg_captcha"),
controllers.UserRegister,
)
// 用二步验证户登录
user.POST("2fa", controllers.User2FALogin)
// 发送密码重设邮件
user.POST("reset", middleware.CaptchaRequired("forget_captcha"), controllers.UserSendReset)
// 通过邮件里的链接重设密码
user.PATCH("reset", controllers.UserReset)
// 邮件激活
user.GET("activate/:id",
middleware.SignRequired(auth.General),
middleware.HashID(hashid.UserID),
controllers.UserActivate,
)
// 初始化QQ登录
user.POST("qq", controllers.UserQQLogin)
// WebAuthn登陆初始化
user.GET("authn/:username",
middleware.IsFunctionEnabled("authn_enabled"),
controllers.StartLoginAuthn,
)
// WebAuthn登陆
user.POST("authn/finish/:username",
middleware.IsFunctionEnabled("authn_enabled"),
controllers.FinishLoginAuthn,
)
// 获取用户主页展示用分享
user.GET("profile/:id",
middleware.HashID(hashid.UserID),
controllers.GetUserShare,
)
// 获取用户头像
user.GET("avatar/:id/:size",
middleware.HashID(hashid.UserID),
middleware.StaticResourceCache(),
controllers.GetUserAvatar,
)
}
// 需要携带签名验证的
sign := v3.Group("")
sign.Use(middleware.SignRequired(auth.General))
{
file := sign.Group("file")
{
// 文件外链(直接输出文件数据)
file.GET("get/:id/:name",
middleware.Sandbox(),
middleware.StaticResourceCache(),
controllers.AnonymousGetContent,
)
// 文件外链(301跳转)
file.GET("source/:id/:name", controllers.AnonymousPermLinkDeprecated)
// 下载文件
file.GET("download/:id",
middleware.StaticResourceCache(),
controllers.Download,
)
// 打包并下载文件
file.GET("archive/:sessionID/archive.zip", controllers.DownloadArchive)
}
// Copy user session
sign.GET(
"user/session/copy/:id",
middleware.MobileRequestOnly(),
controllers.UserPerformCopySession,
)
}
// 从机的 RPC 通信
slave := v3.Group("slave")
slave.Use(middleware.SlaveRPCSignRequired(cluster.Default))
{
// 事件通知
slave.PUT("notification/:subject", controllers.SlaveNotificationPush)
// 上传
upload := slave.Group("upload")
{
// 上传分片
upload.POST(":sessionId", controllers.SlaveUpload)
// 创建上传会话上传
upload.PUT("", controllers.SlaveGetUploadSession)
// 删除上传会话
upload.DELETE(":sessionId", controllers.SlaveDeleteUploadSession)
}
// Oauth 存储策略凭证
slave.GET("credential/:id", controllers.SlaveGetOauthCredential)
}
// 回调接口
callback := v3.Group("callback")
{
// QQ互联回调
callback.POST(
"qq",
controllers.QQCallback,
)
// PAYJS回调
callback.POST(
"payjs",
controllers.PayJSCallback,
)
// 支付宝回调
callback.POST(
"alipay",
controllers.AlipayCallback,
)
// 微信扫码支付回调
callback.POST(
"wechat",
controllers.WechatCallback,
)
// Custom payment callback
callback.GET(
"custom/:orderno/:id",
middleware.SignRequired(auth.General),
controllers.CustomCallback,
)
// 远程策略上传回调
callback.POST(
"remote/:sessionID/:key",
middleware.UseUploadSession("remote"),
middleware.RemoteCallbackAuth(),
controllers.RemoteCallback,
)
// 七牛策略上传回调
callback.POST(
"qiniu/:sessionID",
middleware.UseUploadSession("qiniu"),
middleware.QiniuCallbackAuth(),
controllers.QiniuCallback,
)
// 阿里云OSS策略上传回调
callback.POST(
"oss/:sessionID",
middleware.UseUploadSession("oss"),
middleware.OSSCallbackAuth(),
controllers.OSSCallback,
)
// 又拍云策略上传回调
callback.POST(
"upyun/:sessionID",
middleware.UseUploadSession("upyun"),
middleware.UpyunCallbackAuth(),
controllers.UpyunCallback,
)
onedrive := callback.Group("onedrive")
{
// 文件上传完成
onedrive.POST(
"finish/:sessionID",
middleware.UseUploadSession("onedrive"),
middleware.OneDriveCallbackAuth(),
controllers.OneDriveCallback,
)
// OAuth 完成
onedrive.GET(
"auth",
controllers.OneDriveOAuth,
)
}
// Google Drive related
gdrive := callback.Group("googledrive")
{
// OAuth 完成
gdrive.GET(
"auth",
controllers.GoogleDriveOAuth,
)
}
// 腾讯云COS策略上传回调
callback.GET(
"cos/:sessionID",
middleware.UseUploadSession("cos"),
controllers.COSCallback,
)
// AWS S3策略上传回调
callback.GET(
"s3/:sessionID",
middleware.UseUploadSession("s3"),
controllers.S3Callback,
)
}
// 分享相关
share := v3.Group("share", middleware.ShareAvailable())
{
// 获取分享
share.GET("info/:id", controllers.GetShare)
// 创建文件下载会话
share.PUT("download/:id",
middleware.CheckShareUnlocked(),
middleware.BeforeShareDownload(),
controllers.GetShareDownload,
)
// 预览分享文件
share.GET("preview/:id",
middleware.CSRFCheck(),
middleware.CheckShareUnlocked(),
middleware.ShareCanPreview(),
middleware.BeforeShareDownload(),
controllers.PreviewShare,
)
// 取得Office文档预览地址
share.GET("doc/:id",
middleware.CheckShareUnlocked(),
middleware.ShareCanPreview(),
middleware.BeforeShareDownload(),
controllers.GetShareDocPreview,
)
// 获取文本文件内容
share.GET("content/:id",
middleware.CheckShareUnlocked(),
middleware.BeforeShareDownload(),
controllers.PreviewShareText,
)
// 分享目录列文件
share.GET("list/:id/*path",
middleware.CheckShareUnlocked(),
controllers.ListSharedFolder,
)
// 分享目录搜索
share.GET("search/:id/:type/:keywords",
middleware.CheckShareUnlocked(),
controllers.SearchSharedFolder,
)
// 归档打包下载
share.POST("archive/:id",
middleware.CheckShareUnlocked(),
middleware.BeforeShareDownload(),
controllers.ArchiveShare,
)
// 获取README文本文件内容
share.GET("readme/:id",
middleware.CheckShareUnlocked(),
controllers.PreviewShareReadme,
)
// 获取缩略图
share.GET("thumb/:id/:file",
middleware.CheckShareUnlocked(),
middleware.ShareCanPreview(),
controllers.ShareThumb,
)
// 举报分享
share.POST("report/:id",
middleware.IsFunctionEnabled("report_enabled"),
middleware.CheckShareUnlocked(),
controllers.ReportShare,
)
// 搜索公共分享
v3.Group("share").GET("search", middleware.AuthRequired(), controllers.SearchShare)
}
wopi := v3.Group(
"wopi",
middleware.HashID(hashid.FileID),
middleware.WopiAccessValidation(wopi2.Default, cache.Store),
)
{
// 获取文件信息
wopi.GET("files/:id", controllers.CheckFileInfo)
// 获取文件内容
wopi.GET("files/:id/contents", controllers.GetFile)
// 更新文件内容
wopi.POST("files/:id/contents", middleware.WopiWriteAccess(), controllers.PutFile)
// 通用文件操作
wopi.POST("files/:id", middleware.WopiWriteAccess(), controllers.ModifyFile)
}
// 需要登录保护的
auth := v3.Group("")
auth.Use(middleware.AuthRequired())
{
// 管理
admin := auth.Group("admin", middleware.IsAdmin())
{
// 获取站点概况
admin.GET("summary", controllers.AdminSummary)
// 获取社区新闻
// admin.GET("news", controllers.AdminNews)
// 更改设置
admin.PATCH("setting", controllers.AdminChangeSetting)
// 获取设置
admin.POST("setting", controllers.AdminGetSetting)
// 获取用户组列表
admin.GET("groups", controllers.AdminGetGroups)
// 重新加载子服务
admin.GET("reload/:service", controllers.AdminReloadService)
// 测试设置
test := admin.Group("test")
{
// 测试邮件设置
test.POST("mail", controllers.AdminSendTestMail)
// 测试缩略图生成器调用
test.POST("thumb", controllers.AdminTestThumbGenerator)
}
// 离线下载相关
aria2 := admin.Group("aria2")
{
// 测试连接配置
aria2.POST("test", controllers.AdminTestAria2)
}
vol := admin.Group("vol")
{
vol.GET("sync", controllers.AdminSyncVol)
}
// 兑换码相关
redeem := admin.Group("redeem")
{
// 列出激活码
redeem.POST("list", controllers.AdminListRedeems)
// 生成激活码
redeem.POST("", controllers.AdminGenerateRedeems)
// 删除激活码
redeem.DELETE(":id", controllers.AdminDeleteRedeem)
}
// 存储策略管理
policy := admin.Group("policy")
{
// 列出存储策略
policy.POST("list", controllers.AdminListPolicy)
// 测试本地路径可用性
policy.POST("test/path", controllers.AdminTestPath)
// 测试从机通信
policy.POST("test/slave", controllers.AdminTestSlave)
// 创建存储策略
policy.POST("", controllers.AdminAddPolicy)
// 创建跨域策略
policy.POST("cors", controllers.AdminAddCORS)
// 创建COS回调函数
policy.POST("scf", controllers.AdminAddSCF)
// 获取 OneDrive OAuth URL
oauth := policy.Group(":id/oauth")
{
// 获取 OneDrive OAuth URL
oauth.GET("onedrive", controllers.AdminOAuthURL("onedrive"))
// 获取 Google Drive OAuth URL
oauth.GET("googledrive", controllers.AdminOAuthURL("googledrive"))
}
// 获取 存储策略
policy.GET(":id", controllers.AdminGetPolicy)
// 删除 存储策略
policy.DELETE(":id", controllers.AdminDeletePolicy)
}
// 用户组管理
group := admin.Group("group")
{
// 列出用户组
group.POST("list", controllers.AdminListGroup)
// 获取用户组
group.GET(":id", controllers.AdminGetGroup)
// 创建/保存用户组
group.POST("", controllers.AdminAddGroup)
// 删除
group.DELETE(":id", controllers.AdminDeleteGroup)
}
user := admin.Group("user")
{
// 列出用户
user.POST("list", controllers.AdminListUser)
// 获取用户
user.GET(":id", controllers.AdminGetUser)
// 创建/保存用户
user.POST("", controllers.AdminAddUser)
// 删除
user.POST("delete", controllers.AdminDeleteUser)
// 封禁/解封用户
user.PATCH("ban/:id", controllers.AdminBanUser)
}
file := admin.Group("file")
{
// 列出文件
file.POST("list", controllers.AdminListFile)
// 预览文件
file.GET("preview/:id", middleware.Sandbox(), controllers.AdminGetFile)
// 删除
file.POST("delete", controllers.AdminDeleteFile)
// 列出用户或外部文件系统目录
file.GET("folders/:type/:id/*path",
controllers.AdminListFolders)
}
share := admin.Group("share")
{
// 列出分享
share.POST("list", controllers.AdminListShare)
// 删除
share.POST("delete", controllers.AdminDeleteShare)
}
order := admin.Group("order")
{
// 列出订单
order.POST("list", controllers.AdminListOrder)
// 删除
order.POST("delete", controllers.AdminDeleteOrder)
}
download := admin.Group("download")
{
// 列出任务
download.POST("list", controllers.AdminListDownload)
// 删除
download.POST("delete", controllers.AdminDeleteDownload)
}
task := admin.Group("task")
{
// 列出任务
task.POST("list", controllers.AdminListTask)
// 删除
task.POST("delete", controllers.AdminDeleteTask)
// 新建文件导入任务
task.POST("import", controllers.AdminCreateImportTask)
}
report := admin.Group("report")
{
// 列出未处理举报
report.POST("list", controllers.AdminListReport)
// 删除
report.POST("delete", controllers.AdminDeleteReport)
}
node := admin.Group("node")
{
// 列出从机节点
node.POST("list", controllers.AdminListNodes)
// 列出从机节点
node.POST("aria2/test", controllers.AdminTestAria2)
// 创建/保存节点
node.POST("", controllers.AdminAddNode)
// 启用/暂停节点
node.PATCH("enable/:id/:desired", controllers.AdminToggleNode)
// 删除节点
node.DELETE(":id", controllers.AdminDeleteNode)
// 获取节点
node.GET(":id", controllers.AdminGetNode)
}
}
// 用户
user := auth.Group("user")
{
// 当前登录用户信息
user.GET("me", controllers.UserMe)
// 存储信息
user.GET("storage", controllers.UserStorage)
// 退出登录
user.DELETE("session", controllers.UserSignOut)
// Generate temp URL for copying client-side session, used in adding accounts
// for mobile App.
user.GET("session", controllers.UserPrepareCopySession)
// WebAuthn 注册相关
authn := user.Group("authn",
middleware.IsFunctionEnabled("authn_enabled"))
{
authn.PUT("", controllers.StartRegAuthn)
authn.PUT("finish", controllers.FinishRegAuthn)
}
// 用户设置
setting := user.Group("setting")
{
// 获取用户可选存储策略
setting.GET("policies", controllers.UserAvailablePolicies)
// 获取用户可选节点
setting.GET("nodes", controllers.UserAvailableNodes)
// 任务队列
setting.GET("tasks", controllers.UserTasks)
// 获取当前用户设定
setting.GET("", controllers.UserSetting)
// 从文件上传头像
setting.POST("avatar", controllers.UploadAvatar)
// 设定为Gravatar头像
setting.PUT("avatar", controllers.UseGravatar)
// 更改用户设定
setting.PATCH(":option", controllers.UpdateOption)
// 获得二步验证初始化信息
setting.GET("2fa", controllers.UserInit2FA)
}
}
// 文件
file := auth.Group("file", middleware.PhoneRequired(), middleware.HashID(hashid.FileID))
{
// 上传
upload := file.Group("upload")
{
// 文件上传
upload.POST(":sessionId/:index", controllers.FileUpload)
// 创建上传会话
upload.PUT("", controllers.GetUploadSession)
// 删除给定上传会话
upload.DELETE(":sessionId", controllers.DeleteUploadSession)
// 删除全部上传会话
upload.DELETE("", controllers.DeleteAllUploadSession)
}
// 更新文件
file.PUT("update/:id", controllers.PutContent)
// 创建空白文件
file.POST("create", controllers.CreateFile)
// 创建文件下载会话
file.PUT("download/:id", controllers.CreateDownloadSession)
// 预览文件
file.GET("preview/:id", middleware.Sandbox(), controllers.Preview)
// 获取文本文件内容
file.GET("content/:id", middleware.Sandbox(), controllers.PreviewText)
// 取得Office文档预览地址
file.GET("doc/:id", controllers.GetDocPreview)
// 获取缩略图
file.GET("thumb/:id", controllers.Thumb)
// 取得文件外链
file.POST("source", controllers.GetSource)
// 打包要下载的文件
file.POST("archive", controllers.Archive)
// 创建文件压缩任务
file.POST("compress", controllers.Compress)
// 创建文件解压缩任务
file.POST("decompress", controllers.Decompress)
// 创建文件转移任务
file.POST("relocate", controllers.Relocate)
// 搜索文件
file.GET("search/:type/:keywords", controllers.SearchFile)
}
// 离线下载任务
aria2 := auth.Group("aria2", middleware.PhoneRequired())
{
// 创建URL下载任务
aria2.POST("url", controllers.AddAria2URL)
// 创建种子下载任务
aria2.POST("torrent/:id", middleware.HashID(hashid.FileID), controllers.AddAria2Torrent)
// 重新选择要下载的文件
aria2.PUT("select/:gid", controllers.SelectAria2File)
// 取消或删除下载任务
aria2.DELETE("task/:gid", controllers.CancelAria2Download)
// 获取正在下载中的任务
aria2.GET("downloading", controllers.ListDownloading)
// 获取已完成的任务
aria2.GET("finished", controllers.ListFinished)
}
// 目录
directory := auth.Group("directory", middleware.PhoneRequired())
{
// 创建目录
directory.PUT("", controllers.CreateDirectory)
// 列出目录下内容
directory.GET("*path", controllers.ListDirectory)
}
// 对象,文件和目录的抽象
object := auth.Group("object", middleware.PhoneRequired())
{
// 删除对象
object.DELETE("", controllers.Delete)
// 移动对象
object.PATCH("", controllers.Move)
// 复制对象
object.POST("copy", controllers.Copy)
// 重命名对象
object.POST("rename", controllers.Rename)
// 获取对象属性
object.GET("property/:id", controllers.GetProperty)
}
// 分享
share := auth.Group("share", middleware.PhoneRequired())
{
// 创建新分享
share.POST("", controllers.CreateShare)
// 列出我的分享
share.GET("", controllers.ListShare)
// 转存他人分享
share.POST("save/:id",
middleware.ShareAvailable(),
middleware.CheckShareUnlocked(),
middleware.BeforeShareDownload(),
controllers.SaveShare,
)
// 更新分享属性
share.PATCH(":id",
middleware.ShareAvailable(),
middleware.ShareOwner(),
controllers.UpdateShare,
)
// 删除分享
share.DELETE(":id",
controllers.DeleteShare,
)
}
// 用户标签
tag := auth.Group("tag")
{
// 创建文件分类标签
tag.POST("filter", controllers.CreateFilterTag)
// 创建目录快捷方式标签
tag.POST("link", controllers.CreateLinkTag)
// 删除标签
tag.DELETE(":id", middleware.HashID(hashid.TagID), controllers.DeleteTag)
}
// 增值服务相关
vas := auth.Group("vas", middleware.PhoneRequired())
{
// 获取容量包及配额信息
vas.GET("pack", controllers.GetQuota)
// 获取商品信息,同时返回支付信息
vas.GET("product", controllers.GetProduct)
// 新建支付订单
vas.POST("order", controllers.NewOrder)
// 查询订单状态
vas.GET("order/:id", controllers.OrderStatus)
// 获取兑换码信息
vas.GET("redeem/:code", controllers.GetRedeemInfo)
// 执行兑换
vas.POST("redeem/:code", controllers.DoRedeem)
}
// WebDAV管理相关
webdav := auth.Group("webdav", middleware.PhoneRequired())
{
// 获取账号信息
webdav.GET("accounts", controllers.GetWebDAVAccounts)
// 新建账号
webdav.POST("accounts", controllers.CreateWebDAVAccounts)
// 删除账号
webdav.DELETE("accounts/:id", controllers.DeleteWebDAVAccounts)
// 删除目录挂载
webdav.DELETE("mount/:id",
middleware.HashID(hashid.FolderID),
controllers.DeleteWebDAVMounts,
)
// 创建目录挂载
webdav.POST("mount", controllers.CreateWebDAVMounts)
// 更新账号可读性和是否使用代理服务
webdav.PATCH("accounts", controllers.UpdateWebDAVAccounts)
}
}
}
// 初始化WebDAV相关路由
initWebDAV(r.Group("dav"))
return r
}
// initWebDAV 初始化WebDAV相关路由
func initWebDAV(group *gin.RouterGroup) {
{
group.Use(middleware.WebDAVAuth())
group.Any("/*path", controllers.ServeWebDAV)
group.Any("", controllers.ServeWebDAV)
group.Handle("PROPFIND", "/*path", controllers.ServeWebDAV)
group.Handle("PROPFIND", "", controllers.ServeWebDAV)
group.Handle("MKCOL", "/*path", controllers.ServeWebDAV)
group.Handle("LOCK", "/*path", controllers.ServeWebDAV)
group.Handle("UNLOCK", "/*path", controllers.ServeWebDAV)
group.Handle("PROPPATCH", "/*path", controllers.ServeWebDAV)
group.Handle("COPY", "/*path", controllers.ServeWebDAV)
group.Handle("MOVE", "/*path", controllers.ServeWebDAV)
}
}