diff --git a/database/database.go b/database/database.go index 8e3869b..d9be243 100644 --- a/database/database.go +++ b/database/database.go @@ -31,7 +31,7 @@ var ( privateKeyK = []byte("privatekey") ) -// Open initialises and returns an open database. If no database file is found +// Open initializes and returns an open database. If no database file is found // at the provided path, a new one will be created. func Open(ctx context.Context, shutdownWg *sync.WaitGroup, dbFile string) (*VspDatabase, error) { @@ -61,14 +61,14 @@ func Open(ctx context.Context, shutdownWg *sync.WaitGroup, dbFile string) (*VspD // Create all storage buckets of the VSP if they don't already exist. err = db.Update(func(tx *bolt.Tx) error { if tx.Bucket(vspBktK) == nil { - log.Debug("Initialising new database") + log.Debug("Initializing new database") // Create parent bucket. vspBkt, err := tx.CreateBucket(vspBktK) if err != nil { return fmt.Errorf("failed to create %s bucket: %v", string(vspBktK), err) } - // Initialise with database version 1. + // Initialize with database version 1. vbytes := make([]byte, 4) binary.LittleEndian.PutUint32(vbytes, uint32(1)) err = vspBkt.Put(versionK, vbytes) diff --git a/database/ticket.go b/database/ticket.go index 08c802d..4b9f9b6 100644 --- a/database/ticket.go +++ b/database/ticket.go @@ -155,3 +155,31 @@ func (vdb *VspDatabase) UpdateExpireAndFee(ticketHash string, expiration int64, return ticketBkt.Put(hashBytes, ticketBytes) }) } + +func (vdb *VspDatabase) CountTickets() (int, int, error) { + var total, feePaid int + err := vdb.db.View(func(tx *bolt.Tx) error { + ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK) + + return ticketBkt.ForEach(func(k, v []byte) error { + total++ + var ticket Ticket + err := json.Unmarshal(v, &ticket) + if err != nil { + return fmt.Errorf("could not unmarshal ticket: %v", err) + } + + if ticket.FeeTxHash != "" { + feePaid++ + } + + return nil + }) + }) + + if err != nil { + return 0, 0, err + } + + return total, feePaid, nil +} diff --git a/main.go b/main.go index a45c58a..931ee6d 100644 --- a/main.go +++ b/main.go @@ -39,7 +39,7 @@ func run(ctx context.Context) error { // Load config file and parse CLI args. cfg, err := loadConfig() if err != nil { - // Don't use logger here because it may not be initialised. + // Don't use logger here because it may not be initialized. fmt.Fprintf(os.Stderr, "Config error: %v\n", err) return err } @@ -120,7 +120,7 @@ func run(ctx context.Context) error { } err = webapi.Start(ctx, shutdownRequestChannel, &shutdownWg, cfg.Listen, db, feeWalletConnect, votingWalletConnect, cfg.WebServerDebug, apiCfg) if err != nil { - log.Errorf("Failed to initialise webapi: %v", err) + log.Errorf("Failed to initialize webapi: %v", err) requestShutdown() shutdownWg.Wait() return err diff --git a/webapi/templates/homepage.html b/webapi/templates/homepage.html index e9eaec2..47caa4c 100644 --- a/webapi/templates/homepage.html +++ b/webapi/templates/homepage.html @@ -11,5 +11,12 @@

{{ .Message }}

+ + + + + +
Total tickets:{{ .TotalTickets }}
FeePaid tickets:{{ .FeePaidTickets }}
VSP Fee:{{ .VSPFee }}
Network:{{ .Network }}
+

Last updated: {{.UpdateTime}}

\ No newline at end of file diff --git a/webapi/webapi.go b/webapi/webapi.go index 04e577c..46830cc 100644 --- a/webapi/webapi.go +++ b/webapi/webapi.go @@ -5,6 +5,7 @@ import ( "crypto/ed25519" "encoding/hex" "encoding/json" + "fmt" "net" "net/http" "sync" @@ -26,6 +27,8 @@ type Config struct { FeeAddressExpiration time.Duration } +var homepageData *gin.H + var cfg Config var db *database.VspDatabase var feeWalletConnect rpc.Connect @@ -34,6 +37,13 @@ var votingWalletConnect rpc.Connect func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *sync.WaitGroup, listen string, vdb *database.VspDatabase, fWalletConnect rpc.Connect, vWalletConnect rpc.Connect, debugMode bool, config Config) error { + // Populate template data before starting webserver. + var err error + homepageData, err = updateHomepageData(vdb, config) + if err != nil { + return fmt.Errorf("could not initialize homepage data: %v", err) + } + // Create TCP listener. var listenConfig net.ListenConfig listener, err := listenConfig.Listen(ctx, "tcp", listen) @@ -78,6 +88,31 @@ func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *s } }() + // Use a ticker to update template data. + var refresh time.Duration + if debugMode { + refresh = 1 * time.Second + } else { + refresh = 5 * time.Minute + } + shutdownWg.Add(1) + go func() { + ticker := time.NewTicker(refresh) + for { + select { + case <-ctx.Done(): + ticker.Stop() + shutdownWg.Done() + return + case <-ticker.C: + homepageData, err = updateHomepageData(db, cfg) + if err != nil { + log.Errorf("Failed to update homepage data: %v", err) + } + } + } + }() + cfg = config db = vdb feeWalletConnect = fWalletConnect @@ -125,10 +160,23 @@ func router(debugMode bool) *gin.Engine { return router } +func updateHomepageData(db *database.VspDatabase, cfg Config) (*gin.H, error) { + total, feePaid, err := db.CountTickets() + if err != nil { + return nil, err + } + return &gin.H{ + "Message": "Welcome to dcrvsp!", + "TotalTickets": total, + "FeePaidTickets": feePaid, + "VSPFee": cfg.VSPFee, + "Network": cfg.NetParams.Name, + "UpdateTime": time.Now().Format("Mon Jan _2 15:04:05 2006"), + }, nil +} + func homepage(c *gin.Context) { - c.HTML(http.StatusOK, "homepage.html", gin.H{ - "Message": "Welcome to dcrvsp!", - }) + c.HTML(http.StatusOK, "homepage.html", homepageData) } func sendJSONResponse(resp interface{}, c *gin.Context) {