feat(go): scheduled bank sync (Go only)
The Go server now runs sync -> infer -> flush-cache by itself at the times in SYNC_SCHEDULE (comma-separated HH:MM, default 04:00, "off" disables), read in SYNC_TZ (default Europe/Prague). Nothing runs at startup; an invalid value stops the server with an error. - internal/scheduler: time-of-day parsing, Next, and the run loop - banksync.Runner: one lock shared by /sync-bank and the scheduler so runs never overlap, plus the status of the last run - footer shows the last sync since start and the next scheduled run - binary embeds time/tzdata because the Alpine image has no zone data Deliberately not implemented in the legacy Python app, which is being phased out; recorded in CLAUDE.md under "Deliberately Go-only features". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,17 +21,26 @@ func NewHTMLHandler(r *Renderer, b BuildInfo, ah *api.Handler, actions ActionHan
|
||||
return &HTMLHandler{renderer: r, build: b, apiHandler: ah, actions: actions}
|
||||
}
|
||||
|
||||
// page builds the data shared by every page (nav highlight, build info, sync summary).
|
||||
func (h *HTMLHandler) page(active string) PageData {
|
||||
pd := PageData{Active: active, Build: h.build}
|
||||
if h.actions.SyncInfo != nil {
|
||||
pd.SyncInfo = h.actions.SyncInfo()
|
||||
}
|
||||
return pd
|
||||
}
|
||||
|
||||
func (h *HTMLHandler) ServeAdults(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := h.apiHandler.AssembleAdults(r.Context())
|
||||
if err != nil {
|
||||
h.renderer.Render(w, "adults", AdultsPageData{
|
||||
PageData: PageData{Active: "adults", Build: h.build},
|
||||
PageData: h.page("adults"),
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
h.renderer.Render(w, "adults", AdultsPageData{
|
||||
PageData: PageData{Active: "adults", Build: h.build},
|
||||
PageData: h.page("adults"),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
@@ -40,13 +49,13 @@ func (h *HTMLHandler) ServeJuniors(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := h.apiHandler.AssembleJuniors(r.Context())
|
||||
if err != nil {
|
||||
h.renderer.Render(w, "juniors", JuniorsPageData{
|
||||
PageData: PageData{Active: "juniors", Build: h.build},
|
||||
PageData: h.page("juniors"),
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
h.renderer.Render(w, "juniors", JuniorsPageData{
|
||||
PageData: PageData{Active: "juniors", Build: h.build},
|
||||
PageData: h.page("juniors"),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
@@ -55,20 +64,20 @@ func (h *HTMLHandler) ServePayments(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := h.apiHandler.AssemblePayments(r.Context())
|
||||
if err != nil {
|
||||
h.renderer.Render(w, "payments", PaymentsPageData{
|
||||
PageData: PageData{Active: "payments", Build: h.build},
|
||||
PageData: h.page("payments"),
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
h.renderer.Render(w, "payments", PaymentsPageData{
|
||||
PageData: PageData{Active: "payments", Build: h.build},
|
||||
PageData: h.page("payments"),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// ServeSync handles GET /sync-bank: runs sync+infer+flush then renders the result.
|
||||
func (h *HTMLHandler) ServeSync(w http.ResponseWriter, r *http.Request) {
|
||||
pd := PageData{Active: "sync", Build: h.build}
|
||||
pd := h.page("sync")
|
||||
|
||||
if h.actions.BankSync == nil {
|
||||
h.renderer.Render(w, "sync", SyncPageData{
|
||||
@@ -96,7 +105,7 @@ func (h *HTMLHandler) ServeSync(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
h.renderer.Render(w, "sync", SyncPageData{
|
||||
PageData: pd,
|
||||
PageData: h.page("sync"), // rebuilt so the footer reflects this run
|
||||
Output: buf.String(),
|
||||
Success: success,
|
||||
})
|
||||
@@ -105,7 +114,7 @@ func (h *HTMLHandler) ServeSync(w http.ResponseWriter, r *http.Request) {
|
||||
// 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},
|
||||
PageData: h.page("flush"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -113,7 +122,7 @@ func (h *HTMLHandler) ServeFlushCacheGET(w http.ResponseWriter, r *http.Request)
|
||||
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},
|
||||
PageData: h.page("flush"),
|
||||
Flushed: true,
|
||||
Deleted: n,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user