- migracija 102: indeksi na FK kolonama (stavke_prodaje, servisni_delovi,
servisni_potrazivani_delovi, magacinske_promene, stavke_nabavke, pdv_kir,
pdv_kpr) — potvrđeno kreiranje i idempotentnost
- BrojKriticnihZaliha/KriticneZalihe filtriraju samo proizvode i aktivne
artikle, usklađeno sa ArtikalRepo.Lista(SamoKriticni)
- url.QueryEscape na query string u magacin.go/klijent.go (paginacija)
- TestFiskalizacije/BeStatus vraćaju generičku poruku, detalj u slog
- SetFlash loguje grešku umesto tihog ignorisanja
- os.WriteFile/os.MkdirAll u main.go proveravaju grešku
- dummyHash generisanje panikuje na startu ako bcrypt ikad zakaže
(umesto tihog sloma anti-enumeracije)
- PrihvatiPredlozene proverava redovi.Err()
- /healthz endpoint (DB ping)
- hx-boost na "Osveži" linkovima (blokirane_ip, fiskal_pazar)
- MaxBytesReader (4KB) na javne /status/{token}/* POST rute
- komentar u auth.go zašto postoje dva flash mehanizma (namerno, ne spojeno)
Provereno: jePrivatnaAdresa ne radi DNS lookup (nema net.LookupIP u repou),
pa DNS-rebinding nalaz iz pregleda nije primenljiv na trenutni kod.
66 lines
1.5 KiB
Go
66 lines
1.5 KiB
Go
package middleware
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"ntech/internal/model"
|
|
)
|
|
|
|
// SetFlash čuva flash poruku u koloni flash aktivne sesije
|
|
func SetFlash(w http.ResponseWriter, r *http.Request, db *sql.DB, tip, poruka string) {
|
|
kolacic, err := r.Cookie("ntech_sesija")
|
|
if err != nil {
|
|
return
|
|
}
|
|
data, err := json.Marshal(model.FlashPoruka{Tip: tip, Poruka: poruka})
|
|
if err != nil {
|
|
return
|
|
}
|
|
if _, err := db.ExecContext(r.Context(),
|
|
`UPDATE sesije SET flash = ? WHERE token = ?`,
|
|
string(data), kolacic.Value); err != nil {
|
|
slog.Error("SetFlash: upis nije uspeo", "error", err)
|
|
}
|
|
}
|
|
|
|
// GetFlash čita i atomično briše flash poruku iz aktivne sesije
|
|
func GetFlash(r *http.Request, db *sql.DB) *model.FlashPoruka {
|
|
kolacic, err := r.Cookie("ntech_sesija")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
tx, err := db.BeginTx(r.Context(), nil)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
var flashJSON sql.NullString
|
|
if err := tx.QueryRowContext(r.Context(),
|
|
`SELECT flash FROM sesije WHERE token = ?`, kolacic.Value).Scan(&flashJSON); err != nil {
|
|
return nil
|
|
}
|
|
if !flashJSON.Valid || flashJSON.String == "" {
|
|
return nil
|
|
}
|
|
|
|
if _, err := tx.ExecContext(r.Context(),
|
|
`UPDATE sesije SET flash = NULL WHERE token = ?`, kolacic.Value); err != nil {
|
|
return nil
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return nil
|
|
}
|
|
|
|
var f model.FlashPoruka
|
|
if err := json.Unmarshal([]byte(flashJSON.String), &f); err != nil {
|
|
return nil
|
|
}
|
|
return &f
|
|
}
|