From 9b7d06eb1e2db3c14179282455fd42e0d37264b0 Mon Sep 17 00:00:00 2001 From: jholdstock Date: Mon, 30 Mar 2020 13:41:48 +0100 Subject: [PATCH 1/4] gui: Implement new style errors. --- gui/account.go | 125 ++++++++++++++++++++++ gui/admin.go | 41 ++++---- gui/assets/public/css/dcrpool.css | 56 ++++------ gui/assets/public/js/modal.js | 2 +- gui/assets/templates/account.html | 10 +- gui/assets/templates/admin.html | 8 +- gui/assets/templates/header.html | 10 +- gui/assets/templates/index.html | 36 +++++-- gui/gui.go | 17 ++- gui/index.go | 169 ++++++++---------------------- 10 files changed, 264 insertions(+), 210 deletions(-) create mode 100644 gui/account.go diff --git a/gui/account.go b/gui/account.go new file mode 100644 index 00000000..79b04c71 --- /dev/null +++ b/gui/account.go @@ -0,0 +1,125 @@ +// 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" + "github.com/gorilla/mux" +) + +// accountPageData contains all of the necessary information to render the +// account template. +type accountPageData struct { + HeaderData headerData + MinedWork []*pool.AcceptedWork + Payments []*pool.Payment + Clients []*pool.ClientInfo + AccountID string + Address string + BlockExplorerURL string +} + +// Account is the handler for "GET /account/{address}". Renders the account +// template if the provided address is valid and has associated account +// information, otherwise renders the index template with an appopriate error +// message. +func (ui *GUI) Account(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 + } + + address := mux.Vars(r)["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 + } + + work, err := ui.cfg.FetchMinedWorkByAccount(accountID) + if err != nil { + ui.renderIndex(w, r, fmt.Sprintf("FetchMinedWorkByAddress error: %v", + err.Error())) + return + + } + + payments, err := ui.cfg.FetchPaymentsForAccount(accountID) + if err != nil { + ui.renderIndex(w, r, fmt.Sprintf("FetchPaymentsForAddress error: %v", + err.Error())) + return + } + + data := &accountPageData{ + HeaderData: headerData{ + CSRF: csrf.TemplateField(r), + Designation: ui.cfg.Designation, + ShowMenu: true, + }, + MinedWork: work, + Payments: payments, + Clients: ui.cfg.FetchAccountClientInfo(accountID), + AccountID: accountID, + Address: address, + BlockExplorerURL: ui.cfg.BlockExplorerURL, + } + + ui.renderTemplate(w, "account", data) +} + +// IsPoolAccount is the handler for "GET /account_exist". If the provided +// address has an account on the server a "200 OK" response is returned, +// otherwise a "400 Bad Request" is returned. +func (ui *GUI) IsPoolAccount(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 + } + + address := r.FormValue("address") + + accountID, err := pool.AccountID(address, ui.cfg.ActiveNet) + if err != nil { + http.Error(w, "Invalid address", http.StatusBadRequest) + return + } + + if !ui.cfg.AccountExists(accountID) { + http.Error(w, "Nothing found for address", http.StatusBadRequest) + return + } + + w.WriteHeader(http.StatusOK) +} diff --git a/gui/admin.go b/gui/admin.go index 2ac7104d..f10255f0 100644 --- a/gui/admin.go +++ b/gui/admin.go @@ -1,11 +1,10 @@ -// 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" @@ -13,12 +12,12 @@ import ( "github.com/decred/dcrpool/pool" ) +// 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 + Connections map[string][]*pool.ClientInfo } // AdminPage is the handler for "GET /admin". If the current session is @@ -46,22 +45,22 @@ func (ui *GUI) AdminPage(w http.ResponseWriter, r *http.Request) { 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, - } - pageData := adminPageData{ - CSRF: csrf.TemplateField(r), - Designation: ui.cfg.Designation, + HeaderData: headerData{ + CSRF: csrf.TemplateField(r), + Designation: ui.cfg.Designation, + ShowMenu: false, + }, + PoolStatsData: poolStatsData{ + 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, + }, Connections: ui.cfg.FetchClientInfo(), - PoolStats: poolStats, - Admin: true, } ui.renderTemplate(w, "admin", pageData) diff --git a/gui/assets/public/css/dcrpool.css b/gui/assets/public/css/dcrpool.css index 5201680d..49d8d33a 100644 --- a/gui/assets/public/css/dcrpool.css +++ b/gui/assets/public/css/dcrpool.css @@ -1946,7 +1946,7 @@ textarea.form-control { .btn { display: inline-block; - font-weight: 400; + font-weight: bold; text-align: center; white-space: nowrap; vertical-align: middle; @@ -2004,8 +2004,8 @@ fieldset:disabled a.btn { border-color: #005cbf; } .btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-primary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } + -webkit-box-shadow: none; + box-shadow: none; } .btn-secondary { color: #fff; @@ -4342,26 +4342,30 @@ input[type="button"].btn-block { background-color: #1b1e21; border-color: #1b1e21; } -.close { +.modal-close { float: right; font-size: 1.7rem; font-weight: 100; line-height: 1; color: #000; text-shadow: 0 1px 0 #fff; - opacity: .4; } - .close:not(:disabled):not(.disabled) { - cursor: pointer; } - .close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { - color: #000; - text-decoration: none; - opacity: .65; } - -button.close { padding: 0; background-color: transparent; border: 0; - -webkit-appearance: none; } + -webkit-appearance: none; + opacity: .4; } +.modal-close:not(:disabled):not(.disabled) { + cursor: pointer; } +.modal-close:not(:disabled):not(.disabled):hover, +.modal-close:not(:disabled):not(.disabled):focus { + color: #000; + text-decoration: none; + opacity: .65; } + +.modal-close:active, +.modal-close:focus { + outline: none; +} .modal-open { overflow: hidden; } @@ -4440,11 +4444,11 @@ button.close { bottom: 0; left: 0; z-index: 1040; - background-color: #000; } + background-color: #091440; } .modal-backdrop.fade { opacity: 0; } .modal-backdrop.show { - opacity: 0.5; } + opacity: 0.3; } .modal-header { display: -webkit-box; @@ -8334,20 +8338,6 @@ input[type="submit"]:disabled { box-shadow: 0 1px 2px 0 rgba(9, 20, 64, 0.21); } .modal-blockquote span { color: #fd704a; } - .modal-close { - padding: 0; - cursor: pointer; - -webkit-appearance: none !important; - border: 0; - background: transparent; } - .modal-close img { - vertical-align: super; - } - .modal-close:focus { - outline: none; } - @media (max-width: 1199.98px) { - .modal-close img { - width: 16px; } } .modal-header { border: 0; } .modal-dialog { @@ -8359,8 +8349,6 @@ input[type="submit"]:disabled { .modal-header--long-title .modal-title { max-width: 60%; text-align: right; } - .modal-header--long-title .modal-close { - margin: -20px 0 4px 0; } .modal-header--long-title img { float: right; } .modal-header--long-title span { @@ -8381,7 +8369,7 @@ input[type="submit"]:disabled { font-size: 28px; line-height: 1.26; letter-spacing: normal; - color: #48566e; } + color: #3D5873; } @media (max-width: 767.98px) { .modal-title { font-size: 18px; } } @@ -8394,7 +8382,7 @@ input[type="submit"]:disabled { max-height: 100vh; } } .modal-body p { font-size: 16px; - color: #48566e; } + color: #091440; } .modal-body strong { color: #091440; margin-bottom: 1rem; diff --git a/gui/assets/public/js/modal.js b/gui/assets/public/js/modal.js index f25c7fdb..68af4be0 100644 --- a/gui/assets/public/js/modal.js +++ b/gui/assets/public/js/modal.js @@ -48,7 +48,7 @@ $("#account-form").on("submit", function (e) { type: $(this).attr('method'), data: $(this).serialize(), success: function() { - window.location.replace("/?address="+address); + window.location.replace("/account/"+address); }, error: function(response) { if (response.status === 400 || response.status === 429) { diff --git a/gui/assets/templates/account.html b/gui/assets/templates/account.html index 8a793bd9..3a2790cd 100644 --- a/gui/assets/templates/account.html +++ b/gui/assets/templates/account.html @@ -1,5 +1,5 @@ {{define "account"}} -{{template "header" .}} +{{template "header" .HeaderData}}