diff --git a/japicore/fidRouteHandling.go b/japicore/fidRouteHandling.go index dc2fe8b..cd677ab 100644 --- a/japicore/fidRouteHandling.go +++ b/japicore/fidRouteHandling.go @@ -1,17 +1,15 @@ package japicore import ( - "bytes" "fmt" "net/http" "strings" "github.com/JackalLabs/jackalapi/jutils" - "github.com/JackalLabs/jackalgo/handlers/file_io_handler" "github.com/uptrace/bunrouter" ) -func IpfsHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueue) bunrouter.HandlerFunc { +func (j JApiCore) IpfsHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { var allBytes []byte @@ -31,7 +29,7 @@ func IpfsHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueue) bunr cid := strings.ReplaceAll(id, "/", "_") - handler, err := fileIo.DownloadFile(fmt.Sprintf("%s/%s", operatingRoot, cid)) + handler, err := j.FileIo.DownloadFile(fmt.Sprintf("%s/%s", operatingRoot, cid)) if err != nil { if !toClone { warning := "IPFS CID Not Found" @@ -44,11 +42,14 @@ func IpfsHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueue) bunr return err } - byteReader := bytes.NewReader(byteBuffer.Bytes()) - workingBytes := jutils.CloneBytes(byteReader) - allBytes = jutils.CloneBytes(byteReader) + clonedBytes1, clonedBytes2, err := jutils.CloneByteSlice(byteBuffer.Bytes()) + if err != nil { + jutils.ProcessHttpError("httpGetFileRequest", err, 404, w) + return err + } + allBytes = clonedBytes2 - fid := processUpload(w, fileIo, workingBytes, cid, operatingRoot, queue) + fid := processUpload(w, j.FileIo, clonedBytes1, cid, operatingRoot, j.FileIoQueue) if len(fid) == 0 { warning := "Failed to get FID post-upload" return jutils.ProcessCustomHttpError("IpfsHandler", warning, 500, w) @@ -65,7 +66,7 @@ func IpfsHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueue) bunr } } -func DownloadByFidHandler(fileIo *file_io_handler.FileIoHandler) bunrouter.HandlerFunc { +func (j JApiCore) DownloadByFidHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { id := req.Param("id") if len(id) == 0 { @@ -74,7 +75,7 @@ func DownloadByFidHandler(fileIo *file_io_handler.FileIoHandler) bunrouter.Handl } fid := strings.ReplaceAll(id, "/", "_") - handler, err := fileIo.DownloadFileFromFid(fid) + handler, err := j.FileIo.DownloadFileFromFid(fid) if err != nil { return err } @@ -88,7 +89,7 @@ func DownloadByFidHandler(fileIo *file_io_handler.FileIoHandler) bunrouter.Handl } } -func DeleteByFidHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueue) bunrouter.HandlerFunc { +func (j JApiCore) DeleteByFidHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { id := req.Param("id") if len(id) == 0 { @@ -101,13 +102,13 @@ func DeleteByFidHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueu // TODO - update after deletion by fid is added to jackalgo - //folder, err := fileIo.DownloadFolder(queue.GetRoot("bulk")) + //folder, err := j.FileIo.DownloadFolder(j.FileIoQueue.GetRoot("bulk")) //if err != nil { // jutils.ProcessHttpError("DeleteFile", err, 404, w) // return err //} // - //err = fileIo.DeleteTargets([]string{fid}, folder) + //err = j.FileIo.DeleteTargets([]string{fid}, folder) //if err != nil { // jutils.ProcessHttpError("DeleteFile", err, 500, w) // return err @@ -115,7 +116,7 @@ func DeleteByFidHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueu // message := createJsonResponse("Deletion complete") message := createJsonResponse("Deletion Not Implemented") - condensedWriteJSON(w, message) + jutils.SimpleWriteJSON(w, message) return nil } } diff --git a/japicore/miscRouteHandling.go b/japicore/miscRouteHandling.go index e32d221..becbfdd 100644 --- a/japicore/miscRouteHandling.go +++ b/japicore/miscRouteHandling.go @@ -13,30 +13,30 @@ var ( Module = "Jackal API Core" ) -func Handler() bunrouter.HandlerFunc { +func (j JApiCore) Handler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { return nil } } -func RouteNotFoundHandler() bunrouter.HandlerFunc { +func (j JApiCore) RouteNotFoundHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { warning := fmt.Sprintf("%s is not an availble route", req.URL.Path) return jutils.ProcessCustomHttpError("MethodNotAllowedHandler", warning, 404, w) } } -func MethodNotAllowedHandler() bunrouter.HandlerFunc { +func (j JApiCore) MethodNotAllowedHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { warning := fmt.Sprintf("%s method not availble for \"%s\"", req.URL.Path, req.Method) return jutils.ProcessCustomHttpError("MethodNotAllowedHandler", warning, 405, w) } } -func VersionHandler() bunrouter.HandlerFunc { +func (j JApiCore) VersionHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { message := createJsonResponse("") - condensedWriteJSON(w, message) + jutils.SimpleWriteJSON(w, message) return nil } } diff --git a/japicore/pathRouteHandling.go b/japicore/pathRouteHandling.go index fe66dc6..4d858dc 100644 --- a/japicore/pathRouteHandling.go +++ b/japicore/pathRouteHandling.go @@ -11,16 +11,15 @@ import ( "sync" "github.com/JackalLabs/jackalapi/jutils" - "github.com/JackalLabs/jackalgo/handlers/file_io_handler" "github.com/uptrace/bunrouter" ) -func downloadByPathCore(fileIo *file_io_handler.FileIoHandler, operatingRoot string) bunrouter.HandlerFunc { +func (j JApiCore) downloadByPathCore(operatingRoot string, reportFunc func(num int64)) bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { location := req.Param("location") if len(location) == 0 { warning := "Failed to get Location" - return jutils.ProcessCustomHttpError("DownloadByPathHandler", warning, 404, w) + return jutils.ProcessCustomHttpError("BasicDownloadByPathHandler", warning, 404, w) } uniquePath := readUniquePath(req) @@ -29,21 +28,24 @@ func downloadByPathCore(fileIo *file_io_handler.FileIoHandler, operatingRoot str } operatingRoot += "/" + location - handler, err := fileIo.DownloadFile(operatingRoot) + handler, err := j.FileIo.DownloadFile(operatingRoot) if err != nil { return err } + size := handler.GetFile().Details.Size + reportFunc(size) + fileBytes := handler.GetFile().Buffer().Bytes() _, err = w.Write(fileBytes) if err != nil { - jutils.ProcessError("WWriteError for DownloadByPathHandler", err) + jutils.ProcessError("WWriteError for BasicDownloadByPathHandler", err) } return nil } } -func deleteByPathCore(fileIo *file_io_handler.FileIoHandler, operatingRoot string) bunrouter.HandlerFunc { +func (j JApiCore) deleteByPathCore(operatingRoot string, delFunc func(num int64)) bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { filename := req.Param("filename") if len(filename) == 0 { @@ -54,7 +56,7 @@ func deleteByPathCore(fileIo *file_io_handler.FileIoHandler, operatingRoot strin location := req.Param("location") if len(location) == 0 { warning := "Failed to get Location" - return jutils.ProcessCustomHttpError("DownloadByPathHandler", warning, 404, w) + return jutils.ProcessCustomHttpError("BasicDownloadByPathHandler", warning, 404, w) } cleanFilename := strings.ReplaceAll(filename, "/", "_") @@ -66,25 +68,28 @@ func deleteByPathCore(fileIo *file_io_handler.FileIoHandler, operatingRoot strin } operatingRoot += "/" + location - folder, err := fileIo.DownloadFolder(operatingRoot) + folder, err := j.FileIo.DownloadFolder(operatingRoot) if err != nil { jutils.ProcessHttpError("DeleteFile", err, 404, w) return err } - err = fileIo.DeleteTargets([]string{cleanFilename}, folder) + deletionSize := folder.GetChildFiles()[cleanFilename].Size + delFunc(deletionSize) + + err = j.FileIo.DeleteTargets([]string{cleanFilename}, folder) if err != nil { jutils.ProcessHttpError("DeleteFile", err, 500, w) return err } message := createJsonResponse("Deletion complete") - condensedWriteJSON(w, message) + jutils.SimpleWriteJSON(w, message) return nil } } -func ImportHandler(fileIo *file_io_handler.FileIoHandler, queue *ScrapeQueue) bunrouter.HandlerFunc { +func (j JApiCore) ImportHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_BULK_ROOT", "s/JAPI/Bulk") @@ -107,28 +112,18 @@ func ImportHandler(fileIo *file_io_handler.FileIoHandler, queue *ScrapeQueue) bu for _, target := range data.Targets { wg.Add(1) - queue.Push(fileIo, w, &wg, operatingRoot, target, source) + j.ScrapeQueue.Push(j.FileIo, w, &wg, operatingRoot, target, source) } wg.Wait() message := createJsonResponse("Import complete") - condensedWriteJSON(w, message) + jutils.SimpleWriteJSON(w, message) return nil } } -func DownloadFromBulkByPathHandler(fileIo *file_io_handler.FileIoHandler) bunrouter.HandlerFunc { - operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_BULK_ROOT", "s/JAPI/Bulk") - return downloadByPathCore(fileIo, operatingRoot) -} - -func DownloadByPathHandler(fileIo *file_io_handler.FileIoHandler) bunrouter.HandlerFunc { - operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_OP_ROOT", "s/JAPI") - return downloadByPathCore(fileIo, operatingRoot) -} - -func UploadByPathHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQueue) bunrouter.HandlerFunc { +func (j JApiCore) UploadByPathHandler() bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_OP_ROOT", "s/JAPI") var byteBuffer bytes.Buffer @@ -176,7 +171,7 @@ func UploadByPathHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQue return err } - fid := processUpload(w, fileIo, byteBuffer.Bytes(), head.Filename, operatingRoot, queue) + fid := processUpload(w, j.FileIo, byteBuffer.Bytes(), head.Filename, operatingRoot, j.FileIoQueue) if len(fid) == 0 { warning := "Failed to get FID" return jutils.ProcessCustomHttpError("processUpload", warning, 500, w) @@ -192,17 +187,47 @@ func UploadByPathHandler(fileIo *file_io_handler.FileIoHandler, queue *FileIoQue } message := createJsonResponse("Upload complete") - condensedWriteJSON(w, message) + jutils.SimpleWriteJSON(w, message) return nil } } -func DeleteFromBulkByPathHandler(fileIo *file_io_handler.FileIoHandler) bunrouter.HandlerFunc { +func (j JApiCore) BasicDownloadFromBulkByPathHandler() bunrouter.HandlerFunc { + operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_BULK_ROOT", "s/JAPI/Bulk") + return j.downloadByPathCore(operatingRoot, func(num int64) {}) +} + +func (j JApiCore) BasicDownloadByPathHandler() bunrouter.HandlerFunc { + operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_OP_ROOT", "s/JAPI") + return j.downloadByPathCore(operatingRoot, func(num int64) {}) +} + +func (j JApiCore) BasicDeleteFromBulkByPathHandler() bunrouter.HandlerFunc { + operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_BULK_ROOT", "s/JAPI/Bulk") + return j.deleteByPathCore(operatingRoot, func(num int64) {}) +} + +func (j JApiCore) BasicDeleteByPathHandler() bunrouter.HandlerFunc { + operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_OP_ROOT", "s/JAPI") + return j.deleteByPathCore(operatingRoot, func(num int64) {}) +} + +func (j JApiCore) AdvancedDownloadFromBulkByPathHandler(reportFunc func(num int64)) bunrouter.HandlerFunc { + operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_BULK_ROOT", "s/JAPI/Bulk") + return j.downloadByPathCore(operatingRoot, reportFunc) +} + +func (j JApiCore) AdvancedDownloadByPathHandler(reportFunc func(num int64)) bunrouter.HandlerFunc { + operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_OP_ROOT", "s/JAPI") + return j.downloadByPathCore(operatingRoot, reportFunc) +} + +func (j JApiCore) AdvancedDeleteFromBulkByPathHandler(delFunc func(num int64)) bunrouter.HandlerFunc { operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_BULK_ROOT", "s/JAPI/Bulk") - return deleteByPathCore(fileIo, operatingRoot) + return j.deleteByPathCore(operatingRoot, delFunc) } -func DeleteByPathHandler(fileIo *file_io_handler.FileIoHandler) bunrouter.HandlerFunc { +func (j JApiCore) AdvancedDeleteByPathHandler(delFunc func(num int64)) bunrouter.HandlerFunc { operatingRoot := jutils.LoadEnvVarOrFallback("JAPI_OP_ROOT", "s/JAPI") - return deleteByPathCore(fileIo, operatingRoot) + return j.deleteByPathCore(operatingRoot, delFunc) } diff --git a/japicore/types.go b/japicore/types.go index 4e6e1a2..0fe5545 100644 --- a/japicore/types.go +++ b/japicore/types.go @@ -3,6 +3,8 @@ package japicore import ( "net/http" + "github.com/JackalLabs/jackalgo/handlers/wallet_handler" + "github.com/uptrace/bunrouter" "github.com/JackalLabs/jackalgo/handlers/file_io_handler" @@ -31,3 +33,25 @@ func createJsonResponse(message string) jsonResponse { Version: Version, } } + +type JApiCore struct { + FileIo *file_io_handler.FileIoHandler + FileIoQueue *FileIoQueue + ScrapeQueue *ScrapeQueue + Wallet *wallet_handler.WalletHandler +} + +func InitJApiCore() *JApiCore { + wallet, fileIo := InitWalletSession() + fileIoQueue := NewFileIoQueue() + scrapeQueue := NewScrapeQueue(fileIoQueue) + + core := JApiCore{ + FileIo: fileIo, + FileIoQueue: fileIoQueue, + ScrapeQueue: scrapeQueue, + Wallet: wallet, + } + + return &core +} diff --git a/japicore/utils.go b/japicore/utils.go index 29051d0..624ad65 100644 --- a/japicore/utils.go +++ b/japicore/utils.go @@ -1,9 +1,6 @@ package japicore import ( - "bytes" - "encoding/json" - "fmt" "net/http" "sync" @@ -43,21 +40,6 @@ func processUpload(w http.ResponseWriter, fileIo *file_io_handler.FileIoHandler, return m.Fid() } -func condensedWriteJSON(w http.ResponseWriter, respVal interface{}) { - var buf bytes.Buffer - if err := json.NewEncoder(&buf).Encode(respVal); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - fmt.Printf("json.NewEncoder.Encode: %v", err) - return - } - w.Header().Set("Content-Type", "application/json") - written, err := w.Write(buf.Bytes()) - if err != nil { - fmt.Printf("Written bytes: %d\n", written) - fmt.Println(err) - } -} - func readUniquePath(req bunrouter.Request) string { uniquePath, ok := req.Context().Value(jutils.ReqUniquePath{}).(string) if !ok { diff --git a/jutils/data.go b/jutils/data.go index fc659be..8c63cf8 100644 --- a/jutils/data.go +++ b/jutils/data.go @@ -2,6 +2,9 @@ package jutils import ( "bytes" + "encoding/json" + "fmt" + "net/http" ) func CloneBytes(reader *bytes.Reader) []byte { @@ -16,3 +19,40 @@ func CloneBytes(reader *bytes.Reader) []byte { } return allBytes } + +func CloneByteSlice(source []byte) ([]byte, []byte, error) { + var firstSlice []byte + var SecondSlice []byte + byteReader := bytes.NewReader(source) + + _, err := byteReader.Read(firstSlice) + if err != nil { + return firstSlice, SecondSlice, err + } + + _, err = byteReader.Seek(0, 0) + if err != nil { + return firstSlice, SecondSlice, err + } + + _, err = byteReader.Read(SecondSlice) + if err != nil { + return firstSlice, SecondSlice, err + } + + return firstSlice, SecondSlice, nil +} + +func SimpleWriteJSON(w http.ResponseWriter, respVal interface{}) { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(respVal); err != nil { + ProcessHttpError("SimpleWriteJSON", err, 500, w) + return + } + w.Header().Set("Content-Type", "application/json") + written, err := w.Write(buf.Bytes()) + if err != nil { + fmt.Printf("Written bytes: %d\n", written) + ProcessError("SimpleWriteJSON", err) + } +} diff --git a/jutils/errors.go b/jutils/errors.go index 2f210e4..49fc86c 100644 --- a/jutils/errors.go +++ b/jutils/errors.go @@ -14,6 +14,12 @@ func ProcessError(block string, caughtError error) { fmt.Println("***** End Error Report *****") } +func ProcessCustomError(block string, customError string) error { + asError := errors.New(strings.ToLower(customError)) + ProcessError(block, asError) + return asError +} + func ProcessHttpError(block string, caughtError error, eCode int, w http.ResponseWriter) { ProcessError(block, caughtError) http.Error(w, caughtError.Error(), eCode) diff --git a/jutils/jutils_test.go b/jutils/jutils_test.go index 2c819a4..5c504e3 100644 --- a/jutils/jutils_test.go +++ b/jutils/jutils_test.go @@ -90,6 +90,23 @@ func TestCloneBytes(t *testing.T) { r.Equal(matches, true) } +func TestCloneByteSlice(t *testing.T) { + r := require.New(t) + + byteBuffer := new(bytes.Buffer) + byteArray := byteBuffer.Bytes() + + cloneArray1, cloneArray2, err := CloneByteSlice(byteArray) + if err != nil { + ProcessError("TestCloneByteSlice", err) + } + + matches := reflect.DeepEqual(byteArray, cloneArray1) + r.Equal(matches, true) + matches = reflect.DeepEqual(byteArray, cloneArray2) + r.Equal(matches, true) +} + // Date funcs func TestFriendlyTimestamp(t *testing.T) { r := require.New(t) @@ -104,11 +121,19 @@ func TestFriendlyTimestamp(t *testing.T) { r.IsType(parsedTime, time.Time{}) } +func TestUnixMsTimestamp(t *testing.T) { + // TODO - add test +} + // Error funcs func TestProcessError(t *testing.T) { // TODO - add test } +func TestProcessCustomError(t *testing.T) { + // TODO - add test +} + func TestProcessHttpError(t *testing.T) { // TODO - add test } diff --git a/jutils/time.go b/jutils/time.go index 3b6dc5b..a748c46 100644 --- a/jutils/time.go +++ b/jutils/time.go @@ -15,3 +15,8 @@ func FriendlyTimestamp() string { currentTime.Minute(), currentTime.Second()) } + +func UnixMsTimestamp() int64 { + currentTime := time.Now() + return currentTime.UnixMilli() +} diff --git a/main.go b/main.go index c000841..e81df56 100644 --- a/main.go +++ b/main.go @@ -14,14 +14,11 @@ import ( ) func main() { - wallet, fileIo := japicore.InitWalletSession() - fileIoQueue := japicore.NewFileIoQueue() - - scrapeQueue := japicore.NewScrapeQueue(fileIoQueue) + coreSession := japicore.InitJApiCore() router := bunrouter.New( - bunrouter.WithMethodNotAllowedHandler(japicore.MethodNotAllowedHandler()), - bunrouter.WithNotFoundHandler(japicore.RouteNotFoundHandler()), + bunrouter.WithMethodNotAllowedHandler(coreSession.MethodNotAllowedHandler()), + bunrouter.WithNotFoundHandler(coreSession.RouteNotFoundHandler()), ) group := router.NewGroup("") @@ -29,29 +26,29 @@ func main() { handler = cors.Default().Handler(handler) group.WithGroup("", func(group *bunrouter.Group) { - group.GET("/version", japicore.VersionHandler()) + group.GET("/version", coreSession.VersionHandler()) }) group.WithGroup("/fid", func(group *bunrouter.Group) { - group.GET("/download/:id", japicore.DownloadByFidHandler(fileIo)) - group.GET("/d/:id", japicore.DownloadByFidHandler(fileIo)) - group.GET("/ipfs/:id", japicore.IpfsHandler(fileIo, fileIoQueue)) + group.GET("/download/:id", coreSession.DownloadByFidHandler()) + group.GET("/d/:id", coreSession.DownloadByFidHandler()) + group.GET("/ipfs/:id", coreSession.IpfsHandler()) - group.POST("/upload", japicore.UploadByPathHandler(fileIo, fileIoQueue)) - group.POST("/u", japicore.UploadByPathHandler(fileIo, fileIoQueue)) - group.DELETE("/del/:id", japicore.DeleteByFidHandler(fileIo, fileIoQueue)) + group.POST("/upload", coreSession.UploadByPathHandler()) + group.POST("/u", coreSession.UploadByPathHandler()) + group.DELETE("/del/:id", coreSession.DeleteByFidHandler()) }) group.WithGroup("/p", func(group *bunrouter.Group) { - group.GET("/downloadfrombulk/*location", japicore.DownloadFromBulkByPathHandler(fileIo)) - group.GET("/download/*location", japicore.DownloadByPathHandler(fileIo)) - group.GET("/d/*location", japicore.DownloadByPathHandler(fileIo)) - - group.POST("/import", japicore.ImportHandler(fileIo, scrapeQueue)) - group.POST("/upload", japicore.UploadByPathHandler(fileIo, fileIoQueue)) - group.POST("/u", japicore.UploadByPathHandler(fileIo, fileIoQueue)) - group.DELETE("/delfrombulk/:filename/*location", japicore.DeleteFromBulkByPathHandler(fileIo)) - group.DELETE("/del/:filename/*location", japicore.DeleteByPathHandler(fileIo)) + group.GET("/downloadfrombulk/*location", coreSession.BasicDownloadFromBulkByPathHandler()) + group.GET("/download/*location", coreSession.BasicDownloadByPathHandler()) + group.GET("/d/*location", coreSession.BasicDownloadByPathHandler()) + + group.POST("/import", coreSession.ImportHandler()) + group.POST("/upload", coreSession.UploadByPathHandler()) + group.POST("/u", coreSession.UploadByPathHandler()) + group.DELETE("/delfrombulk/:filename/*location", coreSession.BasicDeleteFromBulkByPathHandler()) + group.DELETE("/del/:filename/*location", coreSession.BasicDeleteByPathHandler()) }) port := jutils.LoadEnvVarOrFallback("JAPI_PORT", "3535") @@ -68,8 +65,8 @@ func main() { panic(err) } - fmt.Printf("🌍 JAPI Wallet: %s\n", wallet.GetAddress()) - fmt.Printf("🌍 JAPI Network: %s\n", wallet.GetChainID()) + fmt.Printf("🌍 JAPI Wallet: %s\n", coreSession.Wallet.GetAddress()) + fmt.Printf("🌍 JAPI Network: %s\n", coreSession.Wallet.GetChainID()) fmt.Println("<<<<< * >>>>>") if errors.Is(err, http.ErrServerClosed) {