diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 4ee4a37..611342b 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -402,6 +402,9 @@ func (bl *FxBlockchain) serve(w http.ResponseWriter, r *http.Request) { actionGetFolderSize: func(from peer.ID, w http.ResponseWriter, r *http.Request) { bl.handleGetFolderSize(r.Context(), from, w, r) }, + actionGetDatastoreSize: func(from peer.ID, w http.ResponseWriter, r *http.Request) { + bl.handleGetDatastoreSize(r.Context(), from, w, r) + }, } // Look up the function in the map and call it @@ -677,7 +680,7 @@ func (bl *FxBlockchain) authorized(pid peer.ID, action string) bool { return true } switch action { - case actionBloxFreeSpace, actionAccountFund, actionAssetsBalance, actionGetFolderSize, actionFetchContainerLogs, actionEraseBlData, actionWifiRemoveall, actionReboot, actionPartition, actionDeleteWifi, actionDisconnectWifi, actionDeleteFulaConfig, actionGetAccount, actionSeeded, actionAccountExists, actionPoolCreate, actionPoolJoin, actionPoolCancelJoin, actionPoolRequests, actionPoolList, actionPoolVote, actionPoolLeave, actionManifestUpload, actionManifestStore, actionManifestAvailable, actionManifestRemove, actionManifestRemoveStorer, actionManifestRemoveStored: + case actionBloxFreeSpace, actionAccountFund, actionAssetsBalance, actionGetDatastoreSize, actionGetFolderSize, actionFetchContainerLogs, actionEraseBlData, actionWifiRemoveall, actionReboot, actionPartition, actionDeleteWifi, actionDisconnectWifi, actionDeleteFulaConfig, actionGetAccount, actionSeeded, actionAccountExists, actionPoolCreate, actionPoolJoin, actionPoolCancelJoin, actionPoolRequests, actionPoolList, actionPoolVote, actionPoolLeave, actionManifestUpload, actionManifestStore, actionManifestAvailable, actionManifestRemove, actionManifestRemoveStorer, actionManifestRemoveStored: bl.authorizedPeersLock.RLock() _, ok := bl.authorizedPeers[pid] bl.authorizedPeersLock.RUnlock() diff --git a/blockchain/blox.go b/blockchain/blox.go index 4954937..0c19f6a 100644 --- a/blockchain/blox.go +++ b/blockchain/blox.go @@ -282,6 +282,43 @@ func (bl *FxBlockchain) GetFolderSize(ctx context.Context, to peer.ID, r wifi.Ge } } +func (bl *FxBlockchain) GetDatastoreSize(ctx context.Context, to peer.ID, r wifi.GetDatastoreSizeRequest) ([]byte, error) { + + if bl.allowTransientConnection { + ctx = network.WithUseTransient(ctx, "fx.blockchain") + } + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(r); err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://"+to.String()+".invalid/"+actionGetDatastoreSize, &buf) + if err != nil { + return nil, err + } + resp, err := bl.c.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + b, err := io.ReadAll(resp.Body) + switch { + case err != nil: + return nil, err + case resp.StatusCode != http.StatusAccepted: + // Attempt to parse the body as JSON. + if jsonErr := json.Unmarshal(b, &apiError); jsonErr != nil { + // If we can't parse the JSON, return the original body in the error. + return nil, fmt.Errorf("unexpected response: %d %s", resp.StatusCode, string(b)) + } + // Return the parsed error message and description. + return nil, fmt.Errorf("unexpected response: %d %s - %s", resp.StatusCode, apiError.Message, apiError.Description) + default: + return b, nil + } +} + func (bl *FxBlockchain) DeleteWifi(ctx context.Context, to peer.ID, r wifi.DeleteWifiRequest) ([]byte, error) { if bl.allowTransientConnection { @@ -428,3 +465,32 @@ func (bl *FxBlockchain) handleGetFolderSize(ctx context.Context, from peer.ID, w } } + +func (bl *FxBlockchain) handleGetDatastoreSize(ctx context.Context, from peer.ID, w http.ResponseWriter, r *http.Request) { + log := log.With("action", actionGetDatastoreSize, "from", from) + + // Parse the JSON body of the request into the DeleteWifiRequest struct + var req wifi.GetDatastoreSizeRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request: %v", err) + http.Error(w, "failed to decode request", http.StatusBadRequest) + return + } + log.Debugw("handleGetDatastoreSize received", "req", req) + + out := wifi.GetDatastoreSizeResponse{} + res, err := wifi.GetDatastoreSize(ctx, req) + if err != nil { + out = wifi.GetDatastoreSizeResponse{} + } else { + out = res + } + log.Debugw("handleGetDatastoreSize response", "out", out) + w.WriteHeader(http.StatusAccepted) + if err := json.NewEncoder(w).Encode(out); err != nil { + log.Error("failed to write response: %v", err) + http.Error(w, "failed to write response", http.StatusInternalServerError) + return + } + +} diff --git a/blockchain/interface.go b/blockchain/interface.go index 1ce826e..4f45244 100644 --- a/blockchain/interface.go +++ b/blockchain/interface.go @@ -46,6 +46,7 @@ const ( actionGetAccount = "get-account" actionFetchContainerLogs = "fetch-container-logs" actionGetFolderSize = "get-folder-size" + actionGetDatastoreSize = "get-datastore-size" ) type LinkWithLimit struct { @@ -358,6 +359,7 @@ type Blockchain interface { GetAccount(context.Context, peer.ID) ([]byte, error) FetchContainerLogs(context.Context, peer.ID, wifi.FetchContainerLogsRequest) ([]byte, error) GetFolderSize(context.Context, peer.ID, wifi.GetFolderSizeRequest) ([]byte, error) + GetDatastoreSize(context.Context, peer.ID, wifi.GetDatastoreSizeRequest) ([]byte, error) } var requestTypes = map[string]reflect.Type{ @@ -396,6 +398,7 @@ var requestTypes = map[string]reflect.Type{ actionGetAccount: reflect.TypeOf(GetAccountRequest{}), actionFetchContainerLogs: reflect.TypeOf(wifi.FetchContainerLogsRequest{}), actionGetFolderSize: reflect.TypeOf(wifi.GetFolderSizeRequest{}), + actionGetDatastoreSize: reflect.TypeOf(wifi.GetDatastoreSizeRequest{}), } var responseTypes = map[string]reflect.Type{ @@ -434,4 +437,5 @@ var responseTypes = map[string]reflect.Type{ actionGetAccount: reflect.TypeOf(GetAccountResponse{}), actionFetchContainerLogs: reflect.TypeOf(wifi.FetchContainerLogsResponse{}), actionGetFolderSize: reflect.TypeOf(wifi.GetFolderSizeResponse{}), + actionGetDatastoreSize: reflect.TypeOf(wifi.GetDatastoreSizeResponse{}), } diff --git a/cmd/blox/main.go b/cmd/blox/main.go index b7a9291..129e2ff 100644 --- a/cmd/blox/main.go +++ b/cmd/blox/main.go @@ -87,7 +87,7 @@ type Child struct { var ( logger = logging.Logger("fula/cmd/blox") - baseURL = "http://localhost:5002" + baseURL = "http://localhost:5001" app struct { cli.App initOnly bool diff --git a/mobile/blockchain.go b/mobile/blockchain.go index 9012f4b..954d73e 100644 --- a/mobile/blockchain.go +++ b/mobile/blockchain.go @@ -203,3 +203,8 @@ func (c *Client) GetFolderSize(folderPath string) ([]byte, error) { ctx := context.TODO() return c.bl.GetFolderSize(ctx, c.bloxPid, wifi.GetFolderSizeRequest{FolderPath: folderPath}) } + +func (c *Client) GetDatastoreSize() ([]byte, error) { + ctx := context.TODO() + return c.bl.GetDatastoreSize(ctx, c.bloxPid, wifi.GetDatastoreSizeRequest{}) +} diff --git a/wap/pkg/wifi/properties.go b/wap/pkg/wifi/properties.go index 9625930..f586472 100644 --- a/wap/pkg/wifi/properties.go +++ b/wap/pkg/wifi/properties.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "crypto/sha256" "encoding/base64" + "encoding/json" "fmt" "io" "os/exec" @@ -14,7 +15,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" + "github.com/ipfs/kubo/client/rpc" "github.com/libp2p/go-libp2p/core/crypto" + ma "github.com/multiformats/go-multiaddr" "golang.org/x/crypto/ed25519" ) @@ -39,6 +42,25 @@ type GetFolderSizeResponse struct { SizeInBytes string `json:"size"` } +type GetDatastoreSizeRequest struct { +} + +type GetDatastoreSizeResponse struct { + RepoSize string `json:"size"` + StorageMax string `json:"storage_max"` + NumObjects string `json:"count"` + RepoPath string `json:"folder_path"` + Version string `json:"version"` +} + +type getDatastoreSizeResponseLocal struct { + RepoSize int64 + StorageMax int64 + NumObjects int + RepoPath string + Version string +} + type FetchContainerLogsResponse struct { Status bool `json:"status"` Msg string `json:"msg"` @@ -138,6 +160,51 @@ func GetFolderSize(ctx context.Context, req GetFolderSizeRequest) (GetFolderSize }, nil } +func GetDatastoreSize(ctx context.Context, req GetDatastoreSizeRequest) (GetDatastoreSizeResponse, error) { + nodeMultiAddr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5001") + if err != nil { + return GetDatastoreSizeResponse{}, fmt.Errorf("invalid multiaddress: %w", err) + } + node, err := rpc.NewApi(nodeMultiAddr) + if err != nil { + return GetDatastoreSizeResponse{}, fmt.Errorf("error creating API client: %v", err) + } + + stat, err := node.Request("repo/stat").Send(ctx) + if err != nil { + return GetDatastoreSizeResponse{}, fmt.Errorf("error executing api command: %v", err) + } + + if stat.Output != nil { + defer stat.Output.Close() // Ensure the output is closed after reading + + data, err := io.ReadAll(stat.Output) + if err != nil { + return GetDatastoreSizeResponse{}, fmt.Errorf("error reading response output: %v", err) + } + log.Debugw("GetDatastoreSize response received", "data", string(data)) + var response getDatastoreSizeResponseLocal + err = json.Unmarshal(data, &response) + if err != nil { + return GetDatastoreSizeResponse{}, fmt.Errorf("error unmarshaling response data: %v", err) + } + responseString := GetDatastoreSizeResponse{ + RepoSize: fmt.Sprint(response.RepoSize), + StorageMax: fmt.Sprint(response.StorageMax), + NumObjects: fmt.Sprint(response.NumObjects), + RepoPath: response.RepoPath, + Version: response.Version, + } + + // Optionally, print the response data for debugging + fmt.Println("Response data:", response) + + return responseString, nil + } else { + return GetDatastoreSizeResponse{}, fmt.Errorf("no output received") + } +} + func GetBloxFreeSpace() (BloxFreeSpaceResponse, error) { cmd := `df -B1 2>/dev/null | grep -nE '/storage/(usb|sd[a-z]|nvme)' | awk '{sum2+=$2; sum3+=$3; sum4+=$4; sum5+=$5} END { print NR "," sum2 "," sum3 "," sum4 "," sum5}'` out, err := exec.Command("sh", "-c", cmd).Output()