From 125fc4772afadac5115550f4675dbd6e595c4feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Fri, 12 Jun 2026 21:56:17 +0200 Subject: [PATCH] =?UTF-8?q?fix(backup):=20pozadinske=20gorutine=20koriste?= =?UTF-8?q?=20sve=C5=BEu=20konekciju=20posle=20obnove?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auto-backup i čišćenje sesija/pokušaja su koristili originalni db handle iz main.go, koji posle obnove backupa (VratiBackup) ostaje zatvoren — gorutine bi prestale da rade do restarta. Sada rade preko novog helpera h.SaBazom, koji pod deljenim zaključavanjem prosleđuje trenutnu h.DB, pa vide zamenjenu konekciju. Gorutine su premeštene da startuju posle kreiranja h. time.Sleep je van zaključavanja da ne odlaže obnovu. --- cmd/ntech/main.go | 52 +++++++++++++++++++++---------------- internal/handler/handler.go | 11 ++++++++ 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index 874ca5f..c878b5f 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -109,28 +109,6 @@ func main() { napraviBackup(db, putanjaBaze) - // periodični automatski backup — interval se čita iz podešavanja u svakom ciklusu, - // tako da izmena u podešavanjima stupa na snagu bez restarta (od sledećeg ciklusa) - go func() { - for { - sati := procitajIntPodesavanje(db, "backup_interval_sati", 24) - time.Sleep(time.Duration(sati) * time.Hour) - napraviBackup(db, putanjaBaze) - } - }() - - // periodično brisanje isteklih sesija i starih pokušaja prijave - go func() { - ticker := time.NewTicker(time.Hour) - defer ticker.Stop() - sesijeRepo := sqlite.NoviSesijeRepo(db) - pokusajiRepo := sqlite.NoviPokusajiPrijaveRepo(db) - for range ticker.C { - _ = sesijeRepo.ObrisiIstekle(context.Background()) - _ = pokusajiRepo.ObrisiStare(context.Background(), time.Now().Add(-24*time.Hour)) - } - }() - os.MkdirAll("web/static/uploads", 0755) h := handler.Novi(db, totpKljuc) @@ -151,6 +129,36 @@ func main() { log.Printf("Keš šablona kreiran: %d šablona", len(kes)) } + // Pozadinske gorutine se pokreću posle kreiranja h i rade preko h.SaBazom, + // pa uvek koriste TRENUTNU konekciju baze (posle obnove backupa h.DB se menja). + + // periodični automatski backup — interval se čita iz podešavanja u svakom + // ciklusu, pa izmena stupa na snagu bez restarta (od sledećeg ciklusa) + go func() { + for { + sati := 24 + h.SaBazom(func(db *sql.DB) { + sati = procitajIntPodesavanje(db, "backup_interval_sati", 24) + }) + time.Sleep(time.Duration(sati) * time.Hour) + h.SaBazom(func(db *sql.DB) { + napraviBackup(db, putanjaBaze) + }) + } + }() + + // periodično brisanje isteklih sesija i starih pokušaja prijave + go func() { + ticker := time.NewTicker(time.Hour) + defer ticker.Stop() + for range ticker.C { + h.SaBazom(func(db *sql.DB) { + _ = sqlite.NoviSesijeRepo(db).ObrisiIstekle(context.Background()) + _ = sqlite.NoviPokusajiPrijaveRepo(db).ObrisiStare(context.Background(), time.Now().Add(-24*time.Hour)) + }) + } + }() + r := chi.NewRouter() r.Use(ntechmw.BezbednostHeaders()) r.Use(ntechmw.CsrfMiddleware) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 85e7d4f..53a2705 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -57,6 +57,17 @@ func (h *Handler) ZakljucajCitanje(next http.Handler) http.Handler { }) } +// SaBazom izvršava fn sa TRENUTNOM konekcijom baze, pod deljenim zaključavanjem. +// Namenjeno pozadinskim gorutinama (auto-backup, čišćenje): posle obnove backupa +// h.DB se menja, pa gorutine moraju da čitaju aktuelni handle, a ne zatvoreni. +// Zaključavanje se drži samo za vreme fn — ne pozivaj iz njega operacije koje +// dugo blokiraju (npr. time.Sleep), da ne bi nepotrebno odlagao obnovu. +func (h *Handler) SaBazom(fn func(*sql.DB)) { + h.mu.RLock() + defer h.mu.RUnlock() + fn(h.DB) +} + // Novi kreira novi Handler sa datom bazom. // totpKljuc je 32-bajtni ključ za šifrovanje TOTP tajni u mirovanju. func Novi(baza *sql.DB, totpKljuc []byte) *Handler {