Bezbednosne i integritetne popravke iz pregleda koda

- foreign_keys pragma po konekciji (DSN _pragma) — ranije aktivna na
  samo delu konekcija iz pula, rizik siročadi u bazi pri brisanju
- busy_timeout + _txlock=immediate za konkurentan pristup SQLite bazi
- ispravljen DnevniPrometMaloprodaje (DATE() -> substr) — dnevni pazar
  za KIR/KPO je bio uvek nula
- rate-limit (isti IP-bruteforce brojač) dodat na TOTP/2FA verifikaciju
- ispravljen nil-pointer u DodajDeloNalogu + uklonjeni debug logovi
- izvuciIP prihvata X-Real-IP/X-Forwarded-For samo sa poverljive
  (loopback/privatne) adrese — sprečava zaobilaženje bruteforce brave
- ispravljen naziv dozvole podesavanja.izmena -> podesavanja.izmeni
- brisanje artikala/kategorija prebačeno sa GET na POST (CSRF zaštita)
This commit is contained in:
2026-07-03 23:00:44 +02:00
parent 4a94db7684
commit 49296e4315
11 changed files with 118 additions and 60 deletions
+16 -13
View File
@@ -12,23 +12,26 @@ import (
_ "modernc.org/sqlite"
)
// OtvoriDB otvara konekciju ka SQLite bazi i primenjuje performance PRAGMA podešavanja
// OtvoriDB otvara konekciju ka SQLite bazi i primenjuje performance PRAGMA podešavanja.
//
// Pragme se prosleđuju kroz DSN (_pragma=...) jer database/sql drži pul konekcija —
// PRAGMA postavljena preko db.Exec važi samo za konekciju koja je taj Exec uslužila,
// dok DSN _pragma parametri primenjuje driver na SVAKU novu konekciju pri otvaranju.
func OtvoriDB(putanja string) (*sql.DB, error) {
db, err := sql.Open("sqlite", putanja)
dsn := "file:" + putanja +
"?_pragma=journal_mode(WAL)" +
"&_pragma=synchronous(NORMAL)" +
"&_pragma=cache_size(10000)" +
"&_pragma=foreign_keys(1)" +
"&_pragma=busy_timeout(5000)" +
"&_txlock=immediate"
db, err := sql.Open("sqlite", dsn)
if err != nil {
return nil, fmt.Errorf("ntech: OtvoriDB: %w", err)
}
pragme := []string{
"PRAGMA journal_mode=WAL",
"PRAGMA synchronous=NORMAL",
"PRAGMA cache_size=10000",
"PRAGMA foreign_keys=ON",
}
for _, p := range pragme {
if _, err := db.Exec(p); err != nil {
return nil, fmt.Errorf("ntech: OtvoriDB: %s: %w", p, err)
}
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("ntech: OtvoriDB: %w", err)
}
return db, nil
+2 -2
View File
@@ -446,7 +446,7 @@ func (r *ProdajaRepo) DnevniPrometMaloprodaje(ctx context.Context, datum string)
// broj naloga tog dana
err := r.db.QueryRowContext(ctx, `
SELECT COUNT(*) FROM prodajni_nalozi
WHERE klijent_id IS NULL AND stornirano = 0 AND DATE(datum) = ?`, datum,
WHERE klijent_id IS NULL AND stornirano = 0 AND substr(datum, 1, 10) = ?`, datum,
).Scan(&p.BrojNaloga)
if err != nil {
return p, fmt.Errorf("ntech: ProdajaRepo.DnevniPrometMaloprodaje: count: %w", err)
@@ -458,7 +458,7 @@ func (r *ProdajaRepo) DnevniPrometMaloprodaje(ctx context.Context, datum string)
SELECT s.pdv_stopa, SUM(s.cena_bez_pdv * s.kolicina), SUM(s.pdv_iznos * s.kolicina)
FROM stavke_prodaje s
JOIN prodajni_nalozi p ON p.id = s.nalog_id
WHERE p.klijent_id IS NULL AND p.stornirano = 0 AND DATE(p.datum) = ?
WHERE p.klijent_id IS NULL AND p.stornirano = 0 AND substr(p.datum, 1, 10) = ?
GROUP BY s.pdv_stopa`, datum,
)
if err != nil {
-4
View File
@@ -5,7 +5,6 @@ import (
"database/sql"
"errors"
"fmt"
"log/slog"
"ntech/internal/model"
)
@@ -80,16 +79,13 @@ func (r *ServisniDeloviRepo) UgradiIliPotrazuj(ctx context.Context, nalogID, art
func ugradiIliPotrazujTx(ctx context.Context, tx *sql.Tx, nalogID, artikalID int64, kolicina int, cenaKomada float64, korisnikID *int64, predlozeno bool) (ugradjeno, nedostaje int, err error) {
// Predloženi delovi: ne skidaju sa lagera, svaki predlog je poseban red (ne merge)
if predlozeno {
slog.Info("PREDLOG_INSERT", "nalogID", nalogID, "artikalID", artikalID, "kolicina", kolicina)
_, err = tx.ExecContext(ctx,
"INSERT INTO servisni_potrazivani_delovi (nalog_id, artikal_id, kolicina, cena_komada, predlozeno) VALUES (?, ?, ?, ?, 1)",
nalogID, artikalID, kolicina, cenaKomada,
)
if err != nil {
slog.Error("PREDLOG_INSERT_ERR", "err", err)
return 0, 0, fmt.Errorf("ntech: ugradiIliPotrazujTx: predlozeni: %w", err)
}
slog.Info("PREDLOG_INSERT_OK")
return 0, kolicina, nil
}