Init V4 community edition (#2265)
* Init V4 community edition * Init V4 community edition
This commit is contained in:
@@ -1,76 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cluster"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/googledrive"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/onedrive"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/oauth"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/mq"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SlaveNotificationService struct {
|
||||
Subject string `uri:"subject" binding:"required"`
|
||||
}
|
||||
|
||||
type OauthCredentialService struct {
|
||||
PolicyID uint `uri:"id" binding:"required"`
|
||||
}
|
||||
|
||||
func HandleMasterHeartbeat(req *serializer.NodePingReq) serializer.Response {
|
||||
res, err := cluster.DefaultController.HandleHeartBeat(req)
|
||||
if err != nil {
|
||||
return serializer.Err(serializer.CodeInternalSetting, "Cannot initialize slave controller", err)
|
||||
}
|
||||
|
||||
return serializer.Response{
|
||||
Code: 0,
|
||||
Data: res,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSlaveNotificationPush 转发从机的消息通知到本机消息队列
|
||||
func (s *SlaveNotificationService) HandleSlaveNotificationPush(c *gin.Context) serializer.Response {
|
||||
var msg mq.Message
|
||||
dec := gob.NewDecoder(c.Request.Body)
|
||||
if err := dec.Decode(&msg); err != nil {
|
||||
return serializer.ParamErr("Cannot parse notification message", err)
|
||||
}
|
||||
|
||||
mq.GlobalMQ.Publish(s.Subject, msg)
|
||||
return serializer.Response{}
|
||||
}
|
||||
|
||||
// Get 获取主机Oauth策略的AccessToken
|
||||
func (s *OauthCredentialService) Get(c *gin.Context) serializer.Response {
|
||||
policy, err := model.GetPolicyByID(s.PolicyID)
|
||||
if err != nil {
|
||||
return serializer.Err(serializer.CodePolicyNotExist, "", err)
|
||||
}
|
||||
|
||||
var client oauth.TokenProvider
|
||||
switch policy.Type {
|
||||
case "onedrive":
|
||||
client, err = onedrive.NewClient(&policy)
|
||||
if err != nil {
|
||||
return serializer.Err(serializer.CodeInternalSetting, "Cannot initialize OneDrive client", err)
|
||||
}
|
||||
case "googledrive":
|
||||
client, err = googledrive.NewClient(&policy)
|
||||
if err != nil {
|
||||
return serializer.Err(serializer.CodeInternalSetting, "Cannot initialize Google Drive client", err)
|
||||
}
|
||||
default:
|
||||
return serializer.Err(serializer.CodePolicyNotExist, "", nil)
|
||||
}
|
||||
|
||||
if err := client.UpdateCredential(c, conf.SystemConfig.Mode == "slave"); err != nil {
|
||||
return serializer.Err(serializer.CodeInternalSetting, "Cannot refresh OneDrive credential", err)
|
||||
}
|
||||
|
||||
return serializer.Response{Data: client.AccessToken()}
|
||||
}
|
||||
1
service/node/response.go
Normal file
1
service/node/response.go
Normal file
@@ -0,0 +1 @@
|
||||
package node
|
||||
120
service/node/rpc.go
Normal file
120
service/node/rpc.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/credmanager"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SlaveNotificationService struct {
|
||||
Subject string `uri:"subject" binding:"required"`
|
||||
}
|
||||
|
||||
type (
|
||||
OauthCredentialParamCtx struct{}
|
||||
OauthCredentialService struct {
|
||||
ID string `uri:"id" binding:"required"`
|
||||
}
|
||||
)
|
||||
|
||||
// Get 获取主机Oauth策略的AccessToken
|
||||
func (s *OauthCredentialService) Get(c *gin.Context) (*credmanager.CredentialResponse, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
credManager := dep.CredManager()
|
||||
|
||||
cred, err := credManager.Obtain(c, s.ID)
|
||||
if cred == nil || err != nil {
|
||||
return nil, serializer.NewError(serializer.CodeNotFound, "Credential not found", err)
|
||||
}
|
||||
|
||||
return &credmanager.CredentialResponse{
|
||||
Token: cred.String(),
|
||||
ExpireAt: cred.Expiry(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type (
|
||||
StatelessPrepareUploadParamCtx struct{}
|
||||
)
|
||||
|
||||
func StatelessPrepareUpload(s *fs.StatelessPrepareUploadService, c *gin.Context) (*fs.StatelessPrepareUploadResponse, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
userClient := dep.UserClient()
|
||||
user, err := userClient.GetLoginUserByID(c, s.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := context.WithValue(c.Request.Context(), inventory.UserCtx{}, user)
|
||||
fm := manager.NewFileManager(dep, user)
|
||||
uploadSession, err := fm.PrepareUpload(ctx, s.UploadRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fs.StatelessPrepareUploadResponse{
|
||||
Session: uploadSession,
|
||||
Req: s.UploadRequest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type (
|
||||
StatelessCompleteUploadParamCtx struct{}
|
||||
)
|
||||
|
||||
func StatelessCompleteUpload(s *fs.StatelessCompleteUploadService, c *gin.Context) (fs.File, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
userClient := dep.UserClient()
|
||||
user, err := userClient.GetLoginUserByID(c, s.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
util.WithValue(c, inventory.UserCtx{}, user)
|
||||
fm := manager.NewFileManager(dep, user)
|
||||
return fm.CompleteUpload(c, s.UploadSession)
|
||||
}
|
||||
|
||||
type (
|
||||
StatelessOnUploadFailedParamCtx struct{}
|
||||
)
|
||||
|
||||
func StatelessOnUploadFailed(s *fs.StatelessOnUploadFailedService, c *gin.Context) error {
|
||||
dep := dependency.FromContext(c)
|
||||
userClient := dep.UserClient()
|
||||
user, err := userClient.GetLoginUserByID(c, s.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
util.WithValue(c, inventory.UserCtx{}, user)
|
||||
fm := manager.NewFileManager(dep, user)
|
||||
fm.OnUploadFailed(c, s.UploadSession)
|
||||
return nil
|
||||
}
|
||||
|
||||
type StatelessCreateFileParamCtx struct{}
|
||||
|
||||
func StatelessCreateFile(s *fs.StatelessCreateFileService, c *gin.Context) error {
|
||||
dep := dependency.FromContext(c)
|
||||
userClient := dep.UserClient()
|
||||
user, err := userClient.GetLoginUserByID(c, s.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri, err := fs.NewUriFromString(s.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
util.WithValue(c, inventory.UserCtx{}, user)
|
||||
fm := manager.NewFileManager(dep, user)
|
||||
_, err = fm.Create(c, uri, s.Type)
|
||||
return err
|
||||
}
|
||||
150
service/node/task.go
Normal file
150
service/node/task.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/task"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/cluster"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/workflows"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/queue"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type (
|
||||
CreateSlaveTaskParamCtx struct{}
|
||||
)
|
||||
|
||||
func CreateTaskInSlave(s *cluster.CreateSlaveTask, c *gin.Context) (int, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
registry := dep.TaskRegistry()
|
||||
|
||||
props, err := slaveTaskPropsFromContext(c)
|
||||
if err != nil {
|
||||
return 0, serializer.NewError(serializer.CodeParamErr, "failed to get master props from header", err)
|
||||
}
|
||||
|
||||
var t queue.Task
|
||||
switch s.Type {
|
||||
case queue.SlaveUploadTaskType:
|
||||
t = workflows.NewSlaveUploadTask(c, props, registry.NextID(), s.State)
|
||||
case queue.SlaveCreateArchiveTaskType:
|
||||
t = workflows.NewSlaveCreateArchiveTask(c, props, registry.NextID(), s.State)
|
||||
case queue.SlaveExtractArchiveType:
|
||||
t = workflows.NewSlaveExtractArchiveTask(c, props, registry.NextID(), s.State)
|
||||
default:
|
||||
return 0, serializer.NewError(serializer.CodeParamErr, "type not supported", nil)
|
||||
}
|
||||
|
||||
if err := dep.SlaveQueue(c).QueueTask(c, t); err != nil {
|
||||
return 0, serializer.NewError(serializer.CodeInternalSetting, "failed to queue task", err)
|
||||
}
|
||||
|
||||
registry.Set(t.ID(), t)
|
||||
return t.ID(), nil
|
||||
}
|
||||
|
||||
type (
|
||||
GetSlaveTaskParamCtx struct{}
|
||||
GetSlaveTaskService struct {
|
||||
ID int `uri:"id" binding:"required"`
|
||||
}
|
||||
)
|
||||
|
||||
func (s *GetSlaveTaskService) Get(c *gin.Context) (*cluster.SlaveTaskSummary, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
registry := dep.TaskRegistry()
|
||||
|
||||
t, ok := registry.Get(s.ID)
|
||||
if !ok {
|
||||
return nil, serializer.NewError(serializer.CodeNotFound, "task not found", nil)
|
||||
}
|
||||
status := t.Status()
|
||||
_, clearOnComplete := c.GetQuery(routes.SlaveClearTaskRegistryQuery)
|
||||
if clearOnComplete && status == task.StatusCompleted ||
|
||||
status == task.StatusError ||
|
||||
status == task.StatusCanceled {
|
||||
registry.Delete(s.ID)
|
||||
}
|
||||
|
||||
res := &cluster.SlaveTaskSummary{
|
||||
Status: status,
|
||||
PrivateState: t.State(),
|
||||
Progress: t.Progress(c),
|
||||
}
|
||||
err := t.Error()
|
||||
if err != nil {
|
||||
res.Error = err.Error()
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func slaveTaskPropsFromContext(ctx context.Context) (*types.SlaveTaskProps, error) {
|
||||
nodeIdStr, ok := ctx.Value(cluster.SlaveNodeIDCtx{}).(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to get node ID from context")
|
||||
}
|
||||
|
||||
nodeId, err := strconv.Atoi(nodeIdStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert node ID to int: %w", err)
|
||||
}
|
||||
|
||||
masterSiteUrl := cluster.MasterSiteUrlFromContext(ctx)
|
||||
if masterSiteUrl == "" {
|
||||
return nil, fmt.Errorf("failed to get master site URL from context")
|
||||
}
|
||||
|
||||
masterSiteVersion, ok := ctx.Value(cluster.MasterSiteVersionCtx{}).(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to get master site version from context")
|
||||
}
|
||||
|
||||
masterSiteId, ok := ctx.Value(cluster.MasterSiteIDCtx{}).(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert master site ID to int: %w", err)
|
||||
}
|
||||
|
||||
props := &types.SlaveTaskProps{
|
||||
NodeID: nodeId,
|
||||
MasterSiteID: masterSiteId,
|
||||
MasterSiteURl: masterSiteUrl,
|
||||
MasterSiteVersion: masterSiteVersion,
|
||||
}
|
||||
|
||||
return props, nil
|
||||
}
|
||||
|
||||
type (
|
||||
FolderCleanupParamCtx struct{}
|
||||
)
|
||||
|
||||
func Cleanup(args *cluster.FolderCleanup, c *gin.Context) error {
|
||||
l := logging.FromContext(c)
|
||||
ae := serializer.NewAggregateError()
|
||||
for _, p := range args.Path {
|
||||
l.Info("Cleaning up folder %q", p)
|
||||
if err := os.RemoveAll(p); err != nil {
|
||||
l.Warning("Failed to clean up folder %q: %s", p, err)
|
||||
ae.Add(p, err)
|
||||
}
|
||||
}
|
||||
|
||||
return ae.Aggregate()
|
||||
}
|
||||
|
||||
type (
|
||||
CreateSlaveDownloadTaskParamCtx struct{}
|
||||
GetSlaveDownloadTaskParamCtx struct{}
|
||||
CancelSlaveDownloadTaskParamCtx struct{}
|
||||
SelectSlaveDownloadFilesParamCtx struct{}
|
||||
TestSlaveDownloadParamCtx struct{}
|
||||
)
|
||||
Reference in New Issue
Block a user