| @@ -1,26 +0,0 @@ | |||||
| package consts | |||||
| const ( | |||||
| IPFSStateOK = "OK" | |||||
| StorageDirectoryStateOK = "OK" | |||||
| NodeStateNormal = "Normal" | |||||
| NodeStateUnavailable = "Unavailable" | |||||
| ) | |||||
| const ( | |||||
| ObjectStateNormal = "Normal" | |||||
| ObjectStateDeleted = "Deleted" | |||||
| ) | |||||
| const ( | |||||
| StorageObjectStateNormal = "Normal" | |||||
| StorageObjectStateDeleted = "Deleted" | |||||
| StorageObjectStateOutdated = "Outdated" | |||||
| ) | |||||
| const ( | |||||
| CacheStatePinned = "Pinned" | |||||
| CacheStateTemp = "Temp" | |||||
| ) | |||||
| @@ -1,6 +1,6 @@ | |||||
| module gitlink.org.cn/cloudream/common | module gitlink.org.cn/cloudream/common | ||||
| go 1.18 | |||||
| go 1.20 | |||||
| require ( | require ( | ||||
| github.com/antonfisher/nested-logrus-formatter v1.3.1 | github.com/antonfisher/nested-logrus-formatter v1.3.1 | ||||
| @@ -16,40 +16,4 @@ type RepRedundancyConfig struct { | |||||
| } | } | ||||
| type ECRedundancyConfig struct { | type ECRedundancyConfig struct { | ||||
| } | |||||
| type RedundancyDataTypes interface{} | |||||
| type RedundancyDataTypesConst interface { | |||||
| RepRedundancyData | ECRedundancyData | |||||
| } | |||||
| type RepRedundancyData struct { | |||||
| FileHash string `json:"fileHash"` | |||||
| } | |||||
| func NewRedundancyRepData(fileHash string) RepRedundancyData { | |||||
| return RepRedundancyData{ | |||||
| FileHash: fileHash, | |||||
| } | |||||
| } | |||||
| type ECRedundancyData struct { | |||||
| Blocks []ObjectBlock `json:"blocks"` | |||||
| } | |||||
| func NewECRedundancyData(blocks []ObjectBlock) ECRedundancyData { | |||||
| return ECRedundancyData{ | |||||
| Blocks: blocks, | |||||
| } | |||||
| } | |||||
| type ObjectBlock struct { | |||||
| Index int `json:"index"` | |||||
| FileHash string `json:"fileHash"` | |||||
| } | |||||
| func NewObjectBlock(index int, fileHash string) ObjectBlock { | |||||
| return ObjectBlock{ | |||||
| Index: index, | |||||
| FileHash: fileHash, | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,63 @@ | |||||
| package mq | |||||
| import ( | |||||
| "fmt" | |||||
| myreflect "gitlink.org.cn/cloudream/common/utils/reflect" | |||||
| ) | |||||
| type HandlerFn func(svcBase any, msg *Message) (*Message, error) | |||||
| type MessageDispatcher struct { | |||||
| Handlers map[myreflect.Type]HandlerFn | |||||
| } | |||||
| func NewMessageDispatcher() MessageDispatcher { | |||||
| return MessageDispatcher{ | |||||
| Handlers: make(map[myreflect.Type]HandlerFn), | |||||
| } | |||||
| } | |||||
| func (h *MessageDispatcher) Add(typ myreflect.Type, handler HandlerFn) { | |||||
| h.Handlers[typ] = handler | |||||
| } | |||||
| func (h *MessageDispatcher) Handle(svcBase any, msg *Message) (*Message, error) { | |||||
| typ := myreflect.TypeOfValue(msg.Body) | |||||
| fn, ok := h.Handlers[typ] | |||||
| if !ok { | |||||
| return nil, fmt.Errorf("unsupported message type: %s", typ.Name()) | |||||
| } | |||||
| return fn(svcBase, msg) | |||||
| } | |||||
| // 将Service中的一个接口函数作为指定类型消息的处理函数 | |||||
| func AddServiceFn[TSvc any, TReq any, TResp any](dispatcher *MessageDispatcher, svcFn func(svc TSvc, msg *TReq) (*TResp, *CodeMessage)) { | |||||
| dispatcher.Add(myreflect.TypeOf[TReq](), func(svcBase any, reqMsg *Message) (*Message, error) { | |||||
| reqMsgBody := reqMsg.Body.(TReq) | |||||
| ret, codeMsg := svcFn(svcBase.(TSvc), &reqMsgBody) | |||||
| var body MessageBodyTypes | |||||
| if ret != nil { | |||||
| body = *ret | |||||
| } | |||||
| respMsg := MakeMessage(body) | |||||
| respMsg.SetCodeMessage(codeMsg.Code, codeMsg.Message) | |||||
| return &respMsg, nil | |||||
| }) | |||||
| } | |||||
| // 将Service中的一个*没有返回值的*接口函数作为指定类型消息的处理函数 | |||||
| func AddNoRespServiceFn[TSvc any, TReq any](dispatcher *MessageDispatcher, svcFn func(svc TSvc, msg *TReq)) { | |||||
| dispatcher.Add(myreflect.TypeOf[TReq](), func(svcBase any, reqMsg *Message) (*Message, error) { | |||||
| reqMsgBody := reqMsg.Body.(TReq) | |||||
| svcFn(svcBase.(TSvc), &reqMsgBody) | |||||
| return nil, nil | |||||
| }) | |||||
| } | |||||
| @@ -1,74 +0,0 @@ | |||||
| package utils | |||||
| import ( | |||||
| "fmt" | |||||
| "regexp" | |||||
| "strconv" | |||||
| "github.com/beevik/etree" | |||||
| ) | |||||
| type EcConfig struct { | |||||
| ecid string `xml:"ecid"` | |||||
| class string `xml:"class"` | |||||
| n int `xml:"n"` | |||||
| k int `xml:"k"` | |||||
| w int `xml:"w"` | |||||
| opt int `xml:"opt"` | |||||
| } | |||||
| func (r *EcConfig) GetK() int { | |||||
| return r.k | |||||
| } | |||||
| func (r *EcConfig) GetN() int { | |||||
| return r.n | |||||
| } | |||||
| func GetEcPolicy() *map[string]EcConfig { | |||||
| doc := etree.NewDocument() | |||||
| if err := doc.ReadFromFile("../conf/sysSetting.xml"); err != nil { | |||||
| panic(err) | |||||
| } | |||||
| ecMap := make(map[string]EcConfig, 20) | |||||
| root := doc.SelectElement("setting") | |||||
| for _, attr := range root.SelectElements("attribute") { | |||||
| if name := attr.SelectElement("name"); name.Text() == "ec.policy" { | |||||
| for _, eci := range attr.SelectElements("value") { | |||||
| tt := EcConfig{} | |||||
| tt.ecid = eci.SelectElement("ecid").Text() | |||||
| tt.class = eci.SelectElement("class").Text() | |||||
| tt.n, _ = strconv.Atoi(eci.SelectElement("n").Text()) | |||||
| tt.k, _ = strconv.Atoi(eci.SelectElement("k").Text()) | |||||
| tt.w, _ = strconv.Atoi(eci.SelectElement("w").Text()) | |||||
| tt.opt, _ = strconv.Atoi(eci.SelectElement("opt").Text()) | |||||
| ecMap[tt.ecid] = tt | |||||
| } | |||||
| } | |||||
| } | |||||
| fmt.Println(ecMap) | |||||
| return &ecMap | |||||
| // | |||||
| } | |||||
| func GetAgentIps() []string { | |||||
| doc := etree.NewDocument() | |||||
| if err := doc.ReadFromFile("../conf/sysSetting.xml"); err != nil { | |||||
| panic(err) | |||||
| } | |||||
| root := doc.SelectElement("setting") | |||||
| var ips []string // 定义存储 IP 的字符串切片 | |||||
| for _, attr := range root.SelectElements("attribute") { | |||||
| if name := attr.SelectElement("name"); name.Text() == "agents.addr" { | |||||
| for _, ip := range attr.SelectElements("value") { | |||||
| ipRegex := regexp.MustCompile(`\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b`) | |||||
| match := ipRegex.FindString(ip.Text()) | |||||
| print(match) | |||||
| ips = append(ips, match) | |||||
| } | |||||
| } | |||||
| } | |||||
| return ips | |||||
| } | |||||
| @@ -1,123 +0,0 @@ | |||||
| package grpc | |||||
| // TODO 拆分到存储服务的common包里去 | |||||
| /* | |||||
| import ( | |||||
| "context" | |||||
| "fmt" | |||||
| "io" | |||||
| myio "gitlink.org.cn/cloudream/common/utils/io" | |||||
| "gitlink.org.cn/cloudream/proto" | |||||
| ) | |||||
| type fileReadCloser struct { | |||||
| io.ReadCloser | |||||
| stream proto.FileTransport_GetFileClient | |||||
| cancelFn context.CancelFunc | |||||
| readData []byte | |||||
| } | |||||
| func (s *fileReadCloser) Read(p []byte) (int, error) { | |||||
| if s.readData == nil { | |||||
| resp, err := s.stream.Recv() | |||||
| if err != nil { | |||||
| return 0, err | |||||
| } | |||||
| if resp.Type == proto.FileDataPacketType_Data { | |||||
| s.readData = resp.Data | |||||
| } else if resp.Type == proto.FileDataPacketType_EOF { | |||||
| return 0, io.EOF | |||||
| } else { | |||||
| return 0, fmt.Errorf("unsuppoted packt type: %v", resp.Type) | |||||
| } | |||||
| } | |||||
| cnt := copy(p, s.readData) | |||||
| if len(s.readData) == cnt { | |||||
| s.readData = nil | |||||
| } else { | |||||
| s.readData = s.readData[cnt:] | |||||
| } | |||||
| return cnt, nil | |||||
| } | |||||
| func (s *fileReadCloser) Close() error { | |||||
| s.cancelFn() | |||||
| return nil | |||||
| } | |||||
| func GetFileAsStream(client proto.FileTransportClient, fileHash string) (io.ReadCloser, error) { | |||||
| ctx, cancel := context.WithCancel(context.Background()) | |||||
| stream, err := client.GetFile(ctx, &proto.GetReq{ | |||||
| FileHash: fileHash, | |||||
| }) | |||||
| if err != nil { | |||||
| cancel() | |||||
| return nil, fmt.Errorf("request grpc failed, err: %w", err) | |||||
| } | |||||
| return &fileReadCloser{ | |||||
| stream: stream, | |||||
| cancelFn: cancel, | |||||
| }, nil | |||||
| } | |||||
| type fileWriteCloser struct { | |||||
| myio.PromiseWriteCloser[string] | |||||
| stream proto.FileTransport_SendFileClient | |||||
| } | |||||
| func (s *fileWriteCloser) Write(p []byte) (int, error) { | |||||
| err := s.stream.Send(&proto.FileDataPacket{ | |||||
| Type: proto.FileDataPacketType_Data, | |||||
| Data: p, | |||||
| }) | |||||
| if err != nil { | |||||
| return 0, err | |||||
| } | |||||
| return len(p), nil | |||||
| } | |||||
| func (s *fileWriteCloser) Abort(err error) { | |||||
| s.stream.CloseSend() | |||||
| } | |||||
| func (s *fileWriteCloser) Finish() (string, error) { | |||||
| err := s.stream.Send(&proto.FileDataPacket{ | |||||
| Type: proto.FileDataPacketType_EOF, | |||||
| }) | |||||
| if err != nil { | |||||
| return "", fmt.Errorf("send EOF packet failed, err: %w", err) | |||||
| } | |||||
| resp, err := s.stream.CloseAndRecv() | |||||
| if err != nil { | |||||
| return "", fmt.Errorf("receive response failed, err: %w", err) | |||||
| } | |||||
| return resp.FileHash, nil | |||||
| } | |||||
| func SendFileAsStream(client proto.FileTransportClient) (myio.PromiseWriteCloser[string], error) { | |||||
| stream, err := client.SendFile(context.Background()) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return &fileWriteCloser{ | |||||
| stream: stream, | |||||
| }, nil | |||||
| } | |||||
| */ | |||||
| @@ -1,82 +0,0 @@ | |||||
| package utils | |||||
| import ( | |||||
| //"fmt" | |||||
| "github.com/go-ping/ping" | |||||
| //"net" | |||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "strings" | |||||
| "time" | |||||
| ) | |||||
| type ConnStatus struct { | |||||
| Addr string | |||||
| IsReachable bool | |||||
| Delay time.Duration | |||||
| TTL int | |||||
| } | |||||
| // 获取本地主机 IP 地址 | |||||
| func getLocalIP() string { | |||||
| resp, err := http.Get("https://api.ipify.org") | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| defer resp.Body.Close() | |||||
| body, err := ioutil.ReadAll(resp.Body) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| ip := strings.TrimSpace(string(body)) | |||||
| return ip | |||||
| } | |||||
| func GetConnStatus(remoteIP string) (*ConnStatus, error) { | |||||
| // 本地主机 IP 地址 | |||||
| //localIP := getLocalIP() | |||||
| //print("!@#@#!") | |||||
| //print(localIP) | |||||
| conn := ConnStatus{ | |||||
| Addr: remoteIP, | |||||
| IsReachable: false, | |||||
| } | |||||
| pinger, err := ping.NewPinger(remoteIP) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| pinger.Count = 5 // 设置 ping 次数为 5 | |||||
| // pinger.Interval = 1 // 设置 ping 时间间隔为 1 秒 | |||||
| //pinger.Timeout = 2 // 设置 ping 超时时间为 2 秒 | |||||
| //pinger.SetPrivileged(true) // 设置使用特权模式以获取 TTL 值 | |||||
| pinger.OnRecv = func(pkt *ping.Packet) { | |||||
| //fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v ttl=%v (DUP!)\n", | |||||
| // pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt, pkt.Ttl) | |||||
| conn.TTL = pkt.Ttl | |||||
| } | |||||
| /*pinger.OnDuplicateRecv = func(pkt *ping.Packet) { | |||||
| fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v ttl=%v (DUP!)\n", | |||||
| pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt, pkt.Ttl) | |||||
| }*/ | |||||
| pinger.OnFinish = func(stats *ping.Statistics) { | |||||
| //fmt.Printf("\n--- %s ping statistics ---\n", stats.Addr) | |||||
| //fmt.Printf("%d packets transmitted, %d packets received, %v%% packet loss\n", | |||||
| // stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss) | |||||
| //fmt.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n", | |||||
| // stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt) | |||||
| if stats.PacketLoss == 0.0 { | |||||
| conn.IsReachable = true | |||||
| } | |||||
| conn.Delay = stats.AvgRtt | |||||
| } | |||||
| err = pinger.Run() // Blocks until finished. | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return &conn, nil | |||||
| } | |||||
| @@ -1,21 +0,0 @@ | |||||
| package utils | |||||
| import ( | |||||
| "fmt" | |||||
| "strings" | |||||
| ) | |||||
| // MakeMoveOperationFileName Move操作时,写入的文件的名称 | |||||
| func MakeMoveOperationFileName(objectID int64, userID int64) string { | |||||
| return fmt.Sprintf("%d-%d", objectID, userID) | |||||
| } | |||||
| // GetDirectoryName 根据objectName获取所属的文件夹名 | |||||
| func GetDirectoryName(objectName string) string { | |||||
| parts := strings.Split(objectName, "/") | |||||
| //若为文件,dirName设置为空 | |||||
| if len(parts) == 1 { | |||||
| return "" | |||||
| } | |||||
| return parts[0] | |||||
| } | |||||