github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/tslib/establish.go (about) 1 package tslib 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "net/url" 9 "os" 10 "path" 11 "time" 12 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors" 15 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/debug" 17 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" 18 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/manifest" 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/sigintTrap" 21 ) 22 23 func EstablishTimestamps(chain string, publisher base.Address) error { 24 tsPath := config.PathToTimestamps(chain) 25 if file.FileExists(tsPath) { 26 return nil 27 } 28 29 database := chain + "-ts" 30 cid, err := manifest.ReadUnchainedIndex(chain, publisher, database) 31 if err != nil { 32 return err 33 } else if len(cid) == 0 { 34 return fmt.Errorf("no record found in the Unchained Index for database %s from publisher %s", database, publisher.Hex()) 35 } 36 37 tmpTsPath := tsPath + ".tmp" 38 if err, canceled := downloadTimestamps(chain, database, tmpTsPath, cid); err != nil || canceled { 39 os.Remove(tmpTsPath) 40 return err 41 } 42 43 os.Remove(tsPath) 44 _ = os.Rename(tmpTsPath, tsPath) 45 46 return err 47 } 48 49 // downloadTimestamps downloads a CID to a binary file 50 func downloadTimestamps(chain, database, outputFn, cid string) (error, bool) { 51 gatewayUrl := config.GetChain(chain).IpfsGateway 52 53 url, err := url.Parse(gatewayUrl) 54 if err != nil { 55 return err, false 56 } 57 url.Path = path.Join(url.Path, cid) 58 59 debug.DebugCurlStr(url.String()) 60 61 logger.InfoTable("Chain:", chain) 62 logger.InfoTable("Database:", database) 63 logger.InfoTable("Gateway:", gatewayUrl) 64 logger.InfoTable("URL:", url.String()) 65 logger.InfoTable("CID:", cid) 66 logger.Info(fmt.Sprintf("%sDownloading database %s (%s). This may take a moment...%s", colors.Yellow, database, cid, colors.Off)) 67 68 header, err := http.Head(url.String()) 69 if err != nil { 70 return err, false 71 } 72 if header.StatusCode != 200 { 73 return fmt.Errorf("CID not found: %d status", header.StatusCode), false 74 } 75 76 if header.ContentLength <= file.FileSize(outputFn) { 77 // The file on disc is larger than the one we will download which means it has more 78 // timestamps in it, so we don't download it. 79 return nil, false 80 } 81 82 //Get the response bytes from the url 83 response, err := http.Get(url.String()) 84 if err != nil { 85 return err, false 86 } 87 defer response.Body.Close() 88 if response.StatusCode != 200 { 89 return fmt.Errorf("CID not found: %d status", header.StatusCode), false 90 } 91 92 logger.Info(fmt.Sprintf("%sDownloading complete %s (%s). Writing file...%s", colors.Yellow, database, cid, colors.Off)) 93 94 userHitCtrlC := false 95 ctx, cancel := context.WithCancel(context.Background()) 96 cleanOnQuit := func() { 97 userHitCtrlC = true 98 logger.Warn(sigintTrap.TrapMessage) 99 } 100 101 go func() { 102 for { 103 if file.FileSize(outputFn) >= header.ContentLength { 104 break 105 } 106 pct := int(float64(file.FileSize(outputFn)) / float64(header.ContentLength) * 100) 107 msg := colors.Yellow + fmt.Sprintf("Downloading timestamps. Please wait... %d%%", pct) + colors.Off 108 if userHitCtrlC { 109 msg = colors.Yellow + fmt.Sprintf("Finishing work. please wait... %d%% ", pct) + colors.Off 110 cancel() 111 } 112 logger.Progress(true, msg) 113 time.Sleep(500 * time.Microsecond) 114 } 115 }() 116 117 trapChannel := sigintTrap.Enable(ctx, cancel, cleanOnQuit) 118 defer sigintTrap.Disable(trapChannel) 119 120 ff, err := os.OpenFile(outputFn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 121 if err != nil { 122 return err, userHitCtrlC 123 } 124 defer ff.Close() 125 126 _, err = io.Copy(ff, response.Body) 127 return err, userHitCtrlC 128 }