feat: add web layer (internal/web) and wire it into main.go
Routing, html/template layout/content pattern, and the Pre-Publish check flow: POST /pruefen runs extraction (Stufe 1) then rules.Evaluate (Stufe 2) and renders the result as an htmx fragment. Nothing is persisted yet — that's the next step (wiring internal/store in). The needsClarification case is rendered explicitly as a request for more information rather than "no findings", matching the core principle. Every result carries the legal-advice disclaimer required by CLAUDE.md's guardrails. Server depends on a narrow Extractor interface rather than *extract. Client directly, so tests inject a fake instead of calling the real API — internal/web's test suite never touches the network. htmx is vendored locally (internal/web/static/htmx.min.js) instead of loaded from a CDN, keeping the UI usable without runtime internet access. cmd/deklarix/main.go now wires all of this together: reads ANTHROPIC_API_KEY (required) and RULES_DIR (default "rules"), builds the extract client and loads the rule set, and serves web.Server instead of the old inline health-only mux. This exposed the same crash-loop risk fixed earlier for DATABASE_URL: postinst's start guard only checked DATABASE_URL, so a fresh install would now crash-loop on a missing ANTHROPIC_API_KEY instead. The guard checks both. scripts/build.sh also now ships rules/*.yaml into the .deb under /usr/share/deklarix/rules (not a conffile — rules are updated via the release pipeline, never hand-edited on a server), and deklarix.env.example points RULES_DIR there by default. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
83
internal/web/handlers.go
Normal file
83
internal/web/handlers.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/netcell-it/deklarix/internal/extract"
|
||||
"github.com/netcell-it/deklarix/internal/rules"
|
||||
)
|
||||
|
||||
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprint(w, `{"ok":true}`)
|
||||
}
|
||||
|
||||
type indexData struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
if err := s.templates.ExecuteTemplate(w, "layout", indexData{Title: "Pre-Publish-Prüfung"}); err != nil {
|
||||
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
type findingView struct {
|
||||
RuleID string
|
||||
Version int
|
||||
Severity string
|
||||
Title string
|
||||
Fix string
|
||||
Sources []string
|
||||
}
|
||||
|
||||
type resultData struct {
|
||||
NeedsClarification bool
|
||||
Findings []findingView
|
||||
}
|
||||
|
||||
// handleCheck führt die Pre-Publish-Prüfung aus: Extraktion (Stufe 1)
|
||||
// gefolgt von der Regelauswertung (Stufe 2). Speichert noch nichts —
|
||||
// das ist der nächste Schritt (Verdrahtung über internal/store).
|
||||
func (s *Server) handleCheck(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "ungültiges Formular", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
platform := r.FormValue("platform")
|
||||
caption := r.FormValue("caption")
|
||||
if platform == "" || caption == "" {
|
||||
http.Error(w, "Plattform und Caption sind Pflichtfelder", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
facts, err := s.extractor.Extract(r.Context(), extract.Input{
|
||||
Platform: platform,
|
||||
Jurisdiction: "DE",
|
||||
Caption: caption,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, "Extraktion fehlgeschlagen: "+err.Error(), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
|
||||
findings, needsClarification := rules.Evaluate(s.ruleSet, facts)
|
||||
|
||||
data := resultData{NeedsClarification: needsClarification}
|
||||
for _, f := range findings {
|
||||
data.Findings = append(data.Findings, findingView{
|
||||
RuleID: f.RuleID,
|
||||
Version: f.RuleVersion,
|
||||
Severity: string(f.Severity),
|
||||
Title: f.Title,
|
||||
Fix: f.Fix,
|
||||
Sources: f.Sources,
|
||||
})
|
||||
}
|
||||
|
||||
if err := s.templates.ExecuteTemplate(w, "result", data); err != nil {
|
||||
http.Error(w, "Ergebnis konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user