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:
noroot
2026-08-27 14:23:45 +02:00
parent db373e51ce
commit 31caa8f66a
12 changed files with 440 additions and 22 deletions

View File

@@ -2,12 +2,14 @@ package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"github.com/netcell-it/deklarix/internal/extract"
"github.com/netcell-it/deklarix/internal/rules"
"github.com/netcell-it/deklarix/internal/store"
"github.com/netcell-it/deklarix/internal/web"
)
func main() {
@@ -26,20 +28,38 @@ func main() {
}
defer db.Close()
apiKey := os.Getenv("ANTHROPIC_API_KEY")
if apiKey == "" {
log.Fatal("ANTHROPIC_API_KEY is required")
}
extractor, err := extract.NewClient(apiKey)
if err != nil {
log.Fatalf("extract client: %v", err)
}
rulesDir := os.Getenv("RULES_DIR")
if rulesDir == "" {
rulesDir = "rules"
}
ruleSet, err := rules.Load(os.DirFS(rulesDir))
if err != nil {
log.Fatalf("load rules from %s: %v", rulesDir, err)
}
log.Printf("%d Regeln aus %s geladen", len(ruleSet), rulesDir)
server, err := web.NewServer(extractor, ruleSet)
if err != nil {
log.Fatalf("web server: %v", err)
}
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
addr := ":" + port
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"ok":true}`)
})
log.Printf("Deklarix server starting on %s", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
if err := http.ListenAndServe(addr, server); err != nil {
log.Fatal(err)
}
}