diff --git a/Taskfile.yml b/Taskfile.yml index b6532f0..f49e14c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,4 +1,4 @@ -version: '3' +version: "3" tasks: install: diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 1bd215d..5a6b814 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -9,6 +9,7 @@ import ( "github.com/massalabs/DeWeb/int/utils" "github.com/massalabs/DeWeb/int/zipper" pkgConfig "github.com/massalabs/DeWeb/pkg/config" + "github.com/massalabs/DeWeb/pkg/webmanager" "github.com/massalabs/DeWeb/pkg/website" "github.com/massalabs/station/pkg/logger" "github.com/urfave/cli/v2" @@ -164,38 +165,27 @@ func processFileForUpload(filepath string) ([][]byte, error) { return website.DivideIntoChunks(websiteBytes, website.ChunkSize), nil } -func viewWebsite(address string, config *pkgConfig.Config) error { - owner, err := website.GetOwner(&config.NetworkInfos, address) +func viewWebsite(scAddress string, config *pkgConfig.Config) error { + owner, err := website.GetOwner(&config.NetworkInfos, scAddress) if err != nil { - logger.Warnf("failed to get owner of %s: %v", address, err) + logger.Warnf("failed to get owner of %s: %v", scAddress, err) } logger.Infof("Website owner: %s", owner) - websiteBytes, err := website.Fetch(&config.NetworkInfos, address) + zipFile, err := webmanager.RequestWebsite(scAddress, &config.NetworkInfos) if err != nil { - return fmt.Errorf("failed to fetch website: %v", err) + return fmt.Errorf("failed to request website: %v", err) } - logger.Infof("Website fetched successfully with size: %d", len(websiteBytes)) - - outputZipPath := fmt.Sprintf("website_%s.zip", address) - - err = os.WriteFile(outputZipPath, websiteBytes, 0o644) - if err != nil { - return fmt.Errorf("failed to write website zip file %v", err) - } - - logger.Infof("Website successfully written to file: %s", outputZipPath) - fileName := "index.html" - content, err := zipper.GetFileFromZip(outputZipPath, fileName) + indexFile, err := zipper.ReadFileFromZip(zipFile, fileName) if err != nil { return fmt.Errorf("failed to get file %s from zip: %v", fileName, err) } - logger.Infof("%s content:\n %s", fileName, content) + logger.Infof("viewing content for %s:\n %s", scAddress, indexFile) return nil } diff --git a/int/zipper/zipper.go b/int/zipper/zipper.go index fa7ac24..9642692 100644 --- a/int/zipper/zipper.go +++ b/int/zipper/zipper.go @@ -2,35 +2,36 @@ package zipper import ( "archive/zip" + "bytes" "fmt" "io" ) -// GetFileFromZip returns the content of the given file from the zip file. -// It returns an error if the file is not found in the zip. -func GetFileFromZip(zipFilePath, fileName string) ([]byte, error) { - zipReader, err := zip.OpenReader(zipFilePath) +// ReadIndexFromZip returns the content of the desired file from the given zip file. +func ReadFileFromZip(zipFile []byte, fileName string) ([]byte, error) { + reader := bytes.NewReader(zipFile) + + zipReader, err := zip.NewReader(reader, int64(reader.Len())) if err != nil { - return []byte{}, fmt.Errorf("failed to open zip file: %w", err) + return nil, fmt.Errorf("failed to initiate reader: %v", err) } - defer zipReader.Close() for _, file := range zipReader.File { if file.Name == fileName { rc, err := file.Open() if err != nil { - return []byte{}, fmt.Errorf("failed to open file in zip: %w", err) + return nil, fmt.Errorf("failed to open file in zip: %w", err) } defer rc.Close() buf, err := io.ReadAll(rc) if err != nil { - return []byte{}, fmt.Errorf("failed to read file in zip: %w", err) + return nil, fmt.Errorf("failed to read file in zip: %w", err) } return buf, nil } } - return []byte{}, fmt.Errorf("%s not found in zip", fileName) + return nil, fmt.Errorf("%s not found in zip", fileName) } diff --git a/pkg/webmanager/manager.go b/pkg/webmanager/manager.go new file mode 100644 index 0000000..378efcb --- /dev/null +++ b/pkg/webmanager/manager.go @@ -0,0 +1,78 @@ +package webmanager + +import ( + "fmt" + "os" + "time" + + "github.com/massalabs/DeWeb/pkg/website" + "github.com/massalabs/station/int/config" + "github.com/massalabs/station/pkg/cache" + "github.com/massalabs/station/pkg/logger" +) + +// RequestWebsite fetches a website and caches it, or retrieves it from the cache if already present. +func RequestWebsite(scAddress string, networkInfo *config.NetworkInfos) ([]byte, error) { + cache := new(cache.Cache) + fileName := fmt.Sprintf("website_%s.zip", scAddress) + + // TODO: get last updated from callSc + lastUpdated := time.Now() + + if cache.IsPresent(fileName) { + logger.Debugf("Website %s present in cache", scAddress) + + isOutdated, err := isFileOutdated(fileName, lastUpdated) + if err != nil { + return nil, fmt.Errorf("failed to check if file is outdated: %w", err) + } + + if !isOutdated { + content, err := cache.Read(fileName) + if err != nil { + return nil, fmt.Errorf("failed to read cached website: %w", err) + } + + return content, nil + } + + logger.Warnf("website %s is outdated, fetching...", fileName) + } + + logger.Debugf("Website %s not found in cache or not up to date, fetching...", scAddress) + + websiteBytes, err := fetchAndCache(networkInfo, scAddress, cache, fileName) + if err != nil { + return nil, fmt.Errorf("failed to fetch and cache website: %w", err) + } + + return websiteBytes, nil +} + +// Fetches the website and saves it to the cache. +func fetchAndCache(networkInfo *config.NetworkInfos, scAddress string, cache *cache.Cache, fileName string) ([]byte, error) { + websiteBytes, err := website.Fetch(networkInfo, scAddress) + if err != nil { + return nil, fmt.Errorf("failed to fetch website: %w", err) + } + + logger.Debugf("Website fetched successfully with size: %d bytes", len(websiteBytes)) + + if err := cache.Save(fileName, websiteBytes); err != nil { + return nil, fmt.Errorf("failed to save website to cache: %w", err) + } + + logger.Infof("Website successfully written to cache at: %s", fileName) + + return websiteBytes, nil +} + +// Compares the last updated timestamp to the file's last modified timestamp. +func isFileOutdated(fileName string, lastUpdated time.Time) (bool, error) { + fi, err := os.Stat("./websitesCache/" + fileName) + if err != nil { + return false, fmt.Errorf("failed to get file info: %w", err) + } + + return fi.ModTime().Before(lastUpdated), nil +}