Add very basic admin page.

This commit is contained in:
jholdstock 2020-06-08 16:09:04 +01:00 committed by David Hill
parent 144060c3cb
commit b50615bc6f
12 changed files with 306 additions and 73 deletions

View File

@ -30,6 +30,8 @@ var (
versionK = []byte("version") versionK = []byte("version")
// feeXPub is the extended public key used for collecting VSP fees. // feeXPub is the extended public key used for collecting VSP fees.
feeXPubK = []byte("feeXPub") feeXPubK = []byte("feeXPub")
// cookieSecret is the secret key for initializing the cookie store.
cookieSecretK = []byte("cookieSecret")
// privatekey is the private key. // privatekey is the private key.
privateKeyK = []byte("privatekey") privateKeyK = []byte("privatekey")
// lastaddressindex is the index of the last address used for fees. // lastaddressindex is the index of the last address used for fees.
@ -103,6 +105,18 @@ func CreateNew(dbFile, feeXPub string) error {
return err return err
} }
// Generate a secret key for initializing the cookie store.
log.Info("Generating cookie secret")
secret := make([]byte, 32)
_, err = rand.Read(secret)
if err != nil {
return err
}
err = vspBkt.Put(cookieSecretK, secret)
if err != nil {
return err
}
log.Info("Storing extended public key") log.Info("Storing extended public key")
// Store fee xpub // Store fee xpub
err = vspBkt.Put(feeXPubK, []byte(feeXPub)) err = vspBkt.Put(feeXPubK, []byte(feeXPub))
@ -228,3 +242,16 @@ func (vdb *VspDatabase) GetFeeXPub() (string, error) {
return feeXPub, err return feeXPub, err
} }
func (vdb *VspDatabase) GetCookieSecret() ([]byte, error) {
var cookieSecret []byte
err := vdb.db.View(func(tx *bolt.Tx) error {
vspBkt := tx.Bucket(vspBktK)
cookieSecret = vspBkt.Get(cookieSecretK)
return nil
})
return cookieSecret, err
}

View File

@ -56,4 +56,4 @@ func TestDatabase(t *testing.T) {
} }
// TODO: Add tests for CountTickets, GetUnconfirmedTickets, GetPendingFees, // TODO: Add tests for CountTickets, GetUnconfirmedTickets, GetPendingFees,
// GetUnconfirmedFees. // GetUnconfirmedFees, GetAllTickets.

View File

@ -227,3 +227,24 @@ func (vdb *VspDatabase) GetUnconfirmedFees() ([]Ticket, error) {
return tickets, err return tickets, err
} }
func (vdb *VspDatabase) GetAllTickets() ([]Ticket, error) {
var tickets []Ticket
err := vdb.db.View(func(tx *bolt.Tx) error {
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)
return ticketBkt.ForEach(func(k, v []byte) error {
var ticket Ticket
err := json.Unmarshal(v, &ticket)
if err != nil {
return fmt.Errorf("could not unmarshal ticket: %v", err)
}
tickets = append(tickets, ticket)
return nil
})
})
return tickets, err
}

11
go.mod
View File

@ -4,16 +4,17 @@ go 1.13
require ( require (
decred.org/dcrwallet v1.2.3-0.20200519180100-f1aa4c354e05 decred.org/dcrwallet v1.2.3-0.20200519180100-f1aa4c354e05
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200528191943-a2771b8ce693 github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200607041702-62fa0661bd57
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200528191943-a2771b8ce693 github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200607041702-62fa0661bd57
github.com/decred/dcrd/dcrec v1.0.0 github.com/decred/dcrd/dcrec v1.0.0
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200528191943-a2771b8ce693 github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200607041702-62fa0661bd57
github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200528191943-a2771b8ce693 github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200607041702-62fa0661bd57
github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.1-0.20200527025017-6fc98347d984 github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.1-0.20200527025017-6fc98347d984
github.com/decred/dcrd/txscript/v3 v3.0.0-20200528191943-a2771b8ce693 github.com/decred/dcrd/txscript/v3 v3.0.0-20200607041702-62fa0661bd57
github.com/decred/dcrd/wire v1.3.0 github.com/decred/dcrd/wire v1.3.0
github.com/decred/slog v1.0.0 github.com/decred/slog v1.0.0
github.com/gin-gonic/gin v1.6.3 github.com/gin-gonic/gin v1.6.3
github.com/gorilla/sessions v1.2.0
github.com/jessevdk/go-flags v1.4.0 github.com/jessevdk/go-flags v1.4.0
github.com/jrick/bitset v1.0.0 github.com/jrick/bitset v1.0.0
github.com/jrick/logrotate v1.0.0 github.com/jrick/logrotate v1.0.0

24
go.sum
View File

@ -20,8 +20,8 @@ github.com/decred/dcrd/addrmgr v1.1.0/go.mod h1:exghL+0+QeVvO4MXezWJ1C2tcpBn3ngf
github.com/decred/dcrd/blockchain/stake/v2 v2.0.2/go.mod h1:o2TT/l/YFdrt15waUdlZ3g90zfSwlA0WgQqHV9UGJF4= github.com/decred/dcrd/blockchain/stake/v2 v2.0.2/go.mod h1:o2TT/l/YFdrt15waUdlZ3g90zfSwlA0WgQqHV9UGJF4=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:aDL94kcVJfaaJP+acWUJrlK7g7xEOqTSiFe6bSN3yRQ= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:aDL94kcVJfaaJP+acWUJrlK7g7xEOqTSiFe6bSN3yRQ=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200528191943-a2771b8ce693 h1:2Wbj27h04yq4l8KSqCJkZQnmWiKfUI4kNYaUms5oSnY= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200607041702-62fa0661bd57 h1:4/glIgVrylnAAYlpfgBmxg8dX9DHWJu/tlBagcNiXz4=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200528191943-a2771b8ce693/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200607041702-62fa0661bd57/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU=
github.com/decred/dcrd/blockchain/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0= github.com/decred/dcrd/blockchain/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0=
github.com/decred/dcrd/blockchain/standalone v1.1.0/go.mod h1:6K8ZgzlWM1Kz2TwXbrtiAvfvIwfAmlzrtpA7CVPCUPE= github.com/decred/dcrd/blockchain/standalone v1.1.0/go.mod h1:6K8ZgzlWM1Kz2TwXbrtiAvfvIwfAmlzrtpA7CVPCUPE=
github.com/decred/dcrd/blockchain/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:R9rIXU8kEJVC9Z4LAlh9bo9hiT3a+ihys3mCrz4PVao= github.com/decred/dcrd/blockchain/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:R9rIXU8kEJVC9Z4LAlh9bo9hiT3a+ihys3mCrz4PVao=
@ -34,8 +34,8 @@ github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200528191943-a2771b8ce693 h1:zTb6LJaZpYAWItJbo/XY8howiwCceEfWzHZbI06PRNI= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200607041702-62fa0661bd57 h1:agpmzwuPv/iaePlk/2MxXPU6Zz14qy2LY1kWmRCBdAQ=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200528191943-a2771b8ce693/go.mod h1:OHbKBa6UZZOXCU1Y8f9Ta3O+GShto7nB1O0nuEutKq4= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200607041702-62fa0661bd57/go.mod h1:OHbKBa6UZZOXCU1Y8f9Ta3O+GShto7nB1O0nuEutKq4=
github.com/decred/dcrd/connmgr/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:mvIMJsrOEngogmVrq+tdbPIZchHVgGnVBZeNwj1cW6E= github.com/decred/dcrd/connmgr/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:mvIMJsrOEngogmVrq+tdbPIZchHVgGnVBZeNwj1cW6E=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
@ -61,14 +61,14 @@ github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:C
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:48ZLpNNrRIYfqYxmvzMgOZrnTZUU3aTJveWtamCkOxo= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:48ZLpNNrRIYfqYxmvzMgOZrnTZUU3aTJveWtamCkOxo=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:jFxEd2LWDLvrWlrIiyx9ZGTQjvoFHZ0OVfBdyIX7jSw= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:jFxEd2LWDLvrWlrIiyx9ZGTQjvoFHZ0OVfBdyIX7jSw=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:/CDBC1SOXKrmihavgXviaTr6eVZSAWKQqEbRmacDxgg= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:/CDBC1SOXKrmihavgXviaTr6eVZSAWKQqEbRmacDxgg=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200528191943-a2771b8ce693 h1:6Sz3pijZe2cyY8y1hhTmeQDz6Bg+KheIA5Hkks1n8QY= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200607041702-62fa0661bd57 h1://D7EuW5XY8Me8uOHH2PH9APsdUQ0cm3KIyXXIcMYGc=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200528191943-a2771b8ce693/go.mod h1:85NtF/fmqL2UDf0/gLhTHG/m/0HQHwG+erQKkwWW27A= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200607041702-62fa0661bd57/go.mod h1:85NtF/fmqL2UDf0/gLhTHG/m/0HQHwG+erQKkwWW27A=
github.com/decred/dcrd/gcs/v2 v2.0.0/go.mod h1:3XjKcrtvB+r2ezhIsyNCLk6dRnXRJVyYmsd1P3SkU3o= github.com/decred/dcrd/gcs/v2 v2.0.0/go.mod h1:3XjKcrtvB+r2ezhIsyNCLk6dRnXRJVyYmsd1P3SkU3o=
github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200312171759-0a8cc56a776e h1:tBOk2P8F9JyRUSp0iRTs4nYEBro1FKBDIbg/UualLWw= github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200312171759-0a8cc56a776e h1:tBOk2P8F9JyRUSp0iRTs4nYEBro1FKBDIbg/UualLWw=
github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200312171759-0a8cc56a776e/go.mod h1:JJGd1m0DrFgV4J2J8HKNB9YVkM06ewQHT6iINis39Z4= github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200312171759-0a8cc56a776e/go.mod h1:JJGd1m0DrFgV4J2J8HKNB9YVkM06ewQHT6iINis39Z4=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200421213827-b60c60ffe98b/go.mod h1:qKN0WzeSEEZ4fUBsTwKzOPkLP7GqSM6jBUm5Auq9mrM= github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200421213827-b60c60ffe98b/go.mod h1:qKN0WzeSEEZ4fUBsTwKzOPkLP7GqSM6jBUm5Auq9mrM=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200528191943-a2771b8ce693 h1:8MKtyq4+zxRC/SEtJuF4+qB+Svd5vvHZeG9kbHxg9wY= github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200607041702-62fa0661bd57 h1:fGahtE/RfIBYlguw7tG11g1RBMBqqaZKVWFYlccYbX4=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200528191943-a2771b8ce693/go.mod h1:qKN0WzeSEEZ4fUBsTwKzOPkLP7GqSM6jBUm5Auq9mrM= github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200607041702-62fa0661bd57/go.mod h1:qKN0WzeSEEZ4fUBsTwKzOPkLP7GqSM6jBUm5Auq9mrM=
github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.0/go.mod h1:c5S+PtQWNIA2aUakgrLhrlopkMadcOv51dWhCEdo49c= github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.0/go.mod h1:c5S+PtQWNIA2aUakgrLhrlopkMadcOv51dWhCEdo49c=
github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.1-0.20200527025017-6fc98347d984 h1:xfdiilBsDinOLbglqzHH98fOO1iBtVrpSHMVpNK/2lg= github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.1-0.20200527025017-6fc98347d984 h1:xfdiilBsDinOLbglqzHH98fOO1iBtVrpSHMVpNK/2lg=
github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.1-0.20200527025017-6fc98347d984/go.mod h1:c5S+PtQWNIA2aUakgrLhrlopkMadcOv51dWhCEdo49c= github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.1-0.20200527025017-6fc98347d984/go.mod h1:c5S+PtQWNIA2aUakgrLhrlopkMadcOv51dWhCEdo49c=
@ -76,8 +76,8 @@ github.com/decred/dcrd/txscript/v2 v2.1.0/go.mod h1:XaJAVrZU4NWRx4UEzTiDAs86op1m
github.com/decred/dcrd/txscript/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:ATMA8K0SOo+M9Wdbr6dMnAd8qICJi6pXjGLlKsJc99E= github.com/decred/dcrd/txscript/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:ATMA8K0SOo+M9Wdbr6dMnAd8qICJi6pXjGLlKsJc99E=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:KsDS7McU1yFaCYR9LCIwk6YnE15YN3wJUDxhKdFqlsc= github.com/decred/dcrd/txscript/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:KsDS7McU1yFaCYR9LCIwk6YnE15YN3wJUDxhKdFqlsc=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200421213827-b60c60ffe98b/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0= github.com/decred/dcrd/txscript/v3 v3.0.0-20200421213827-b60c60ffe98b/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200528191943-a2771b8ce693 h1:8QJvuZnGXKs1x2VftugOOApMyARYH4tnu4n4o+JASZo= github.com/decred/dcrd/txscript/v3 v3.0.0-20200607041702-62fa0661bd57 h1:UaIGTrn1bpdZVgULdkwxZhULw5T5Jm3+OPJDw4+/p5c=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200528191943-a2771b8ce693/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0= github.com/decred/dcrd/txscript/v3 v3.0.0-20200607041702-62fa0661bd57/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0=
github.com/decred/dcrd/wire v1.3.0 h1:X76I2/a8esUmxXmFpJpAvXEi014IA4twgwcOBeIS8lE= github.com/decred/dcrd/wire v1.3.0 h1:X76I2/a8esUmxXmFpJpAvXEi014IA4twgwcOBeIS8lE=
github.com/decred/dcrd/wire v1.3.0/go.mod h1:fnKGlUY2IBuqnpxx5dYRU5Oiq392OBqAuVjRVSkIoXM= github.com/decred/dcrd/wire v1.3.0/go.mod h1:fnKGlUY2IBuqnpxx5dYRU5Oiq392OBqAuVjRVSkIoXM=
github.com/decred/go-socks v1.1.0/go.mod h1:sDhHqkZH0X4JjSa02oYOGhcGHYp12FsY1jQ/meV8md0= github.com/decred/go-socks v1.1.0/go.mod h1:sDhHqkZH0X4JjSa02oYOGhcGHYp12FsY1jQ/meV8md0=
@ -105,6 +105,10 @@ github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=

69
webapi/admin.go Normal file
View File

@ -0,0 +1,69 @@
package webapi
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
)
// adminPage is the handler for "GET /admin". The admin template will be
// rendered if the current session is authenticated as an admin, otherwise the
// login template will be rendered.
func adminPage(c *gin.Context) {
session := c.MustGet("session").(*sessions.Session)
admin := session.Values["admin"]
if admin == nil {
c.HTML(http.StatusUnauthorized, "login.html", gin.H{})
return
}
tickets, err := db.GetAllTickets()
if err != nil {
log.Errorf("GetAllTickets error: %v", err)
c.String(http.StatusInternalServerError, "Error getting tickets from db")
return
}
c.HTML(http.StatusOK, "admin.html", gin.H{
"Tickets": tickets,
})
}
// adminLogin is the handler for "POST /admin". If a valid password is provided,
// the current session will be authenticated as an admin.
func adminLogin(c *gin.Context) {
password := c.PostForm("password")
if password != cfg.AdminPass {
log.Warnf("Failed login attempt from %s", c.ClientIP())
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
"IncorrectPassword": true,
})
return
}
setAdminStatus(true, c)
}
// adminLogout is the handler for "POST /admin/logout". The current session will
// have its admin authentication removed.
func adminLogout(c *gin.Context) {
setAdminStatus(nil, c)
}
// setAdminStatus stores the authentication status of the current session.
func setAdminStatus(admin interface{}, c *gin.Context) {
session := c.MustGet("session").(*sessions.Session)
session.Values["admin"] = admin
err := session.Save(c.Request, c.Writer)
if err != nil {
log.Errorf("Error saving session: %v", err)
c.String(http.StatusInternalServerError, "Error saving session")
return
}
c.Redirect(http.StatusFound, "/admin")
c.Abort()
}

View File

@ -2,16 +2,51 @@ package webapi
import ( import (
"net/http" "net/http"
"strings"
"github.com/decred/vspd/rpc" "github.com/decred/vspd/rpc"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
"github.com/gorilla/sessions"
) )
type ticketHashRequest struct { type ticketHashRequest struct {
TicketHash string `json:"tickethash" binding:"required"` TicketHash string `json:"tickethash" binding:"required"`
} }
// withSession middleware adds a gorilla session to the request context for
// downstream handlers to make use of. Sessions are used by admin pages to
// maintain authentication status.
func withSession(store *sessions.CookieStore) gin.HandlerFunc {
return func(c *gin.Context) {
session, err := store.Get(c.Request, "vspd-session")
if err != nil {
// "value is not valid" occurs if the cookie secret changes. This is
// common during development (eg. when using the test harness) but
// it should not occur in production.
if strings.Contains(err.Error(), "securecookie: the value is not valid") {
log.Warn("Cookie secret has changed. Generating new session.")
// Persist the newly generated session.
err = store.Save(c.Request, c.Writer, session)
if err != nil {
log.Errorf("Error saving session: %v", err)
c.String(http.StatusInternalServerError, "Error saving session")
c.Abort()
return
}
} else {
log.Errorf("Session error: %v", err)
c.String(http.StatusInternalServerError, "Error getting session")
c.Abort()
return
}
}
c.Set("session", session)
}
}
// withDcrdClient middleware adds a dcrd client to the request // withDcrdClient middleware adds a dcrd client to the request
// context for downstream handlers to make use of. // context for downstream handlers to make use of.
func withDcrdClient() gin.HandlerFunc { func withDcrdClient() gin.HandlerFunc {

View File

@ -0,0 +1,41 @@
{{ template "header" . }}
<form action="/admin/logout" method="post">
<button type="submit">Logout</button>
</form>
<table>
<tr>
<td>Hash</td>
<td>CommitmentAddress</td>
<td>FeeAddressIndex</td>
<td>FeeAddress</td>
<td>FeeAmount</td>
<td>FeeExpiration</td>
<td>Confirmed</td>
<td>VoteChoices</td>
<td>VotingWIF</td>
<td>FeeTxHex</td>
<td>FeeTxHash</td>
<td>FeeConfirmed</td>
</tr>
{{ range .Tickets }}
<tr>
<td>{{ printf "%.10s" .Hash }}...</td>
<td>{{ printf "%.10s" .CommitmentAddress }}...</td>
<td>{{ printf "%d" .FeeAddressIndex }}</td>
<td>{{ printf "%.10s" .FeeAddress }}...</td>
<td>{{ printf "%f" .FeeAmount }}</td>
<td>{{ printf "%d" .FeeExpiration }}</td>
<td>{{ printf "%t" .Confirmed }}</td>
<td>{{ printf "%.10s" .VoteChoices }}...</td>
<td>{{ printf "%.10s" .VotingWIF }}...</td>
<td>{{ printf "%.10s" .FeeTxHex }}...</td>
<td>{{ printf "%.10s" .FeeTxHash }}...</td>
<td>{{ printf "%t" .FeeConfirmed }}</td>
</tr>
{{ end }}
</table>
</body>
</html>

View File

@ -0,0 +1,45 @@
{{define "header"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<title>vspd</title>
<link rel="stylesheet" type="text/css" href="/public/css/fonts.css" />
<link rel="stylesheet" type="text/css" href="/public/css/vspd.css" />
<!-- Custom favicon -->
<!-- Apple PWA -->
<link rel="apple-touch-icon" sizes="57x57" href="/public/images/favicon/apple-touch-icon-57x57.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="60x60" href="/public/images/favicon/apple-touch-icon-60x60.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="72x72" href="/public/images/favicon/apple-touch-icon-72x72.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="76x76" href="/public/images/favicon/apple-touch-icon-76x76.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="114x114" href="/public/images/favicon/apple-touch-icon-114x114.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="120x120" href="/public/images/favicon/apple-touch-icon-120x120.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="144x144" href="/public/images/favicon/apple-touch-icon-144x144.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="152x152" href="/public/images/favicon/apple-touch-icon-152x152.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="180x180" href="/public/images/favicon/apple-touch-icon-180x180.png?v=gT6Mc">
<!-- Browser -->
<link rel="icon" href="/public/images/favicon/favicon.ico?v=gT6Mc">
<link rel="icon" href="/public/images/favicon/favicon-32x32.png?v=gT6Mc" type="image/png" sizes="32x32">
<link rel="icon" href="/public/images/favicon/favicon-16x16.png?v=gT6Mc" type="image/png" sizes="16x16">
<!-- Android PWA -->
<link rel="manifest" href="/public/images/favicon/manifest.json?v=gT6Mc">
<!-- Safari -->
<link rel="mask-icon" href="/public/images/favicon/safari-pinned-tab.svg?v=gT6Mc" color="#091440">
<!-- Windows PWA -->
<meta name="msapplication-TileColor" content="#091440">
<meta name="msapplication-TileImage" content="/public/images/favicon/mstile-144x144.png?v=gT6Mc">
<meta name="msapplication-config" content="/public/images/favicon/browserconfig.xml?v=gT6Mc">
<!-- End custom favicon -->
</head>
<body>
<img src="/public/images/decred-logo.svg" />
{{end}}

View File

@ -1,55 +1,16 @@
<!DOCTYPE html> {{ template "header" . }}
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<title>vspd</title> <table>
<tr><td>Total tickets:</td><td>{{ .TotalTickets }}</td></tr>
<tr><td>Fee confirmed tickets:</td><td>{{ .FeeConfirmedTickets }}</td></tr>
<tr><td>VSP Fee:</td><td>{{ .VSPFee }}% of vote reward</td></tr>
<tr><td>Network:</td><td>{{ .Network }}</td></tr>
<tr><td>Support:</td><td>{{ .SupportEmail }}</td></tr>
<tr><td>Pubkey:</td><td>{{ printf "%x" .PubKey }}</td></tr>
<tr><td>VSP closed:</td><td>{{ .VspClosed }}</td></tr>
</table>
<link rel="stylesheet" type="text/css" href="/public/css/fonts.css" /> <p>Last updated: {{ .UpdateTime }}</p>
<link rel="stylesheet" type="text/css" href="/public/css/vspd.css" />
<!-- Custom favicon --> </body>
<!-- Apple PWA -->
<link rel="apple-touch-icon" sizes="57x57" href="/public/images/favicon/apple-touch-icon-57x57.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="60x60" href="/public/images/favicon/apple-touch-icon-60x60.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="72x72" href="/public/images/favicon/apple-touch-icon-72x72.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="76x76" href="/public/images/favicon/apple-touch-icon-76x76.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="114x114" href="/public/images/favicon/apple-touch-icon-114x114.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="120x120" href="/public/images/favicon/apple-touch-icon-120x120.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="144x144" href="/public/images/favicon/apple-touch-icon-144x144.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="152x152" href="/public/images/favicon/apple-touch-icon-152x152.png?v=gT6Mc">
<link rel="apple-touch-icon" sizes="180x180" href="/public/images/favicon/apple-touch-icon-180x180.png?v=gT6Mc">
<!-- Browser -->
<link rel="icon" href="/public/images/favicon/favicon.ico?v=gT6Mc">
<link rel="icon" href="/public/images/favicon/favicon-32x32.png?v=gT6Mc" type="image/png" sizes="32x32">
<link rel="icon" href="/public/images/favicon/favicon-16x16.png?v=gT6Mc" type="image/png" sizes="16x16">
<!-- Android PWA -->
<link rel="manifest" href="/public/images/favicon/manifest.json?v=gT6Mc">
<!-- Safari -->
<link rel="mask-icon" href="/public/images/favicon/safari-pinned-tab.svg?v=gT6Mc" color="#091440">
<!-- Windows PWA -->
<meta name="msapplication-TileColor" content="#091440">
<meta name="msapplication-TileImage" content="/public/images/favicon/mstile-144x144.png?v=gT6Mc">
<meta name="msapplication-config" content="/public/images/favicon/browserconfig.xml?v=gT6Mc">
<!-- End custom favicon -->
</head>
<body>
<img src="/public/images/decred-logo.svg" />
<table>
<tr><td>Total tickets:</td><td>{{ .TotalTickets }}</td></tr>
<tr><td>Fee confirmed tickets:</td><td>{{ .FeeConfirmedTickets }}</td></tr>
<tr><td>VSP Fee:</td><td>{{ .VSPFee }}% of vote reward</td></tr>
<tr><td>Network:</td><td>{{ .Network }}</td></tr>
<tr><td>Support:</td><td>{{ .SupportEmail }}</td></tr>
<tr><td>Pubkey:</td><td>{{ printf "%x" .PubKey }}</td></tr>
<tr><td>VSP closed:</td><td>{{ .VspClosed }}</td></tr>
</table>
<p>Last updated: {{.UpdateTime}}</p>
</body>
</html> </html>

View File

@ -0,0 +1,13 @@
{{ template "header" . }}
<form action="/admin" method="post">
<input type="password" name="password" required placeholder="Enter password">
<button type="submit">Login</button>
</form>
{{ if .IncorrectPassword }}
<p>Incorrect password</p>
{{ end }}
</body>
</html>

View File

@ -11,11 +11,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/decred/dcrd/chaincfg/v3"
"github.com/decred/vspd/database" "github.com/decred/vspd/database"
"github.com/decred/vspd/rpc" "github.com/decred/vspd/rpc"
"github.com/decred/dcrd/chaincfg/v3"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
) )
type Config struct { type Config struct {
@ -83,6 +83,12 @@ func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *s
return fmt.Errorf("failed to initialize fee address generator: %v", err) return fmt.Errorf("failed to initialize fee address generator: %v", err)
} }
// Get the secret key used to initialize the cookie store.
cookieSecret, err := vdb.GetCookieSecret()
if err != nil {
return fmt.Errorf("GetCookieSecret error: %v", err)
}
// Create TCP listener. // Create TCP listener.
var listenConfig net.ListenConfig var listenConfig net.ListenConfig
listener, err := listenConfig.Listen(ctx, "tcp", listen) listener, err := listenConfig.Listen(ctx, "tcp", listen)
@ -92,7 +98,7 @@ func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *s
log.Infof("Listening on %s", listen) log.Infof("Listening on %s", listen)
srv := http.Server{ srv := http.Server{
Handler: router(debugMode), Handler: router(debugMode, cookieSecret),
ReadTimeout: 5 * time.Second, // slow requests should not hold connections opened ReadTimeout: 5 * time.Second, // slow requests should not hold connections opened
WriteTimeout: 60 * time.Second, // hung responses must die WriteTimeout: 60 * time.Second, // hung responses must die
} }
@ -155,7 +161,7 @@ func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *s
return nil return nil
} }
func router(debugMode bool) *gin.Engine { func router(debugMode bool, cookieSecret []byte) *gin.Engine {
// With release mode enabled, gin will only read template files once and cache them. // With release mode enabled, gin will only read template files once and cache them.
// With release mode disabled, templates will be reloaded on the fly. // With release mode disabled, templates will be reloaded on the fly.
if !debugMode { if !debugMode {
@ -180,7 +186,7 @@ func router(debugMode bool) *gin.Engine {
router.Static("/public", "webapi/public/") router.Static("/public", "webapi/public/")
// These routes have no extra middleware. They can be accessed by anybody. // These routes have no extra middleware. They can be accessed by anybody.
router.GET("/", homepage) router.GET("", homepage)
router.GET("/api/vspinfo", vspInfo) router.GET("/api/vspinfo", vspInfo)
// These API routes access dcrd and they need authentication. // These API routes access dcrd and they need authentication.
@ -191,6 +197,16 @@ func router(debugMode bool) *gin.Engine {
feeOnly.GET("/ticketstatus", ticketStatus) feeOnly.GET("/ticketstatus", ticketStatus)
feeOnly.POST("/payfee", payFee) feeOnly.POST("/payfee", payFee)
// Create a cookie store for persisting admin session information.
cookieStore := sessions.NewCookieStore(cookieSecret)
admin := router.Group("/admin").Use(
withSession(cookieStore),
)
admin.GET("", adminPage)
admin.POST("", adminLogin)
admin.POST("/logout", adminLogout)
// These API routes access dcrd and the voting wallets, and they need // These API routes access dcrd and the voting wallets, and they need
// authentication. // authentication.
both := router.Group("/api").Use( both := router.Group("/api").Use(