package pihole import ( "bytes" "fmt" "io" "net/http" "sort" "strconv" "strings" "github.com/olekukonko/tablewriter" ) func getSummaryView(c http.Client, settings *Settings) string { var err error var s Status s, err = getStatus(c, settings.apiUrl) if err != nil { return err.Error() } var sb strings.Builder buf := new(bytes.Buffer) switch strings.ToLower(s.Status) { case "disabled": sb.WriteString(" [white]Status [red]DISABLED\n") case "enabled": sb.WriteString(" [white]Status [green]ENABLED\n") default: sb.WriteString(" [white]Status [yellow]UNKNOWN\n") } summaryTable := createTable([]string{}, buf) summaryTable.Append([]string{"Domain blocklist", s.DomainsBeingBlocked, "Queries today", s.DNSQueriesToday}) summaryTable.Append([]string{"Ads blocked today", fmt.Sprintf("%s (%s%%)", s.AdsBlockedToday, s.AdsPercentageToday), "Cached queries", s.QueriesCached}) summaryTable.Append([]string{"Blocklist Age", fmt.Sprintf("%sd %sh %sm", s.GravityLastUpdated.Relative.Days, s.GravityLastUpdated.Relative.Hours, s.GravityLastUpdated.Relative.Minutes), "Forwarded queries", s.QueriesForwarded}) summaryTable.Render() sb.WriteString(buf.String()) return sb.String() } func getTopItemsView(c http.Client, settings *Settings) string { var err error var ti TopItems ti, err = getTopItems(c, settings) if err != nil { return err.Error() } buf := new(bytes.Buffer) var sb strings.Builder tiTable := createTable([]string{"Top Queries", "", "Top Ads", ""}, buf) largest := len(ti.TopAds) if len(ti.TopQueries) > largest { largest = len(ti.TopQueries) } sortedTiQueries := sortMapByIntVal(ti.TopQueries) sortedTiAds := sortMapByIntVal(ti.TopAds) for x := 0; x < largest; x++ { tiQVal := []string{"", ""} if len(sortedTiQueries) > x { tiQVal = []string{shorten(sortedTiQueries[x][0], settings.maxDomainWidth), sortedTiQueries[x][1]} } tiAVal := []string{"", ""} if len(sortedTiAds) > x { tiAVal = []string{shorten(sortedTiAds[x][0], settings.maxDomainWidth), sortedTiAds[x][1]} } tiTable.Append([]string{tiQVal[0], tiQVal[1], tiAVal[0], tiAVal[1]}) } tiTable.Render() sb.WriteString(buf.String()) return sb.String() } func getTopClientsView(c http.Client, settings *Settings) string { tc, err := getTopClients(c, settings) if err != nil { return err.Error() } var tq QueryTypes tq, err = getQueryTypes(c, settings) if err != nil { return err.Error() } buf := new(bytes.Buffer) tcTable := createTable([]string{"Top Clients", "", "Top Query Types", ""}, buf) sortedTcQueries := sortMapByIntVal(tc.TopSources) sortedTopQT := sortMapByFloatVal(tq.QueryTypes) largest := len(tc.TopSources) if len(tq.QueryTypes) > largest { largest = len(tq.QueryTypes) } if settings.showTopClients < largest { largest = settings.showTopClients } for x := 0; x < largest; x++ { tcVal := []string{"", ""} if len(sortedTcQueries) > x { tcVal = []string{sortedTcQueries[x][0], sortedTcQueries[x][1]} } tqtVal := []string{"", ""} if len(sortedTopQT) > x && sortedTopQT[x][0] != "" { tqtVal = []string{sortedTopQT[x][0], sortedTopQT[x][1] + "%"} } tcTable.Append([]string{tcVal[0], tcVal[1], tqtVal[0], tqtVal[1]}) } tcTable.Render() var sb strings.Builder sb.WriteString(buf.String()) return sb.String() } func shorten(s string, limit int) string { if len(s) > limit { return s[:limit] + "..." } return s } func createTable(header []string, buf io.Writer) *tablewriter.Table { table := tablewriter.NewWriter(buf) if len(header) != 0 { table.SetHeader(header) table.SetHeaderLine(false) table.SetHeaderAlignment(0) } table.SetAutoWrapText(false) table.SetAutoFormatHeaders(true) table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) table.SetAlignment(tablewriter.ALIGN_LEFT) table.SetBorder(true) table.SetCenterSeparator("") table.SetColumnSeparator("") table.SetRowSeparator("") table.SetTablePadding(" ") table.SetNoWhiteSpace(false) return table } func sortMapByIntVal(m map[string]int) (sorted [][]string) { type kv struct { Key string Value int } ss := make([]kv, len(m)) for k, v := range m { ss = append(ss, kv{k, v}) } sort.Slice(ss, func(i, j int) bool { return ss[i].Value > ss[j].Value }) for _, kv := range ss { sorted = append(sorted, []string{kv.Key, strconv.Itoa(kv.Value)}) } return } func sortMapByFloatVal(m map[string]float32) (sorted [][]string) { type kv struct { Key string Value float32 } ss := make([]kv, len(m)) for k, v := range m { if k == "" || v == 0.00 { continue } ss = append(ss, kv{k, v}) } sort.Slice(ss, func(i, j int) bool { return ss[i].Value > ss[j].Value }) for _, kv := range ss { sorted = append(sorted, []string{kv.Key, fmt.Sprintf("%.2f", kv.Value)}) } return }