Files
2026-04-06 17:58:13 +03:00

10 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
18-web-dashboard 01 execute 1
pkg/web/server.go
pkg/web/auth.go
pkg/web/handlers.go
pkg/web/embed.go
pkg/web/static/htmx.min.js
pkg/web/static/style.css
pkg/web/templates/layout.html
pkg/web/templates/overview.html
pkg/web/server_test.go
true
WEB-01
WEB-02
WEB-10
truths artifacts key_links
chi v5 HTTP server starts on configurable port and serves embedded static assets
Overview page renders with summary statistics from database
Optional basic auth / token auth blocks unauthenticated requests when configured
path provides exports
pkg/web/server.go chi router setup, middleware stack, NewServer constructor
Server
NewServer
Config
path provides exports
pkg/web/auth.go Basic auth and bearer token auth middleware
AuthMiddleware
path provides exports
pkg/web/handlers.go Overview page handler with stats aggregation
handleOverview
path provides exports
pkg/web/embed.go go:embed directives for static/ and templates/
staticFS
templateFS
path provides
pkg/web/server_test.go Integration tests for server, auth, overview
from to via pattern
pkg/web/server.go pkg/storage DB dependency in Config struct storage.DB
from to via pattern
pkg/web/handlers.go pkg/web/templates/overview.html html/template rendering template..*Execute
from to via pattern
pkg/web/server.go pkg/web/static/ go:embed + http.FileServer http.FileServer
Create the pkg/web package foundation: chi v5 router, go:embed static assets (htmx.min.js, Tailwind CDN reference), html/template-based layout, overview dashboard page with stats, and optional auth middleware.

Purpose: Establishes the HTTP server skeleton that Plans 02 and 03 build upon. Output: Working pkg/web package with chi router, static serving, layout template, overview page, auth middleware.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/18-web-dashboard/18-CONTEXT.md

From pkg/storage/db.go:

type DB struct { ... }
func Open(path string) (*DB, error)
func (db *DB) Close() error
func (db *DB) SQL() *sql.DB

From pkg/storage/findings.go:

type Finding struct {
    ID, ScanID       int64
    ProviderName     string
    KeyValue, KeyMasked, Confidence string
    SourcePath, SourceType string
    LineNumber       int
    CreatedAt        time.Time
    Verified         bool
    VerifyStatus     string
    VerifyHTTPCode   int
    VerifyMetadata   map[string]string
}
func (db *DB) ListFindings(encKey []byte) ([]Finding, error)
func (db *DB) SaveFinding(f Finding, encKey []byte) (int64, error)

From pkg/storage/queries.go:

type Filters struct {
    Provider, Confidence, SourceType string
    Verified                         *bool
    Limit, Offset                    int
}
func (db *DB) ListFindingsFiltered(encKey []byte, f Filters) ([]Finding, error)
func (db *DB) GetFinding(id int64, encKey []byte) (*Finding, error)
func (db *DB) DeleteFinding(id int64) (int64, error)

From pkg/providers/registry.go:

type Registry struct { ... }
func NewRegistry() (*Registry, error)
func (r *Registry) List() []Provider
func (r *Registry) Stats() RegistryStats

From pkg/dorks/registry.go:

type Registry struct { ... }
func NewRegistry() (*Registry, error)
func (r *Registry) List() []Dork
func (r *Registry) Stats() Stats

From pkg/recon/engine.go:

type Engine struct { ... }
func NewEngine() *Engine
func (e *Engine) SweepAll(ctx context.Context, cfg Config) ([]Finding, error)
func (e *Engine) List() []string
Task 1: chi v5 dependency + go:embed static assets + layout template pkg/web/embed.go, pkg/web/static/htmx.min.js, pkg/web/static/style.css, pkg/web/templates/layout.html, pkg/web/templates/overview.html 1. Run `go get github.com/go-chi/chi/v5@v5.2.5` to add chi v5 to go.mod.
  1. Create pkg/web/embed.go:

    • //go:embed static/* into var staticFiles embed.FS
    • //go:embed templates/* into var templateFiles embed.FS
    • Export both via package-level vars.
  2. Download htmx v2.0.4 minified JS (curl from unpkg.com/htmx.org@2.0.4/dist/htmx.min.js) and save to pkg/web/static/htmx.min.js.

  3. Create pkg/web/static/style.css with minimal custom styles (body font, table styling, card class). The layout will load Tailwind v4 from CDN (https://cdn.tailwindcss.com) per the CONTEXT.md deferred decision. The local style.css is for overrides only.

  4. Create pkg/web/templates/layout.html — html/template (NOT templ, per deferred decision):

    • DOCTYPE, html, head with Tailwind CDN link, htmx.min.js script tag (served from /static/htmx.min.js), local style.css link
    • Navigation bar: KeyHunter brand, links to Overview (/), Keys (/keys), Providers (/providers), Recon (/recon), Dorks (/dorks), Settings (/settings)
    • {{block "content" .}}{{end}} placeholder for page content
    • Use {{define "layout"}}...{{end}} wrapping pattern so pages extend it
  5. Create pkg/web/templates/overview.html extending layout:

    • {{template "layout" .}} with {{define "content"}} block
    • Four stat cards in a Tailwind grid (lg:grid-cols-4, sm:grid-cols-2): Total Keys, Providers Loaded, Recon Sources, Last Scan
    • Recent findings table showing last 10 keys (masked): Provider, Masked Key, Source, Confidence, Date
    • Data struct: OverviewData{TotalKeys int, TotalProviders int, ReconSources int, LastScan string, RecentFindings []storage.Finding} cd /home/salva/Documents/apikey && go build ./pkg/web/... pkg/web/embed.go compiles with go:embed directives, htmx.min.js is vendored, layout.html and overview.html parse without errors, chi v5 is in go.mod
Task 2: Server struct, auth middleware, overview handler, and tests pkg/web/server.go, pkg/web/auth.go, pkg/web/handlers.go, pkg/web/server_test.go - Test: GET / returns 200 with "KeyHunter" in body (overview page renders) - Test: GET /static/htmx.min.js returns 200 with JS content - Test: GET / with auth enabled but no credentials returns 401 - Test: GET / with correct basic auth returns 200 - Test: GET / with correct bearer token returns 200 - Test: Overview page shows provider count and key count from injected data 1. Create `pkg/web/server.go`: - `type Config struct { DB *storage.DB; EncKey []byte; Providers *providers.Registry; Dorks *dorks.Registry; ReconEngine *recon.Engine; Port int; AuthUser string; AuthPass string; AuthToken string }` — all fields the server needs - `type Server struct { router chi.Router; cfg Config; tmpl *template.Template }` - `func NewServer(cfg Config) (*Server, error)` — parses all templates from templateFiles embed.FS, builds chi.Router - Router setup: `chi.NewRouter()`, use `middleware.Logger`, `middleware.Recoverer`, `middleware.RealIP` - If AuthUser or AuthToken is set, apply AuthMiddleware (from auth.go) - Mount `/static/` serving from staticFiles embed.FS (use `http.StripPrefix` + `http.FileServer(http.FS(...))`) - Register routes: `GET /` -> handleOverview - `func (s *Server) ListenAndServe() error` — starts `http.Server` on `cfg.Port` - `func (s *Server) Router() chi.Router` — expose for testing
  1. Create pkg/web/auth.go:

    • func AuthMiddleware(user, pass, token string) func(http.Handler) http.Handler
    • Check Authorization header: if "Bearer " matches configured token, pass through
    • If "Basic " matches user:pass, pass through
    • Otherwise return 401 with WWW-Authenticate: Basic realm="keyhunter" header
    • If all auth fields are empty strings, middleware is a no-op passthrough
  2. Create pkg/web/handlers.go:

    • type OverviewData struct { TotalKeys, TotalProviders, ReconSources int; LastScan string; RecentFindings []storage.Finding; PageTitle string }
    • func (s *Server) handleOverview(w http.ResponseWriter, r *http.Request)
    • Query: count findings via len(db.ListFindingsFiltered(encKey, Filters{Limit: 10})) for recent, run a COUNT query on the SQL for total
    • Provider count from s.cfg.Providers.Stats().Total (or len(s.cfg.Providers.List()))
    • Recon sources from len(s.cfg.ReconEngine.List())
    • Render overview template with OverviewData
  3. Create pkg/web/server_test.go:

    • Use httptest.NewRecorder + httptest.NewRequest against s.Router()
    • Test overview returns 200 with "KeyHunter" in body
    • Test static asset serving
    • Test auth middleware (401 without creds, 200 with basic auth, 200 with bearer token)
    • For DB-dependent tests, use in-memory SQLite (storage.Open(":memory:")) or skip DB and test the router/auth independently with a nil-safe overview (show zeroes when DB is nil) cd /home/salva/Documents/apikey && go test ./pkg/web/... -v -count=1 Server starts with chi router, static assets served via go:embed, overview page renders with stats, auth middleware blocks unauthenticated requests when configured, all tests pass
- `go build ./pkg/web/...` compiles without errors - `go test ./pkg/web/... -v` — all tests pass - `go vet ./pkg/web/...` — no issues

<success_criteria>

  • chi v5.2.5 in go.mod
  • pkg/web/server.go exports Server, NewServer, Config
  • GET / returns overview HTML with stat cards
  • GET /static/htmx.min.js returns vendored htmx
  • Auth middleware returns 401 when credentials missing (when auth configured)
  • Auth middleware passes with valid basic auth or bearer token </success_criteria>
After completion, create `.planning/phases/18-web-dashboard/18-01-SUMMARY.md`