You've already forked caddy-opnsense-blocker
Refine requests log filter controls
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -113,6 +114,27 @@ func (s *Service) ListEvents(ctx context.Context, since time.Time, limit int, op
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) ListSourceNames() []string {
|
||||||
|
if s == nil || s.cfg == nil || len(s.cfg.Sources) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
seen := make(map[string]struct{}, len(s.cfg.Sources))
|
||||||
|
items := make([]string, 0, len(s.cfg.Sources))
|
||||||
|
for _, source := range s.cfg.Sources {
|
||||||
|
name := strings.TrimSpace(source.Name)
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[name]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[name] = struct{}{}
|
||||||
|
items = append(items, name)
|
||||||
|
}
|
||||||
|
sort.Strings(items)
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) ListIPs(ctx context.Context, limit int, state string) ([]model.IPState, error) {
|
func (s *Service) ListIPs(ctx context.Context, limit int, state string) ([]model.IPState, error) {
|
||||||
return s.store.ListIPStates(ctx, limit, state)
|
return s.store.ListIPStates(ctx, limit, state)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
type App interface {
|
type App interface {
|
||||||
GetOverview(ctx context.Context, since time.Time, limit int, options model.OverviewOptions) (model.Overview, error)
|
GetOverview(ctx context.Context, since time.Time, limit int, options model.OverviewOptions) (model.Overview, error)
|
||||||
ListEvents(ctx context.Context, since time.Time, limit int, options model.EventListOptions) ([]model.Event, error)
|
ListEvents(ctx context.Context, since time.Time, limit int, options model.EventListOptions) ([]model.Event, error)
|
||||||
|
ListSourceNames() []string
|
||||||
ListIPs(ctx context.Context, limit int, state string) ([]model.IPState, error)
|
ListIPs(ctx context.Context, limit int, state string) ([]model.IPState, error)
|
||||||
ListRecentIPs(ctx context.Context, since time.Time, limit int) ([]model.RecentIPRow, error)
|
ListRecentIPs(ctx context.Context, since time.Time, limit int) ([]model.RecentIPRow, error)
|
||||||
GetIPDetails(ctx context.Context, ip string) (model.IPDetails, error)
|
GetIPDetails(ctx context.Context, ip string) (model.IPDetails, error)
|
||||||
@@ -36,8 +37,9 @@ type handler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type pageData struct {
|
type pageData struct {
|
||||||
Title string
|
Title string
|
||||||
IP string
|
IP string
|
||||||
|
Sources []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type actionPayload struct {
|
type actionPayload struct {
|
||||||
@@ -92,7 +94,7 @@ func (h *handler) handleQueryLogPage(w http.ResponseWriter, r *http.Request) {
|
|||||||
methodNotAllowed(w)
|
methodNotAllowed(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
renderTemplate(w, h.queryLogPage, pageData{Title: "Requests Log"})
|
renderTemplate(w, h.queryLogPage, pageData{Title: "Requests Log", Sources: h.app.ListSourceNames()})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) handleIPPage(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) handleIPPage(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -1020,25 +1022,52 @@ const queryLogHTML = `<!doctype html>
|
|||||||
<form class="filters-grid" id="controls-form" onsubmit="applyFilters(event)">
|
<form class="filters-grid" id="controls-form" onsubmit="applyFilters(event)">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="source-filter">Source</label>
|
<label for="source-filter">Source</label>
|
||||||
<input id="source-filter" type="text" placeholder="gitea">
|
<select id="source-filter">
|
||||||
|
<option value="">Any source</option>
|
||||||
|
{{ range .Sources }}<option value="{{ . }}">{{ . }}</option>{{ end }}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="method-filter">Method</label>
|
<label for="method-filter">Method</label>
|
||||||
<input id="method-filter" type="text" list="method-options" placeholder="GET">
|
<select id="method-filter">
|
||||||
<datalist id="method-options">
|
<option value="">Any method</option>
|
||||||
<option value="GET"></option>
|
<option value="GET">GET</option>
|
||||||
<option value="POST"></option>
|
<option value="POST">POST</option>
|
||||||
<option value="HEAD"></option>
|
<option value="HEAD">HEAD</option>
|
||||||
<option value="PUT"></option>
|
<option value="PUT">PUT</option>
|
||||||
<option value="DELETE"></option>
|
<option value="DELETE">DELETE</option>
|
||||||
<option value="PATCH"></option>
|
<option value="PATCH">PATCH</option>
|
||||||
<option value="OPTIONS"></option>
|
<option value="OPTIONS">OPTIONS</option>
|
||||||
<option value="OTHER"></option>
|
<option value="OTHER">OTHER</option>
|
||||||
</datalist>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="status-filter">HTTP status</label>
|
<label for="status-filter">HTTP status</label>
|
||||||
<input id="status-filter" type="text" placeholder="404 or 4xx">
|
<select id="status-filter">
|
||||||
|
<option value="">Any status</option>
|
||||||
|
<optgroup label="Status classes">
|
||||||
|
<option value="2xx">2xx</option>
|
||||||
|
<option value="3xx">3xx</option>
|
||||||
|
<option value="4xx">4xx</option>
|
||||||
|
<option value="5xx">5xx</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Common client errors">
|
||||||
|
<option value="400">400</option>
|
||||||
|
<option value="401">401</option>
|
||||||
|
<option value="403">403</option>
|
||||||
|
<option value="404">404</option>
|
||||||
|
<option value="405">405</option>
|
||||||
|
<option value="408">408</option>
|
||||||
|
<option value="410">410</option>
|
||||||
|
<option value="429">429</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Common server errors">
|
||||||
|
<option value="500">500</option>
|
||||||
|
<option value="502">502</option>
|
||||||
|
<option value="503">503</option>
|
||||||
|
<option value="504">504</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="state-filter">State</label>
|
<label for="state-filter">State</label>
|
||||||
|
|||||||
@@ -169,6 +169,15 @@ func TestHandlerServesOverviewAndManualActions(t *testing.T) {
|
|||||||
if !strings.Contains(queryLogBody, "Filters, sorting, and pagination") {
|
if !strings.Contains(queryLogBody, "Filters, sorting, and pagination") {
|
||||||
t.Fatalf("requests log page should expose the collapsible controls panel")
|
t.Fatalf("requests log page should expose the collapsible controls panel")
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(queryLogBody, `<select id="source-filter">`) || !strings.Contains(queryLogBody, `<option value="main">main</option>`) {
|
||||||
|
t.Fatalf("requests log page should expose a source dropdown with configured sources")
|
||||||
|
}
|
||||||
|
if !strings.Contains(queryLogBody, `<select id="method-filter">`) || !strings.Contains(queryLogBody, `<option value="POST">POST</option>`) {
|
||||||
|
t.Fatalf("requests log page should expose a method dropdown")
|
||||||
|
}
|
||||||
|
if !strings.Contains(queryLogBody, `<select id="status-filter">`) || !strings.Contains(queryLogBody, `<option value="4xx">4xx</option>`) {
|
||||||
|
t.Fatalf("requests log page should expose a structured HTTP status dropdown")
|
||||||
|
}
|
||||||
if !strings.Contains(queryLogBody, "Rows per page") {
|
if !strings.Contains(queryLogBody, "Rows per page") {
|
||||||
t.Fatalf("requests log page should expose pagination settings")
|
t.Fatalf("requests log page should expose pagination settings")
|
||||||
}
|
}
|
||||||
@@ -224,6 +233,10 @@ type stubApp struct {
|
|||||||
lastEventOptions model.EventListOptions
|
lastEventOptions model.EventListOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubApp) ListSourceNames() []string {
|
||||||
|
return []string{"gitea", "main"}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stubApp) GetOverview(_ context.Context, _ time.Time, _ int, options model.OverviewOptions) (model.Overview, error) {
|
func (s *stubApp) GetOverview(_ context.Context, _ time.Time, _ int, options model.OverviewOptions) (model.Overview, error) {
|
||||||
s.lastOverviewOptions = options
|
s.lastOverviewOptions = options
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
|
|||||||
Reference in New Issue
Block a user