diff --git a/auth.go b/auth.go index b50670d..2c3c03b 100644 --- a/auth.go +++ b/auth.go @@ -3,9 +3,12 @@ package main import ( "database/sql" "encoding/json" + "fmt" "log/slog" "net" "net/http" + "strconv" + "strings" "time" ) @@ -130,14 +133,56 @@ func logsHandler(cfg Config, db *sql.DB) http.HandlerFunc { http.Error(w, "unauthorized", http.StatusUnauthorized) return } - rows, err := db.Query(`SELECT timestamp, action, ip, cidr, ttl_seconds, reason, api_client_ip - FROM audit_log ORDER BY timestamp DESC LIMIT 200`) + + query := `SELECT timestamp, action, ip, cidr, ttl_seconds, reason, api_client_ip FROM audit_log` + var args []any + argIndex := 1 + + filterAction := r.URL.Query().Get("action") + filterIP := r.URL.Query().Get("ip") + limitStr := r.URL.Query().Get("limit") + offsetStr := r.URL.Query().Get("offset") + + var conditions []string + if filterAction != "" { + conditions = append(conditions, fmt.Sprintf(`action = $%d`, argIndex)) + args = append(args, filterAction) + argIndex++ + } + if filterIP != "" { + conditions = append(conditions, fmt.Sprintf(`ip = $%d`, argIndex)) + args = append(args, filterIP) + argIndex++ + } + + if len(conditions) > 0 { + query += " WHERE " + strings.Join(conditions, " AND ") + } + query += ` ORDER BY timestamp DESC` + + limit := 200 + offset := 0 + if limitStr != "" { + if parsed, err := strconv.Atoi(limitStr); err == nil && parsed > 0 && parsed <= 1000 { + limit = parsed + } + } + if offsetStr != "" { + if parsed, err := strconv.Atoi(offsetStr); err == nil && parsed >= 0 { + offset = parsed + } + } + query += fmt.Sprintf(` LIMIT %d OFFSET %d`, limit, offset) + + rows, err := db.Query(query, args...) if err != nil { + slog.Error("failed to query logs", "error", err) http.Error(w, "database error", http.StatusInternalServerError) return } defer rows.Close() - var logs []map[string]interface{} + + logs := make([]map[string]interface{}, 0) for rows.Next() { var ts time.Time var action, apiClientIP, reason string