package stats import ( "bytes" "git.sophuwu.com/manweb/CFG" "git.sophuwu.com/manweb/logs" "github.com/asdine/storm/v3" "go.etcd.io/bbolt" "html/template" "path/filepath" "strings" ) var DB *storm.DB func OpenDB() { if !CFG.EnableStats || CFG.StatisticDB == "" { return } var err error DB, err = storm.Open(CFG.StatisticDB, storm.BoltOptions(0660, &bbolt.Options{ Timeout: 1000 * 1000 * 1000, // 1 second })) logs.CheckFatal("failed to open statistics database", err) } func CloseDB() { if DB == nil { return } err := DB.Close() logs.CheckFatal("failed to close statistics database", err) DB = nil } type Stat struct { Query string `storm:"id,unique"` // The query string Count int } func count(query string) { s := &Stat{Query: query} err := DB.One("Query", query, s) if err != nil { s = &Stat{ Query: query, Count: 1, } DB.Save(s) return } s.Count++ DB.Update(s) } func Count(query string) { if !CFG.EnableStats || DB == nil { return } go count(query) } type Special struct { Page string `storm:"id,unique"` // The page name Count int } func countSpecial(page string) { s := &Special{Page: page} err := DB.One("Page", page, s) if err != nil { s = &Special{ Page: page, Count: 1, } DB.Save(s) return } s.Count++ DB.Update(s) } func SpecialCount(page string) { if !CFG.EnableStats || DB == nil { return } go countSpecial(page) } func GetStats() ([]*Stat, error) { var stats []*Stat err := DB.Select().OrderBy("Count").Reverse().Find(&stats) if err != nil { return nil, err } return stats, nil } type SectionCount struct { Section string Count int Unique int } func addSection(sec *map[string]*SectionCount, name string, count int) { if name == "" { return } name = filepath.Ext(name) name = strings.TrimPrefix(name, ".") secName, ok := (*sec)[name] if !ok { (*sec)[name] = &SectionCount{ Section: name, Count: count, Unique: 1, } return } secName.Count += count secName.Unique++ (*sec)[name] = secName } type html struct { TotalLoads int TotalPages int UniquePages int Searches int Errors int TotalSections []SectionCount AllPages []Special MaxLen int } var T *template.Template func Html() string { if !CFG.EnableStats || DB == nil { return "" } stats, err := GetStats() if err != nil || len(stats) == 0 { return "" } var ht html var Specialc Special _ = DB.One("Page", "Search", &Specialc) ht.Searches = Specialc.Count _ = DB.One("Page", "Error", &Specialc) ht.Errors = Specialc.Count sec := make(map[string]*SectionCount) Maxlen := 0 for _, s := range stats { addSection(&sec, s.Query, s.Count) ht.AllPages = append(ht.AllPages, Special{Page: s.Query, Count: s.Count}) if len(s.Query) > Maxlen { Maxlen = len(s.Query) } } for _, v := range sec { ht.TotalSections = append(ht.TotalSections, *v) ht.TotalPages += v.Count ht.UniquePages += v.Unique } for i := range ht.TotalSections { for j := i + 1; j < len(ht.TotalSections); j++ { if ht.TotalSections[i].Count > ht.TotalSections[j].Count { ht.TotalSections[i], ht.TotalSections[j] = ht.TotalSections[j], ht.TotalSections[i] } } } ht.TotalLoads = ht.TotalPages + ht.UniquePages + ht.Searches + ht.Errors var b bytes.Buffer _ = T.ExecuteTemplate(&b, "stats", &ht) return b.String() }