aboutsummaryrefslogtreecommitdiff
path: root/backend/stats.go
diff options
context:
space:
mode:
Diffstat (limited to 'backend/stats.go')
-rw-r--r--backend/stats.go81
1 files changed, 79 insertions, 2 deletions
diff --git a/backend/stats.go b/backend/stats.go
index 2b07221..dfdf040 100644
--- a/backend/stats.go
+++ b/backend/stats.go
@@ -7,7 +7,10 @@ import (
"log/slog"
"net/http"
"regexp"
+ "strconv"
"strings"
+
+ "github.com/go-chi/chi/v5"
)
var trimRefererReg = regexp.MustCompile(`https?://([a-z-0-9.]+(:\d+)?)/.*`)
@@ -18,7 +21,9 @@ func getDB(ctx context.Context) *sql.DB {
func UpdateStats(ctx context.Context, r *http.Request) error {
target := r.URL.Path
- if strings.HasPrefix(target, "/assets") || strings.HasPrefix(target, "/static") {
+ if strings.HasPrefix(target, "/assets") ||
+ strings.HasPrefix(target, "/static") ||
+ strings.HasPrefix(target, "/admin") {
return nil
}
ref := r.Header.Get("Referer")
@@ -32,7 +37,7 @@ func UpdateStats(ctx context.Context, r *http.Request) error {
ref = subs[1]
if ref == ctx.Value(configKey).(*Config).Domain || ref == fmt.Sprintf("localhost:%d", 8000) {
ref = subs[0][strings.Index(subs[0], ref)+len(ref):]
- if ref == target {
+ if ref == target || strings.HasPrefix(ref, "/admin") || ref == "/favicon.ico" {
return nil
}
}
@@ -63,3 +68,75 @@ func UpdateStats(ctx context.Context, r *http.Request) error {
_, err = db.ExecContext(ctx, "UPDATE stats SET visit = ? WHERE id = ?", nb+1, id)
return err
}
+
+type statRow struct {
+ Origin string
+ Target string
+ Visit uint
+}
+
+const statPerPage = 25
+
+func GetStatRows(ctx context.Context, page uint) ([]statRow, error) {
+ rows, err := getDB(ctx).QueryContext(
+ ctx,
+ "SELECT origin, target, visit FROM stats ORDER BY visit DESC LIMIT ? OFFSET ?",
+ statPerPage, (page-1)*statPerPage,
+ )
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ statRows := make([]statRow, statPerPage)
+ var i uint8
+ for i = 0; rows.Next(); i++ {
+ var stat statRow
+ err = rows.Scan(&stat.Origin, &stat.Target, &stat.Visit)
+ if err != nil {
+ return nil, err
+ }
+ statRows[i] = stat
+ }
+ if i == 0 {
+ return nil, nil
+ }
+ return statRows[:i], nil
+}
+
+type adminData struct {
+ *data
+ Rows []statRow
+ PagesNumber int
+ CurrentPage int
+}
+
+func HandleAdmin(r *chi.Mux) {
+ r.Get("/admin", func(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ if !ctx.Value(loginKey).(bool) {
+ w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
+ return
+ }
+ d := new(adminData)
+ d.data = new(data)
+ rawPage := r.URL.Query().Get("page")
+ page := 1
+ var err error
+ if rawPage != "" {
+ page, err = strconv.Atoi(rawPage)
+ if err != nil || page < 1 {
+ slog.Warn("invalid page number", "rawPage", rawPage)
+ http.Error(w, "Bad request", http.StatusBadRequest)
+ return
+ }
+ }
+ d.Rows, err = GetStatRows(ctx, uint(page))
+ if err != nil {
+ panic(err)
+ }
+ d.PagesNumber = page + max(len(d.Rows)-statPerPage+1, 0)
+ d.CurrentPage = page
+ d.handleGeneric(w, r, "admin", d)
+ })
+}