chore: 初始化 Cloudreve 源码仓库
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/node"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/auth"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/cluster"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/downloader"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/downloader/slave"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/request"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const (
|
||||
nodeStatusCondition = "node_status"
|
||||
)
|
||||
|
||||
func (service *AdminListService) Nodes(c *gin.Context) (*ListNodeResponse, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
nodeClient := dep.NodeClient()
|
||||
|
||||
ctx := context.WithValue(c, inventory.LoadNodeStoragePolicy{}, true)
|
||||
res, err := nodeClient.ListNodes(ctx, &inventory.ListNodeParameters{
|
||||
PaginationArgs: &inventory.PaginationArgs{
|
||||
Page: service.Page - 1,
|
||||
PageSize: service.PageSize,
|
||||
OrderBy: service.OrderBy,
|
||||
Order: inventory.OrderDirection(service.OrderDirection),
|
||||
},
|
||||
Status: node.Status(service.Conditions[nodeStatusCondition]),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, serializer.NewError(serializer.CodeDBError, "Failed to list nodes", err)
|
||||
}
|
||||
|
||||
return &ListNodeResponse{Nodes: res.Nodes, Pagination: res.PaginationResults}, nil
|
||||
}
|
||||
|
||||
type (
|
||||
SingleNodeService struct {
|
||||
ID int `uri:"id" json:"id" binding:"required"`
|
||||
}
|
||||
SingleNodeParamCtx struct{}
|
||||
)
|
||||
|
||||
func (service *SingleNodeService) Get(c *gin.Context) (*GetNodeResponse, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
nodeClient := dep.NodeClient()
|
||||
|
||||
ctx := context.WithValue(c, inventory.LoadNodeStoragePolicy{}, true)
|
||||
node, err := nodeClient.GetNodeById(ctx, service.ID)
|
||||
if err != nil {
|
||||
return nil, serializer.NewError(serializer.CodeDBError, "Failed to get node", err)
|
||||
}
|
||||
|
||||
return &GetNodeResponse{Node: node}, nil
|
||||
}
|
||||
|
||||
type (
|
||||
TestNodeService struct {
|
||||
Node *ent.Node `json:"node" binding:"required"`
|
||||
}
|
||||
TestNodeParamCtx struct{}
|
||||
)
|
||||
|
||||
func (service *TestNodeService) Test(c *gin.Context) error {
|
||||
dep := dependency.FromContext(c)
|
||||
settings := dep.SettingProvider()
|
||||
|
||||
slave, err := url.Parse(service.Node.Server)
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeParamErr, "Failed to parse node URL", err)
|
||||
}
|
||||
|
||||
primaryURL := settings.SiteURL(setting.UseFirstSiteUrl(c)).String()
|
||||
body := map[string]string{
|
||||
"callback": primaryURL,
|
||||
}
|
||||
bodyByte, _ := json.Marshal(body)
|
||||
|
||||
r := dep.RequestClient()
|
||||
res, err := r.Request(
|
||||
"POST",
|
||||
routes.SlavePingRoute(slave),
|
||||
bytes.NewReader(bodyByte),
|
||||
request.WithTimeout(time.Duration(10)*time.Second),
|
||||
request.WithCredential(
|
||||
auth.HMACAuth{SecretKey: []byte(service.Node.SlaveKey)},
|
||||
int64(settings.SlaveRequestSignTTL(c)),
|
||||
),
|
||||
request.WithSlaveMeta(int(service.Node.ID)),
|
||||
request.WithMasterMeta(settings.SiteBasic(c).ID, primaryURL),
|
||||
request.WithCorrelationID(),
|
||||
).CheckHTTPResponse(http.StatusOK).DecodeResponse()
|
||||
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeParamErr, "Failed to connect to node: "+err.Error(), nil)
|
||||
}
|
||||
|
||||
if res.Code != 0 {
|
||||
return serializer.NewError(serializer.CodeParamErr, "Successfully connected to slave node, but slave returns: "+res.Msg, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type (
|
||||
TestNodeDownloaderService struct {
|
||||
Node *ent.Node `json:"node" binding:"required"`
|
||||
}
|
||||
TestNodeDownloaderParamCtx struct{}
|
||||
)
|
||||
|
||||
func (service *TestNodeDownloaderService) Test(c *gin.Context) (string, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
settings := dep.SettingProvider()
|
||||
var (
|
||||
dl downloader.Downloader
|
||||
err error
|
||||
)
|
||||
if service.Node.Type == node.TypeMaster {
|
||||
dl, err = cluster.NewDownloader(c, dep.RequestClient(request.WithContext(c)), dep.SettingProvider(), service.Node.Settings)
|
||||
} else {
|
||||
dl = slave.NewSlaveDownloader(dep.RequestClient(
|
||||
request.WithContext(c),
|
||||
request.WithCorrelationID(),
|
||||
request.WithSlaveMeta(service.Node.ID),
|
||||
request.WithMasterMeta(settings.SiteBasic(c).ID, settings.SiteURL(setting.UseFirstSiteUrl(c)).String()),
|
||||
request.WithCredential(auth.HMACAuth{[]byte(service.Node.SlaveKey)}, int64(settings.SlaveRequestSignTTL(c))),
|
||||
request.WithEndpoint(service.Node.Server),
|
||||
), service.Node.Settings)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", serializer.NewError(serializer.CodeParamErr, "Failed to create downloader", err)
|
||||
}
|
||||
|
||||
version, err := dl.Test(c)
|
||||
if err != nil {
|
||||
return "", serializer.NewError(serializer.CodeParamErr, "Failed to test downloader: "+err.Error(), nil)
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
type (
|
||||
UpsertNodeService struct {
|
||||
Node *ent.Node `json:"node" binding:"required"`
|
||||
}
|
||||
UpsertNodeParamCtx struct{}
|
||||
)
|
||||
|
||||
func (s *UpsertNodeService) Update(c *gin.Context) (*GetNodeResponse, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
nodeClient := dep.NodeClient()
|
||||
|
||||
if s.Node.ID == 0 {
|
||||
return nil, serializer.NewError(serializer.CodeParamErr, "ID is required", nil)
|
||||
}
|
||||
|
||||
node, err := nodeClient.Upsert(c, s.Node)
|
||||
if err != nil {
|
||||
return nil, serializer.NewError(serializer.CodeDBError, "Failed to update node", err)
|
||||
}
|
||||
|
||||
// reload node pool
|
||||
np, err := dep.NodePool(c)
|
||||
if err != nil {
|
||||
return nil, serializer.NewError(serializer.CodeInternalSetting, "Failed to get node pool", err)
|
||||
}
|
||||
np.Upsert(c, node)
|
||||
|
||||
// Clear policy cache since some this node maybe cached by some storage policy
|
||||
kv := dep.KV()
|
||||
kv.Delete(inventory.StoragePolicyCacheKey)
|
||||
|
||||
service := &SingleNodeService{ID: node.ID}
|
||||
return service.Get(c)
|
||||
}
|
||||
|
||||
func (s *UpsertNodeService) Create(c *gin.Context) (*GetNodeResponse, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
nodeClient := dep.NodeClient()
|
||||
|
||||
if s.Node.ID > 0 {
|
||||
return nil, serializer.NewError(serializer.CodeParamErr, "ID must be 0", nil)
|
||||
}
|
||||
|
||||
node, err := nodeClient.Upsert(c, s.Node)
|
||||
if err != nil {
|
||||
return nil, serializer.NewError(serializer.CodeDBError, "Failed to create node", err)
|
||||
}
|
||||
|
||||
// reload node pool
|
||||
np, err := dep.NodePool(c)
|
||||
if err != nil {
|
||||
return nil, serializer.NewError(serializer.CodeInternalSetting, "Failed to get node pool", err)
|
||||
}
|
||||
np.Upsert(c, node)
|
||||
|
||||
service := &SingleNodeService{ID: node.ID}
|
||||
return service.Get(c)
|
||||
}
|
||||
|
||||
func (s *SingleNodeService) Delete(c *gin.Context) error {
|
||||
dep := dependency.FromContext(c)
|
||||
nodeClient := dep.NodeClient()
|
||||
|
||||
ctx := context.WithValue(c, inventory.LoadNodeStoragePolicy{}, true)
|
||||
existing, err := nodeClient.GetNodeById(ctx, s.ID)
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeDBError, "Failed to get node", err)
|
||||
}
|
||||
|
||||
if existing.Type == node.TypeMaster {
|
||||
return serializer.NewError(serializer.CodeInvalidActionOnSystemNode, "", nil)
|
||||
}
|
||||
|
||||
if len(existing.Edges.StoragePolicy) > 0 {
|
||||
return serializer.NewError(
|
||||
serializer.CodeNodeUsedByStoragePolicy,
|
||||
strings.Join(lo.Map(existing.Edges.StoragePolicy, func(i *ent.StoragePolicy, _ int) string {
|
||||
return i.Name
|
||||
}), ", "),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// insert dummpy disabled node in nodepool to evict it
|
||||
disabledNode := &ent.Node{
|
||||
ID: s.ID,
|
||||
Type: node.TypeSlave,
|
||||
Status: node.StatusSuspended,
|
||||
}
|
||||
np, err := dep.NodePool(c)
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeInternalSetting, "Failed to get node pool", err)
|
||||
}
|
||||
np.Upsert(c, disabledNode)
|
||||
return nodeClient.Delete(c, s.ID)
|
||||
}
|
||||
Reference in New Issue
Block a user