2019-11-16 19:47:06 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
rice "github.com/GeertJohan/go.rice"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
"html/template"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/api"
|
|
|
|
)
|
|
|
|
|
|
|
|
type handler struct {
|
|
|
|
api api.FullNode
|
|
|
|
st *storage
|
|
|
|
site *rice.Box
|
|
|
|
assets http.Handler
|
|
|
|
|
|
|
|
templates map[string]*template.Template
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHandler(api api.FullNode, st *storage) (*handler, error) {
|
|
|
|
h := &handler{
|
|
|
|
api: api,
|
|
|
|
st: st,
|
|
|
|
site: rice.MustFindBox("site"),
|
|
|
|
|
|
|
|
templates: map[string]*template.Template{},
|
|
|
|
}
|
|
|
|
h.assets = http.FileServer(h.site.HTTPBox())
|
|
|
|
|
|
|
|
funcs := template.FuncMap{
|
|
|
|
"count": h.count,
|
|
|
|
"countCol": h.countCol,
|
|
|
|
"sum": h.sum,
|
|
|
|
"netPower": h.netPower,
|
|
|
|
"queryNum": h.queryNum,
|
|
|
|
"sizeStr": sizeStr,
|
|
|
|
"strings": h.strings,
|
|
|
|
"param": func(string) string { return "" }, // replaced in request handler
|
|
|
|
}
|
|
|
|
|
|
|
|
base := template.New("")
|
|
|
|
|
|
|
|
base.Funcs(funcs)
|
|
|
|
|
2019-11-16 19:51:54 +00:00
|
|
|
return h, h.site.Walk("", func(path string, info os.FileInfo, err error) error {
|
2019-11-16 19:47:06 +00:00
|
|
|
if filepath.Ext(path) != ".html" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Info(path)
|
|
|
|
|
|
|
|
h.templates["/"+path], err = base.New(path).Parse(h.site.MustString(path))
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
|
h, err := newHandler(h.api, h.st) // for faster dev
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
p := r.URL.Path
|
|
|
|
if p == "/" {
|
|
|
|
p = "/index.html"
|
|
|
|
}
|
|
|
|
|
|
|
|
t, ok := h.templates[p]
|
|
|
|
if !ok {
|
|
|
|
h.assets.ServeHTTP(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
t, err = t.Clone()
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Funcs(map[string]interface{}{
|
|
|
|
"param": r.FormValue,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err := t.Execute(w, nil); err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Info(r.URL.Path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Template funcs
|
|
|
|
|
|
|
|
func (h *handler) count(table string, filters ...string) (int, error) {
|
|
|
|
// explicitly not caring about sql injection too much, this doesn't take user input
|
|
|
|
|
|
|
|
filts := ""
|
|
|
|
if len(filters) > 0 {
|
|
|
|
filts = " where "
|
|
|
|
for _, filter := range filters {
|
|
|
|
filts += filter + " and "
|
|
|
|
}
|
|
|
|
filts = filts[:len(filts)-5]
|
|
|
|
}
|
|
|
|
|
|
|
|
var c int
|
|
|
|
err := h.st.db.QueryRow("select count(1) from " + table + filts).Scan(&c)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) countCol(table string, col string, filters ...string) (int, error) {
|
|
|
|
// explicitly not caring about sql injection too much, this doesn't take user input
|
|
|
|
|
|
|
|
filts := ""
|
|
|
|
if len(filters) > 0 {
|
|
|
|
filts = " where "
|
|
|
|
for _, filter := range filters {
|
|
|
|
filts += filter + " and "
|
|
|
|
}
|
|
|
|
filts = filts[:len(filts)-5]
|
|
|
|
}
|
|
|
|
|
|
|
|
var c int
|
|
|
|
err := h.st.db.QueryRow("select count(distinct " + col + ") from " + table + filts).Scan(&c)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) sum(table string, col string) (types.BigInt, error) {
|
|
|
|
return h.queryNum("select sum(cast(" + col + " as bigint)) from " + table)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) netPower(slashFilt string) (types.BigInt, error) {
|
|
|
|
if slashFilt != "" {
|
|
|
|
slashFilt = " where " + slashFilt
|
|
|
|
}
|
|
|
|
return h.queryNum(`select sum(power) from (
|
|
|
|
select miner_heads.power, miner_heads.slashed_at, max(height) from miner_heads
|
|
|
|
inner join blocks b on miner_heads.stateroot = b.parentStateRoot
|
|
|
|
group by miner_heads.addr)` + slashFilt)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) queryNum(q string, p ...interface{}) (types.BigInt, error) {
|
|
|
|
// explicitly not caring about sql injection too much, this doesn't take user input
|
|
|
|
|
|
|
|
var c string
|
|
|
|
err := h.st.db.QueryRow(q, p...).Scan(&c)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("qnum ", q, p, err)
|
|
|
|
return types.NewInt(0), err
|
|
|
|
}
|
|
|
|
|
|
|
|
i := types.NewInt(0)
|
|
|
|
_, ok := i.SetString(c, 10)
|
|
|
|
if !ok {
|
|
|
|
return types.NewInt(0), xerrors.New("num parse error: " + c)
|
|
|
|
}
|
|
|
|
return i, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var units = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"}
|
|
|
|
|
|
|
|
func sizeStr(size types.BigInt) string {
|
|
|
|
size = types.BigMul(size, types.NewInt(100))
|
|
|
|
i := 0
|
|
|
|
for types.BigCmp(size, types.NewInt(102400)) >= 0 && i < len(units)-1 {
|
|
|
|
size = types.BigDiv(size, types.NewInt(1024))
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s.%s %s", types.BigDiv(size, types.NewInt(100)), types.BigMod(size, types.NewInt(100)), units[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) strings(table string, col string, filter string, args ...interface{}) (out []string, err error) {
|
|
|
|
if len(filter) > 0 {
|
|
|
|
filter = " where " + filter
|
|
|
|
}
|
|
|
|
log.Info("strings qstr ", "select "+col+" from "+table+filter)
|
|
|
|
rws, err := h.st.db.Query("select "+col+" from "+table+filter, args...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for rws.Next() {
|
|
|
|
var r string
|
|
|
|
if err := rws.Scan(&r); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
out = append(out, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ http.Handler = &handler{}
|