feat(go): M6.6 — /qr, /sync-bank, /flush-cache, /version pages
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
- GET /qr: Czech QR Platba PNG; ports Python qr_code() exactly
(account validation, amount clamping, * stripping, SPD format)
- GET /sync-bank: Fio sync → infer → cache flush with captured log
- GET+POST /flush-cache: form + action, shows deleted count
- GET /version: JSON alias of /api/version (Python parity)
- FlushCache() added to membership.Sources; wired through api.Handler
- web.ActionHandlers{BankSync} closure-based dep injection for sync
- New dep: github.com/skip2/go-qrcode
- TestQRBuildSPD (9 cases), TestServeQR, TestServeFlushCache{GET,POST},
TestServeSync, TestServeVersion added
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"fuj-management/go/internal/web/api"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
// HTMLHandler serves the Go-native HTML frontend.
|
||||
@@ -10,11 +13,12 @@ type HTMLHandler struct {
|
||||
renderer *Renderer
|
||||
build BuildInfo
|
||||
apiHandler *api.Handler
|
||||
actions ActionHandlers
|
||||
}
|
||||
|
||||
// NewHTMLHandler constructs an HTMLHandler.
|
||||
func NewHTMLHandler(r *Renderer, b BuildInfo, ah *api.Handler) *HTMLHandler {
|
||||
return &HTMLHandler{renderer: r, build: b, apiHandler: ah}
|
||||
func NewHTMLHandler(r *Renderer, b BuildInfo, ah *api.Handler, actions ActionHandlers) *HTMLHandler {
|
||||
return &HTMLHandler{renderer: r, build: b, apiHandler: ah, actions: actions}
|
||||
}
|
||||
|
||||
func (h *HTMLHandler) ServeAdults(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -62,10 +66,78 @@ func (h *HTMLHandler) ServePayments(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// ServeSync handles GET /sync-bank: runs sync+infer+flush then renders the result.
|
||||
func (h *HTMLHandler) ServeSync(w http.ResponseWriter, r *http.Request) {
|
||||
h.renderer.Render(w, "sync", PageData{Active: "sync", Build: h.build})
|
||||
pd := PageData{Active: "sync", Build: h.build}
|
||||
|
||||
if h.actions.BankSync == nil {
|
||||
h.renderer.Render(w, "sync", SyncPageData{
|
||||
PageData: pd,
|
||||
Output: "Bank sync is not configured.",
|
||||
Success: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
success := true
|
||||
if err := h.actions.BankSync(r.Context(), &buf); err != nil {
|
||||
fmt.Fprintf(&buf, "\nError: %s\n\nStack trace:\n%s", err.Error(), debug.Stack())
|
||||
success = false
|
||||
}
|
||||
|
||||
fmt.Fprintln(&buf, "\n=== Flush Cache ===")
|
||||
n, err := h.apiHandler.FlushCache()
|
||||
if err != nil {
|
||||
fmt.Fprintf(&buf, "flush error: %s\n", err.Error())
|
||||
success = false
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%d cache file(s) deleted.\n", n)
|
||||
}
|
||||
|
||||
h.renderer.Render(w, "sync", SyncPageData{
|
||||
PageData: pd,
|
||||
Output: buf.String(),
|
||||
Success: success,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *HTMLHandler) ServeFlushCache(w http.ResponseWriter, r *http.Request) {
|
||||
h.renderer.Render(w, "flush_cache", PageData{Active: "flush", Build: h.build})
|
||||
// ServeFlushCacheGET handles GET /flush-cache: renders the confirmation form.
|
||||
func (h *HTMLHandler) ServeFlushCacheGET(w http.ResponseWriter, r *http.Request) {
|
||||
h.renderer.Render(w, "flush_cache", FlushPageData{
|
||||
PageData: PageData{Active: "flush", Build: h.build},
|
||||
})
|
||||
}
|
||||
|
||||
// ServeFlushCachePOST handles POST /flush-cache: flushes and re-renders with count.
|
||||
func (h *HTMLHandler) ServeFlushCachePOST(w http.ResponseWriter, r *http.Request) {
|
||||
n, _ := h.apiHandler.FlushCache()
|
||||
h.renderer.Render(w, "flush_cache", FlushPageData{
|
||||
PageData: PageData{Active: "flush", Build: h.build},
|
||||
Flushed: true,
|
||||
Deleted: n,
|
||||
})
|
||||
}
|
||||
|
||||
// ServeQR handles GET /qr: generates and returns a Czech QR Platba PNG.
|
||||
func (h *HTMLHandler) ServeQR(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
account := q.Get("account")
|
||||
amount := q.Get("amount")
|
||||
message := q.Get("message")
|
||||
if account == "" {
|
||||
account = h.apiHandler.Config.BankAccount
|
||||
}
|
||||
if amount == "" {
|
||||
amount = "0"
|
||||
}
|
||||
|
||||
payload := BuildSPD(account, amount, message, h.apiHandler.Config.BankAccount)
|
||||
png, err := RenderQRCode(payload)
|
||||
if err != nil {
|
||||
http.Error(w, "qr encode: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
_, _ = w.Write(png)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user