Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce cache and more pagination. #180

Merged
merged 13 commits into from
Apr 16, 2020
Merged
5 changes: 1 addition & 4 deletions dcrpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,10 @@ func newPool(cfg *config) (*miningPool, error) {
AddPaymentRequest: p.hub.AddPaymentRequest,
FetchMinedWork: p.hub.FetchMinedWork,
FetchWorkQuotas: p.hub.FetchWorkQuotas,
FetchPoolHashRate: p.hub.FetchPoolHashRate,
BackupDB: p.hub.BackupDB,
FetchClientInfo: p.hub.FetchClientInfo,
FetchClients: p.hub.FetchClients,
AccountExists: p.hub.AccountExists,
FetchMinedWorkByAccount: p.hub.FetchMinedWorkByAccount,
FetchPaymentsForAccount: p.hub.FetchPaymentsForAccount,
FetchAccountClientInfo: p.hub.FetchAccountClientInfo,
}
p.gui, err = gui.NewGUI(gcfg)
if err != nil {
Expand Down
111 changes: 111 additions & 0 deletions gui/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) 2020 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package gui

import (
"fmt"
"net/http"

"github.com/decred/dcrpool/pool"
"github.com/gorilla/csrf"
)

// accountPageData contains all of the necessary information to render the
// account template.
type accountPageData struct {
HeaderData headerData
MinedWork []minedWork
Payments []*pool.Payment
ConnectedClients []client
AccountID string
Address string
BlockExplorerURL string
}

// Account is the handler for "GET /account". Renders the account template if
// a valid address with associated account information is provided,
// otherwise renders the index template with an appropriate error message.
func (ui *GUI) Account(w http.ResponseWriter, r *http.Request) {

address := r.FormValue("address")
if address == "" {
ui.renderIndex(w, r, "No address provided")
return
}

// Generate the account id of the provided address.
accountID, err := pool.AccountID(address, ui.cfg.ActiveNet)
if err != nil {
ui.renderIndex(w, r, "Unable to generate account ID for address")
return
}

if !ui.cfg.AccountExists(accountID) {
ui.renderIndex(w, r, "Nothing found for address")
return
}

// Get the most recently mined blocks by this account (max 10).
allWork := ui.cache.getMinedWork()
recentWork := make([]minedWork, 0)
for _, v := range allWork {
if v.AccountID == accountID {
recentWork = append(recentWork, v)
if len(recentWork) >= 10 {
break
}
}
}

payments, err := ui.cfg.FetchPaymentsForAccount(accountID)
if err != nil {
ui.renderIndex(w, r, fmt.Sprintf("FetchPaymentsForAddress error: %v",
err.Error()))
return
}

// Get this accounts connected clients (max 10).
clients := ui.cache.getClients()[accountID]
if len(clients) > 10 {
clients = clients[0:10]
}

data := &accountPageData{
HeaderData: headerData{
CSRF: csrf.TemplateField(r),
Designation: ui.cfg.Designation,
ShowMenu: true,
},
MinedWork: recentWork,
Payments: payments,
ConnectedClients: clients,
AccountID: accountID,
Address: address,
BlockExplorerURL: ui.cfg.BlockExplorerURL,
}

ui.renderTemplate(w, "account", data)
}

// IsPoolAccount is the handler for "HEAD /account". If the provided
// address has an account on the server a "200 OK" response is returned,
// otherwise a "400 Bad Request" or "404 Not Found" are returned.
func (ui *GUI) IsPoolAccount(w http.ResponseWriter, r *http.Request) {

address := r.FormValue("address")

accountID, err := pool.AccountID(address, ui.cfg.ActiveNet)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}

if !ui.cfg.AccountExists(accountID) {
w.WriteHeader(http.StatusNotFound)
return
}

w.WriteHeader(http.StatusOK)
}
99 changes: 30 additions & 69 deletions gui/admin.go
Original file line number Diff line number Diff line change
@@ -1,67 +1,53 @@
// Copyright (c) 2019 The Decred developers
// Copyright (c) 2020 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package gui

import (
"html/template"
"net/http"

"github.com/gorilla/csrf"

"github.com/decred/dcrpool/pool"
"github.com/gorilla/sessions"
)

// adminPageData contains all of the necessary information to render the admin
// template.
type adminPageData struct {
Connections map[string][]*pool.ClientInfo
CSRF template.HTML
Designation string
PoolStats poolStats
Admin bool
HeaderData headerData
PoolStatsData poolStatsData
ConnectedClients map[string][]client
}

// AdminPage is the handler for "GET /admin". If the current session is
// authenticated as an admin, the admin.html template is rendered, otherwise
// returns a redirection to the homepage.
func (ui *GUI) AdminPage(w http.ResponseWriter, r *http.Request) {
session, err := getSession(r, ui.cookieStore)
if err != nil {
log.Errorf("getSession error: %v", err)
http.Error(w, "Session error", http.StatusInternalServerError)
return
}

if !ui.cfg.WithinLimit(session.ID, pool.APIClient) {
http.Error(w, "Request limit exceeded", http.StatusTooManyRequests)
return
}
session := r.Context().Value(sessionKey).(*sessions.Session)

if session.Values["IsAdmin"] != true {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}

ui.poolHashMtx.RLock()
poolHash := ui.poolHash
ui.poolHashMtx.RUnlock()

poolStats := poolStats{
LastWorkHeight: ui.cfg.FetchLastWorkHeight(),
LastPaymentHeight: ui.cfg.FetchLastPaymentHeight(),
PoolHashRate: poolHash,
PaymentMethod: ui.cfg.PaymentMethod,
Network: ui.cfg.ActiveNet.Name,
PoolFee: ui.cfg.PoolFee,
SoloPool: ui.cfg.SoloPool,
}
clients := ui.cache.getClients()

pageData := adminPageData{
CSRF: csrf.TemplateField(r),
Designation: ui.cfg.Designation,
Connections: ui.cfg.FetchClientInfo(),
PoolStats: poolStats,
Admin: true,
HeaderData: headerData{
CSRF: csrf.TemplateField(r),
Designation: ui.cfg.Designation,
ShowMenu: false,
},
PoolStatsData: poolStatsData{
LastWorkHeight: ui.cfg.FetchLastWorkHeight(),
LastPaymentHeight: ui.cfg.FetchLastPaymentHeight(),
PoolHashRate: ui.cache.getPoolHash(),
PaymentMethod: ui.cfg.PaymentMethod,
Network: ui.cfg.ActiveNet.Name,
PoolFee: ui.cfg.PoolFee,
SoloPool: ui.cfg.SoloPool,
},
ConnectedClients: clients,
}

ui.renderTemplate(w, "admin", pageData)
Expand All @@ -71,17 +57,7 @@ func (ui *GUI) AdminPage(w http.ResponseWriter, r *http.Request) {
// supplied, the session is authenticated and a "200 OK" response is returned,
// otherwise a "401 Unauthorized" response is returned.
func (ui *GUI) AdminLogin(w http.ResponseWriter, r *http.Request) {
session, err := getSession(r, ui.cookieStore)
if err != nil {
log.Errorf("getSession error: %v", err)
http.Error(w, "Session error", http.StatusInternalServerError)
return
}

if !ui.cfg.WithinLimit(session.ID, pool.APIClient) {
http.Error(w, "Request limit exceeded", http.StatusTooManyRequests)
return
}
session := r.Context().Value(sessionKey).(*sessions.Session)

pass := r.FormValue("password")

Expand All @@ -92,7 +68,7 @@ func (ui *GUI) AdminLogin(w http.ResponseWriter, r *http.Request) {
}

session.Values["IsAdmin"] = true
err = session.Save(r, w)
err := session.Save(r, w)
if err != nil {
log.Errorf("unable to save session: %v", err)
return
Expand All @@ -105,15 +81,10 @@ func (ui *GUI) AdminLogin(w http.ResponseWriter, r *http.Request) {
// removed from the current session and the request is redirected to the
// homepage handler.
func (ui *GUI) AdminLogout(w http.ResponseWriter, r *http.Request) {
session, err := getSession(r, ui.cookieStore)
if err != nil {
log.Errorf("getSession error: %v", err)
http.Error(w, "Session error", http.StatusInternalServerError)
return
}
session := r.Context().Value(sessionKey).(*sessions.Session)

session.Values["IsAdmin"] = false
err = session.Save(r, w)
err := session.Save(r, w)
if err != nil {
log.Errorf("unable to save session: %v", err)
return
Expand All @@ -126,24 +97,14 @@ func (ui *GUI) AdminLogout(w http.ResponseWriter, r *http.Request) {
// session is authenticated as an admin, a binary representation of the whole
// database is generated and returned to the client.
func (ui *GUI) DownloadDatabaseBackup(w http.ResponseWriter, r *http.Request) {
session, err := getSession(r, ui.cookieStore)
if err != nil {
log.Errorf("getSession error: %v", err)
http.Error(w, "Session error", http.StatusInternalServerError)
return
}

if !ui.cfg.WithinLimit(session.ID, pool.APIClient) {
http.Error(w, "Request limit exceeded", http.StatusTooManyRequests)
return
}
session := r.Context().Value(sessionKey).(*sessions.Session)

if session.Values["IsAdmin"] != true {
http.Error(w, "Not authenticated", http.StatusUnauthorized)
return
}

err = ui.cfg.BackupDB(w)
err := ui.cfg.BackupDB(w)
if err != nil {
log.Errorf("Error backing up database: %v", err)
http.Error(w, "Error backing up database: "+err.Error(),
Expand Down
Loading