Proširena wiki: arhitektura, bezbednost, razvoj, ažurirane sve stranice
+198
@@ -0,0 +1,198 @@
|
|||||||
|
# Arhitektura
|
||||||
|
|
||||||
|
## Struktura direktorijuma
|
||||||
|
|
||||||
|
```
|
||||||
|
ntech/
|
||||||
|
├── assets.go # //go:embed za migrations, web/static, web/templates
|
||||||
|
├── cmd/ntech/main.go # ulazna tačka: ENV, FS izbor, migracije, ruter
|
||||||
|
├── go.mod # modul ntech, Go 1.26
|
||||||
|
├── internal/
|
||||||
|
│ ├── auth/ # bcrypt, TOTP, generisanje tokena, auth log
|
||||||
|
│ │ ├── auth.go # HashujLozinku, ProveriLozinku, TOTP operacije
|
||||||
|
│ │ └── log.go # fail2ban-kompatibilan format loga
|
||||||
|
│ ├── config/ # Prvo pokretanje (setup wizard)
|
||||||
|
│ │ ├── firstrun.go # JelPrvoPokretanje
|
||||||
|
│ │ └── setup.go # PokreniSetup (poseban HTTP server)
|
||||||
|
│ ├── db/
|
||||||
|
│ │ ├── repository.go # SVI interfejsi (ArtikalRepo, ServisRepo, ...)
|
||||||
|
│ │ ├── errors.go # ErrNedovoljnoKolicine
|
||||||
|
│ │ └── sqlite/ # SQLite implementacije (28 fajlova)
|
||||||
|
│ ├── handler/ # HTTP handleri — jedan fajl po domenu (26)
|
||||||
|
│ ├── middleware/ # Auth, CSRF, RBAC, bezbednost, flash
|
||||||
|
│ └── model/ # Domenske strukture i konstante (19 fajlova)
|
||||||
|
├── migrations/ # 61 SQL migracija (001-061), idempotentne
|
||||||
|
└── web/
|
||||||
|
├── static/ # CSS, JS, slike + uploads/
|
||||||
|
└── templates/ # Go html/template šabloni (komponente, stranice)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Slojevi i zavisnosti
|
||||||
|
|
||||||
|
```
|
||||||
|
main.go
|
||||||
|
└─> handler/ → zna za: middleware, db (interfejsi), db/sqlite, model, auth
|
||||||
|
└─> middleware/ → zna za: db/sqlite, model
|
||||||
|
└─> db (interfejsi) → implementira: db/sqlite/
|
||||||
|
└─> model/ → ne zna ni za koga — čist domen, nula internih importa
|
||||||
|
└─> auth/ → samostalan, ne zavisi od handler/db
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pravilo:** `model` je list (nema unutrašnje zavisnosti). `handler` je koren — svi putevi vode kroz njega. Nijedan drugi sloj ne sme da uvozi `handler`.
|
||||||
|
|
||||||
|
## Tok pokretanja programa
|
||||||
|
|
||||||
|
1. **MIME tipovi** — `.js` → `text/javascript`, `.css` → `text/css`
|
||||||
|
2. **`godotenv.Load(ntech.env)`** — učitava ENV iz fajla
|
||||||
|
3. **`auth.InitAuthLog()`** — auth log u fail2ban formatu
|
||||||
|
4. **Izbor fajl-sistema** — disk-first za razvoj, embed za produkciju/demo
|
||||||
|
5. **Prvo pokretanje?** → Setup Wizard na portu 8080
|
||||||
|
6. **Baza + migracije** — `sql.Open("sqlite", ...)`, WAL mod, migracije redom
|
||||||
|
7. **Init seme** — dozvole, TOTP ključ, bcrypt dummy hash, admin korisnik
|
||||||
|
8. **Chi ruter** — middleware stack, rute sa RBAC omotačima
|
||||||
|
9. **`http.ListenAndServe`** — server kreće
|
||||||
|
|
||||||
|
## Middleware stack
|
||||||
|
|
||||||
|
Redosled na chi ruteru:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. middleware.RequestID # ID svakog zahteva
|
||||||
|
2. middleware.RealIP # Prava IP iz X-Forwarded-For
|
||||||
|
3. middleware.Logger # Slog logovanje
|
||||||
|
4. middleware.Recoverer # panic → 500
|
||||||
|
5. middleware.Compress(5, gzip) # GZip kompresija (nivo 5)
|
||||||
|
6. BezbednosniHeaderi # CSP, HSTS, X-Frame-Options, ...
|
||||||
|
7. CSRF # Double-submit cookie obrazac
|
||||||
|
8. FlashPoruke # Flash poruke u template kontekstu
|
||||||
|
--- (javne rute) ---
|
||||||
|
9. RequireAuth # Sesija → korisnik u context
|
||||||
|
10. RequireDozvola / RequireDozvolaMut # RBAC (zaštićene rute)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repository obrazac
|
||||||
|
|
||||||
|
Handleri **ne pišu SQL**. Sva komunikacija sa bazom ide preko interfejsa definisanih u `internal/db/repository.go`.
|
||||||
|
|
||||||
|
Primer interfejsa:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/db/repository.go
|
||||||
|
type ArtikalRepository interface {
|
||||||
|
Lista(ctx context.Context, filter ArtikalFilter) ([]model.ArtikalSaKategorijom, error)
|
||||||
|
DohvatiID(ctx context.Context, id int64) (*model.Artikal, error)
|
||||||
|
Kreiraj(ctx context.Context, a *model.Artikal) (int64, error)
|
||||||
|
Izmeni(ctx context.Context, a *model.Artikal) error
|
||||||
|
Obrisi(ctx context.Context, id int64) error
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Implementacija:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/db/sqlite/artikal.go
|
||||||
|
type ArtikalRepo struct { db *sql.DB }
|
||||||
|
|
||||||
|
func NoviArtikalRepo(db *sql.DB) *ArtikalRepo { ... }
|
||||||
|
func (r *ArtikalRepo) Lista(ctx context.Context, f db.ArtikalFilter) ([]model.ArtikalSaKategorijom, error) {
|
||||||
|
// SELECT ... FROM artikli JOIN kategorije ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Handler koristi interfejs:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/handler/magacin.go
|
||||||
|
type MagacinHandler struct {
|
||||||
|
Artikli db.ArtikalRepository
|
||||||
|
Kategorije db.KategorijaRepository
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ovakav dizajn omogućava:
|
||||||
|
- **Testiranje** — mock-ovanje repozitorijuma
|
||||||
|
- **Zamenu baze** — iste interfejse može implementirati PostgreSQL drajver
|
||||||
|
- **Jasne granice** — handler ne zavisi od SQL dijalekta
|
||||||
|
|
||||||
|
## Repozitorijumi (interfejsi)
|
||||||
|
|
||||||
|
| Interfejs | Domen |
|
||||||
|
|---|---|
|
||||||
|
| `ArtikalRepository` | Artikli, kategorije, cene, dobavljači, arhiviranje |
|
||||||
|
| `KategorijaRepository` | Kategorije artikala |
|
||||||
|
| `PdvStopaRepository` | Šifarnik PDV stopa |
|
||||||
|
| `DobavljacRepository` | Dobavljači |
|
||||||
|
| `KlijentRepository` | Klijenti (firme i fizička lica) |
|
||||||
|
| `ServisRepository` | Servisni nalozi, delovi, javni token |
|
||||||
|
| `ProdajaRepository` | Prodajni nalozi i stavke |
|
||||||
|
| `NabavkaRepository` | Nabavke, zavisni troškovi, nivelacije |
|
||||||
|
| `MagacinRepository` | Stanje zaliha, prometne kartice |
|
||||||
|
| `KorisniciRepository` | Korisnici i dozvole |
|
||||||
|
| `SesijeRepository` | Server-side sesije |
|
||||||
|
| `LoginIstorijaRepository` | Istorija prijava |
|
||||||
|
| `PokusajiPrijaveRepository` | Brute-force evidencija |
|
||||||
|
| `PodesavanjaRepository` | Sva sistemska podešavanja |
|
||||||
|
| `PodsetniciRepository` | Podsetnici |
|
||||||
|
| `PdvEvidencijaRepository` | PDV KIR/KPR evidencija |
|
||||||
|
| `IzvestajRepository` | Izveštajni upiti |
|
||||||
|
| `RezervniKodoviRepository` | Rezervni kodovi za 2FA |
|
||||||
|
|
||||||
|
## Handleri
|
||||||
|
|
||||||
|
Svaki handler je jedan Go fajl u `internal/handler/`:
|
||||||
|
|
||||||
|
| Fajl | Domen |
|
||||||
|
|---|---|
|
||||||
|
| `admin.go` | Admin panel: korisnici, dozvole |
|
||||||
|
| `dashboard.go` | Početna tabla sa statistikama |
|
||||||
|
| `dobavljac.go` | CRUD dobavljača |
|
||||||
|
| `izvestaji.go` | Izveštaji (promet, stanje, inventura, prihod) |
|
||||||
|
| `kategorija.go` | CRUD kategorija |
|
||||||
|
| `klijent.go` | CRUD klijenata, filteri |
|
||||||
|
| `magacin.go` | Magacin — pregled, pretraga, korekcija |
|
||||||
|
| `magacin_forma.go` | Forma za artikal |
|
||||||
|
| `nabavka.go` | Nabavke, nivelacije |
|
||||||
|
| `pdv_kir.go` | PDV KIR (knjiga izdatih računa) |
|
||||||
|
| `pdv_kpr.go` | PDV KPR (knjiga primljenih računa) |
|
||||||
|
| `pdv_obracun.go` | PDV obračun |
|
||||||
|
| `pdv_stopa.go` | Šifarnik PDV stopa |
|
||||||
|
| `podesavanja.go` | Sistemska podešavanja |
|
||||||
|
| `podsetnici.go` | Podsetnici |
|
||||||
|
| `prijava.go` | Prijava, odjava, TOTP, 2FA |
|
||||||
|
| `prodaja.go` | Prodajni nalozi |
|
||||||
|
| `profil.go` | Profil korisnika (lozinka, TOTP) |
|
||||||
|
| `servis.go` | Servisni nalozi |
|
||||||
|
| `handler.go` | Render helperi, `Render`, `RenderStranicu` |
|
||||||
|
| `kes.go` | Keširanje šablona |
|
||||||
|
| `utils.go` | Pomoćne funkcije (parsiranje, prikaz) |
|
||||||
|
|
||||||
|
## Frontend arhitektura
|
||||||
|
|
||||||
|
Frontend koristi tri biblioteke:
|
||||||
|
|
||||||
|
- **HTMX** — navigacija bez punog reload-a (`hx-get`, `hx-post`, `hx-target`, `hx-swap`)
|
||||||
|
- **Alpine.js** — reaktivnost na klijentu (`x-data`, `x-show`, `x-model`, `x-bind`)
|
||||||
|
- **Chart.js** — grafikoni (dashboard, izveštaji)
|
||||||
|
|
||||||
|
Stilovi su organizovani u `web/static/`:
|
||||||
|
- `main.css` — glavni stil (≈4000 linija)
|
||||||
|
- `glass.css` — glassmorphism efekti
|
||||||
|
- `login.css` — stilovi za prijavnu stranicu
|
||||||
|
|
||||||
|
Šabloni u `web/templates/`:
|
||||||
|
```
|
||||||
|
templates/
|
||||||
|
├── stranice/ # Kompletne stranice (dashboard.html, magacin.html, ...)
|
||||||
|
├── komponente/ # Deljive komponente (navbar.html, sidebar.html, modal.html)
|
||||||
|
├── teme/ # Varijacije tema
|
||||||
|
├── setup/ # Setup wizard
|
||||||
|
└── layout/ # Osnovni layout (base.html)
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTMX pravila
|
||||||
|
|
||||||
|
- `hx-select` odbacuje `<head>` — stil koji mora da radi posle navigacije ide u `main.css`
|
||||||
|
- `hx-target` sa `#sadrzaj` — glavni kontejner za dinamički sadržaj
|
||||||
|
- `hx-push-url` — ažurira URL u adresnoj traci
|
||||||
|
- `hx-indicator` — prikazuje loader tokom zahteva
|
||||||
+121
@@ -0,0 +1,121 @@
|
|||||||
|
# Bezbednost
|
||||||
|
|
||||||
|
## Autentifikacija
|
||||||
|
|
||||||
|
### Lozinke
|
||||||
|
|
||||||
|
- **bcrypt**, cost faktor **12**
|
||||||
|
- Heš se čuva u bazi, nikad u plaintext-u
|
||||||
|
- Anti-enumeracija: `IzjednaciVremeProvere` izvršava bcrypt poređenje i kad korisnik ne postoji — vreme odgovora je identično
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/auth/auth.go
|
||||||
|
const bcryptCost = 12
|
||||||
|
|
||||||
|
func HashujLozinku(lozinka string) (string, error) {
|
||||||
|
return bcrypt.GenerateFromPassword([]byte(lozinka), bcryptCost)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IzjednaciVremeProvere(lozinka string) {
|
||||||
|
_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(lozinka))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dvofaktorska autentifikacija (2FA)
|
||||||
|
|
||||||
|
- **TOTP** (Time-based One-Time Password) — RFC 6238
|
||||||
|
- 6-cifreni kodovi, rotiraju na 30 sekundi
|
||||||
|
- Tajna se šifruje AES-256-GCM pre upisa u bazu
|
||||||
|
- QR kod za podešavanje u autentifikator aplikaciji
|
||||||
|
- Rezervni kodovi za oporavak (jednokratni, bcrypt heširani)
|
||||||
|
|
||||||
|
### Sesije
|
||||||
|
|
||||||
|
- **Server-side** — token u HTTP-only kolačiću `ntech_sesija`
|
||||||
|
- Token je **UUID v4** (`github.com/google/uuid`)
|
||||||
|
- Podaci sesije: `korisnik_id`, `token`, `created_at`, `expires_at`
|
||||||
|
- Nema osetljivih podataka u kolačiću — samo nasumični token
|
||||||
|
- `Secure` flag u produkciji i demo modu
|
||||||
|
- `HttpOnly` — JavaScript ne može da čita kolačić
|
||||||
|
- `SameSite=Lax` — zaštita od CSRF-a na nivou brauzera
|
||||||
|
|
||||||
|
## Brute-force zaštita
|
||||||
|
|
||||||
|
```
|
||||||
|
5 neuspelih pokušaja sa iste IP adrese u 15 minuta → IP se blokira
|
||||||
|
```
|
||||||
|
|
||||||
|
- Evidencija u tabeli `pokusaji_prijave`
|
||||||
|
- Auth log u fail2ban formatu (`/var/log/ntech/auth.log`)
|
||||||
|
- Pogodan za integraciju sa spoljnim fail2ban-om
|
||||||
|
|
||||||
|
## CSRF zaštita
|
||||||
|
|
||||||
|
- **Double-submit cookie** obrazac
|
||||||
|
- CSRF token se generiše na serveru i ubacuje u svaki `<form>`
|
||||||
|
- Token se upoređuje sa kolačićem pri svakoj POST/PUT/DELETE mutaciji
|
||||||
|
- Implementacija: `internal/middleware/csrf.go`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Automatski ubacuje renderer -->
|
||||||
|
<form method="post" action="/magacin/novi">
|
||||||
|
<input type="hidden" name="csrf_token" value="...">
|
||||||
|
...
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
## RBAC — Kontrola pristupa zasnovana na ulogama
|
||||||
|
|
||||||
|
### Uloge
|
||||||
|
|
||||||
|
| Uloga | Opis |
|
||||||
|
|---|---|
|
||||||
|
| **superadmin** | Pun pristup — sve dozvole + dodela uloga |
|
||||||
|
| **admin** | Upravljanje korisnicima, podešavanja, svi moduli |
|
||||||
|
| **radnik** | Ograničen pristup — dodeljene dozvole po modulu |
|
||||||
|
|
||||||
|
### Dozvole (39 akcija)
|
||||||
|
|
||||||
|
Akcije su granularne: `modul.akcija` (npr. `artikal.dodaj`, `servis.obrisi`, `izvestaj.pregled`).
|
||||||
|
|
||||||
|
Neke rute su namerno **javne** (bez dozvole):
|
||||||
|
- `/magacin` — pregled magacina
|
||||||
|
- `/podsetnici` — svi prijavljeni vide svoje podsetnike
|
||||||
|
- `/prijava`, `/setup` — neautentifikovani pristup
|
||||||
|
|
||||||
|
### Middleware zaštita
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Mutirajuće rute — vraća 403 na odbijanje
|
||||||
|
r.With(doz("prodaja.dodaj")).Post("/prodaja/nova", ...)
|
||||||
|
|
||||||
|
// GET rute — redirektuje na odbijanje
|
||||||
|
r.With(doz("nabavka.pregled")).Get("/nabavke", ...)
|
||||||
|
|
||||||
|
// Admin rute — po ulozi
|
||||||
|
r.Group(RequireAdmin) → /admin/korisnici*, /admin/dozvole*
|
||||||
|
r.Group(RequireSuperAdmin) → /admin/dozvole/uloga/{id}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security headeri
|
||||||
|
|
||||||
|
Svaki HTTP odgovor dobija:
|
||||||
|
|
||||||
|
| Header | Vrednost |
|
||||||
|
|---|---|
|
||||||
|
| `Content-Security-Policy` | Dinamički: `style-src`, `script-src`, `img-src`, `font-src` |
|
||||||
|
| `X-Content-Type-Options` | `nosniff` |
|
||||||
|
| `X-Frame-Options` | `DENY` |
|
||||||
|
| `X-XSS-Protection` | `0` (zastarelo, CSP preuzima) |
|
||||||
|
| `Referrer-Policy` | `strict-origin-when-cross-origin` |
|
||||||
|
| `Strict-Transport-Security` | `max-age=63072000` (samo u produkciji) |
|
||||||
|
|
||||||
|
## Ostale mere
|
||||||
|
|
||||||
|
- **XSS zaštita** — `ntechToast` koristi `textContent` (ne `innerHTML`)
|
||||||
|
- **Open redirect** — validacija `_next` parametra kroz whitelist
|
||||||
|
- **Limit upload-a** — `http.MaxBytesReader` za sve POST zahteve
|
||||||
|
- **Anti-MIME-sniffing** — `X-Content-Type-Options: nosniff`
|
||||||
|
- **CSP font-src** — dozvoljava samo `'self'` (rešava problem sa Chart.js fontovima u Operi/Chrome-u)
|
||||||
|
- **Secure kolačići** — `Secure=true` u produkciji i demo modu
|
||||||
|
- **OIE zaštita** — `SameSite=Lax` na svim kolačićima
|
||||||
+85
-35
@@ -2,57 +2,107 @@
|
|||||||
|
|
||||||
## Magacin
|
## Magacin
|
||||||
|
|
||||||
- Skladištenje artikala sa kategorijama, šiframa i barkodovima
|
Upravljanje artiklima, kategorijama i zalihama.
|
||||||
- Praćenje stanja zaliha sa kritičnim nivoima
|
|
||||||
- Prometna kartica — istorija ulaza i izlaza za svaki artikal
|
- **Artikli** — naziv, opis, šifra, barkod, tip, jedinica mere
|
||||||
- Nabavna cena u formi artikla
|
- **Kategorije** — grupisanje artikala, kategorije sa kodovima
|
||||||
- Inventura (popis magacina)
|
- **Stanje zaliha** — trenutna količina, kritični minimum
|
||||||
|
- **Prometna kartica** — puna istorija ulaza i izlaza za svaki artikal
|
||||||
|
- **Korekcija količine** — ručno postavljanje stanja sa napomenom
|
||||||
|
- **Nabavna cena** — polje za nabavnu cenu u formi artikla
|
||||||
|
- **Arhiviranje** — artikal se ne briše već arhivira (čuva istoriju)
|
||||||
|
- **Šifra i barkod** — auto-generisanje (npr. `KOMP-0042`) + ručni unos
|
||||||
|
- **Pretraga** — po nazivu, šifri, barkodu, lokaciji, kategoriji
|
||||||
|
|
||||||
## Servis
|
## Servis
|
||||||
|
|
||||||
- Servisni nalozi sa kompletnim praćenjem statusa
|
Servisni nalozi sa kompletnim praćenjem.
|
||||||
- Inline promena statusa direktno iz detalja naloga
|
|
||||||
- Štampa servisnog naloga sa QR kodom
|
- **Novi nalog** — prijem uređaja, opis kvara, klijent, garancija
|
||||||
- Otpremnica pri preuzimanju uređaja
|
- **Statusi** — primljen, u dijagnostici, čeka delove, u popravci, gotov, preuzet
|
||||||
- Pregled troškova i auto-cena delova
|
- **Inline izmena statusa** — direktno iz detalja naloga, bez otvaranja forme
|
||||||
- Javni status naloga za klijente
|
- **Delovi i troškovi** — auto-cena delova, pregled ukupnih troškova
|
||||||
|
- **Štampa** — štampa servisnog naloga sa QR kodom
|
||||||
|
- **Otpremnica** — pri preuzimanju uređaja
|
||||||
|
- **QR kod** — na štampanom nalogu i otpremnici
|
||||||
|
- **Javni status** — klijent može pratiti status preko tokena
|
||||||
|
- **Avans** — polje za avans na otpremnici
|
||||||
|
|
||||||
## Prodaja
|
## Prodaja
|
||||||
|
|
||||||
- Fakture sa stavkama
|
Prodajni nalozi i fakturisanje.
|
||||||
- Obračun PDV-a (KIR, KPR)
|
|
||||||
- Formatiranje iznosa sa separatorom hiljada
|
- **Nalozi** — prodajni dokumenti sa stavkama
|
||||||
- Avans na otpremnici
|
- **Stavke** — artikal, količina, cena, PDV
|
||||||
|
- **Štampa** — štampanje prodajnog naloga/fakture
|
||||||
|
- **Storno** — poništavanje naloga
|
||||||
|
- **Formatiranje** — iznosi sa separatorom hiljada
|
||||||
|
|
||||||
|
## PDV
|
||||||
|
|
||||||
|
Potpuna evidencija PDV-a po srpskim propisima.
|
||||||
|
|
||||||
|
- **KIR** — Knjiga izdatih računa
|
||||||
|
- **KPR** — Knjiga primljenih računa
|
||||||
|
- **Obračun** — PDV obračun sa KIR/KPR podacima
|
||||||
|
- **Šifarnik stopa** — 20%, 10%, 0%, sa mogućnošću aktivacije/deaktivacije
|
||||||
|
- **Evidencija** — sve stavke sa PDV tretmanom, izvorom, iznosom
|
||||||
|
|
||||||
## Nabavka
|
## Nabavka
|
||||||
|
|
||||||
- Nabavke po dobavljaču sa auto-vezom
|
Upravljanje nabavkama i dobavljačima.
|
||||||
- Predlog cene pri izboru artikla
|
|
||||||
- Filter artikala po dobavljaču
|
- **Dobavljači** — firme, PIB, mesto, kontakt
|
||||||
- Dvosmerno računanje marža ↔ prodajna cena
|
- **Nabavke** — dokumenti nabavke sa stavkama
|
||||||
|
- **Vezani dobavljači** — auto-veza artikal–dobavljač pri nabavci
|
||||||
|
- **Filter po dobavljaču** — prikaz artikala vezanih za izabranog dobavljača
|
||||||
|
- **Predlog cene** — poslednja nabavna cena pri izboru artikla
|
||||||
|
- **Marža ↔ cena** — dvosmerno računanje: unesi maržu → dobiješ prodajnu, unesi prodajnu → dobiješ maržu
|
||||||
|
- **Zavisni troškovi** — transport, carina, špedicija na nabavci
|
||||||
|
- **Nivelacije** — korekcija nabavnih/prodajnih cena
|
||||||
|
|
||||||
## Klijenti
|
## Klijenti
|
||||||
|
|
||||||
- Baza firmi i fizičkih lica
|
Baza klijenata — firme i fizička lica.
|
||||||
- Filter po tipu (Svi / Firme / Fizička lica)
|
|
||||||
- Interaktivna pretraga sa paginacijom
|
- **Firme** — naziv, PIB, mesto, kontakt, račun
|
||||||
- Vezani dobavljači na kartici artikla
|
- **Fizička lica** — ime, prezime, kontakt
|
||||||
|
- **Filter po tipu** — Svi / Firme / Fizička lica
|
||||||
|
- **Interaktivna pretraga** — pretraga po nazivu, mestu, kontaktu
|
||||||
|
- **Paginacija** — 50 po stranici
|
||||||
|
|
||||||
## Izveštaji
|
## Izveštaji
|
||||||
|
|
||||||
- Prometni list magacina
|
Pregled poslovanja kroz izveštaje.
|
||||||
- Stanje zaliha
|
|
||||||
- Popis magacina (inventura)
|
|
||||||
- Servisni prihod — broji samo preuzete naloge
|
|
||||||
|
|
||||||
## Podešavanja
|
- **Prometni list** — ulaz/izlaz po artiklima za period
|
||||||
|
- **Stanje zaliha** — trenutno stanje svih artikala
|
||||||
- Logo firme u topbaru sa upload-om
|
- **Popis (inventura)** — popisne liste sa stvarnim stanjem
|
||||||
- Tema sa slajderima za pozadinu i brzinu animacije
|
- **Servisni prihod** — prihod od servisa (samo preuzeti nalozi)
|
||||||
- Korisnici i RBAC dozvole (admin, korisnik)
|
|
||||||
- AJAX čuvanje podešavanja bez reload-a stranice
|
|
||||||
|
|
||||||
## Dashboard
|
## Dashboard
|
||||||
|
|
||||||
- Statističke kartice sa linkovima na odgovarajuće stranice
|
Početna tabla sa ključnim pokazateljima.
|
||||||
- Hover efekti i glassmorphism dizajn
|
|
||||||
|
- **Stat kartice** — broj artikala, servisa, prodaja, klijenata
|
||||||
|
- **Grafikoni** — Chart.js vizualizacija prihoda
|
||||||
|
- **Linkovi** — kartice vode na odgovarajuće stranice
|
||||||
|
- **Hover efekti** — glassmorphism sa zatamnjenjem
|
||||||
|
|
||||||
|
## Podešavanja
|
||||||
|
|
||||||
|
Kompletna administracija sistema.
|
||||||
|
|
||||||
|
- **Opšte** — naziv firme, logo, kontakt, upload logoa, favicon
|
||||||
|
- **Tema** — pozadina, slajder za brzinu animacije, blur, glass opacity
|
||||||
|
- **Prijava** — pozadina prijavne stranice
|
||||||
|
- **Korisnici** — dodavanje, uređivanje, dozvole, reset lozinke
|
||||||
|
- **Dozvole** — RBAC matrica: uloga × akcija
|
||||||
|
- **Backup** — ručni backup baze i podešavanja
|
||||||
|
- **AJAX čuvanje** — podešavanja se čuvaju bez reload-a stranice
|
||||||
|
|
||||||
|
## Podsetnici
|
||||||
|
|
||||||
|
- Lični podsetnici za svakog korisnika
|
||||||
|
- Datum, vreme, opis
|
||||||
|
- Javni za sve prijavljene korisnike
|
||||||
|
|||||||
+40
-24
@@ -1,37 +1,53 @@
|
|||||||
# NTech — Servisni informacioni sistem
|
# NTech — Servisni informacioni sistem
|
||||||
|
|
||||||
**NTech** je veb aplikacija za male servise, knjigovodstvo i upravljanje magacinom. Pisana je u **Go** jeziku, radi u brauzeru i podatke čuva u **SQLite** bazi.
|
**NTech** je veb aplikacija za male servise, knjigovodstvo i upravljanje magacinom. Pisana je u **Go** jeziku i radi u brauzeru. Podatke čuva u **SQLite** bazi — sve je upakovano u jedan binarni fajl, bez Docker-a, bez spoljnih servera.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Osnovne mogućnosti
|
## Osnovne mogućnosti
|
||||||
|
|
||||||
- **Magacin** — artikli, kategorije, stanje zaliha, prometne kartice, inventura
|
| Modul | Opis |
|
||||||
- **Servis** — servisni nalozi, otpremnice, štampa sa QR kodom, praćenje statusa
|
|---|---|
|
||||||
- **Prodaja** — fakture, stavke, obračun PDV-a
|
| **Magacin** | Artikli, kategorije, stanje zaliha, prometne kartice, inventura |
|
||||||
- **Nabavka** — nabavke po dobavljaču, auto-predlog cena, filter artikala
|
| **Servis** | Servisni nalozi, otpremnice, štampa sa QR kodom, praćenje statusa, troškovi |
|
||||||
- **Klijenti** — firme i fizička lica, filteri, interaktivna pretraga
|
| **Prodaja** | Fakture, stavke, obračun PDV-a (KIR/KPR), avans |
|
||||||
- **Izveštaji** — prometni list, stanje zaliha, popis magacina, servisni prihod
|
| **Nabavka** | Nabavke po dobavljaču, auto-predlog cena, zavisni troškovi, nivelacije |
|
||||||
- **Podešavanja** — logo, tema, slajderi, korisnici i dozvole
|
| **Klijenti** | Firme i fizička lica, filteri, interaktivna pretraga, vezani dobavljači |
|
||||||
|
| **Izveštaji** | Prometni list, stanje zaliha, popis magacina, servisni prihod |
|
||||||
|
| **Podešavanja** | Logo, tema, slajderi, animacije, korisnici, dozvole, backup |
|
||||||
|
|
||||||
## Tehnologije
|
---
|
||||||
|
|
||||||
|
## Tehnološki stek
|
||||||
|
|
||||||
| Komponenta | Tehnologija |
|
| Komponenta | Tehnologija |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Jezik | Go (najnovija stabilna verzija) |
|
| Jezik | **Go** (1.26+) |
|
||||||
| Baza | SQLite (`modernc.org/sqlite`, bez CGO) |
|
| Baza | **SQLite** (`modernc.org/sqlite` — čist Go, bez CGO) |
|
||||||
| HTTP ruter | `go-chi/chi` |
|
| HTTP ruter | `go-chi/chi` v5 |
|
||||||
| Šabloni | `html/template` (Go standardna biblioteka) |
|
| Šabloni | `html/template` (Go stdlib) |
|
||||||
| Autentifikacija | bcrypt, TOTP 2FA, serverske sesije |
|
| Frontend | **HTMX** (navigacija), **Alpine.js** (reaktivnost), **Chart.js** (grafikoni) |
|
||||||
| Frontend | HTMX, Alpine.js, Chart.js |
|
| Autentifikacija | bcrypt (cost 12), TOTP 2FA, serverske sesije |
|
||||||
|
| Sesije | HTTP-only kolačić sa tokenom (UUID), server-side u SQLite |
|
||||||
|
| Embed | `//go:embed` — šabloni, statika, migracije u binarnom fajlu |
|
||||||
|
|
||||||
## Bezbednost
|
---
|
||||||
|
|
||||||
- CSRF zaštita na svim mutacijama (double-submit cookie)
|
## Arhitektura
|
||||||
- Lozinke heširane bcrypt-om (cost 12)
|
|
||||||
- 2FA preko TOTP-a
|
|
||||||
- Brute-force zaštita (5 neuspelih pokušaja → 15 minuta blokada)
|
|
||||||
- Security headeri (CSP, HSTS, X-Content-Type-Options)
|
|
||||||
- Anti-enumeracija pri prijavi
|
|
||||||
|
|
||||||
## Licenca
|
- **Repository obrazac** — handleri ne znaju za SQL; rade preko interfejsa
|
||||||
|
- **Jedan binarni fajl** — sve je embed-ovano, nema spoljnih foldera u produkciji
|
||||||
|
- **Server-side sesije** — token u HTTP-only kolačiću, bez podataka u brauzeru
|
||||||
|
- **Migracije pri pokretanju** — 61 SQL migracija, idempotentne, rastuće numerisane
|
||||||
|
- **Bezbednost po difoltu** — CSRF, bcrypt, TOTP, brute-force zaštita, CSP, security headeri
|
||||||
|
- **Modularni handleri** — jedan Go fajl po domenu (magacin.go, servis.go, prodaja.go...)
|
||||||
|
|
||||||
Proprietary — sva prava zadržana.
|
---
|
||||||
|
|
||||||
|
## Linkovi
|
||||||
|
|
||||||
|
- [Instalacija](Instalacija)
|
||||||
|
- [Arhitektura](Arhitektura)
|
||||||
|
- [Funkcionalnosti](Funkcionalnosti)
|
||||||
|
- [Bezbednost](Bezbednost)
|
||||||
|
- [Razvoj](Razvoj)
|
||||||
|
|||||||
+75
-30
@@ -3,60 +3,105 @@
|
|||||||
## Preduslovi
|
## Preduslovi
|
||||||
|
|
||||||
- **Go** 1.26 ili noviji
|
- **Go** 1.26 ili noviji
|
||||||
- [Git](https://git-scm.com/) (opciono, za kloniranje)
|
- **Git** (za kloniranje)
|
||||||
|
- **Linux** ili **Windows** (build skripta podržava oba)
|
||||||
|
|
||||||
## Kloniranje i pokretanje
|
## Brzi start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Kloniraj repozitorijum
|
||||||
git clone https://git.vm-net.in.rs/Dasko/GoNtech.git
|
git clone https://git.vm-net.in.rs/Dasko/GoNtech.git
|
||||||
cd GoNtech
|
cd GoNtech
|
||||||
|
|
||||||
|
# Pripremi konfiguraciju
|
||||||
cp ntech.env.example ntech.env
|
cp ntech.env.example ntech.env
|
||||||
# uredi ntech.env po potrebi
|
|
||||||
|
# Pokreni
|
||||||
go run ./cmd/ntech
|
go run ./cmd/ntech
|
||||||
```
|
```
|
||||||
|
|
||||||
Aplikacija se pokreće na `http://localhost:8080`.
|
Aplikacija se pokreće na `http://localhost:8080`.
|
||||||
|
|
||||||
## Build za produkciju
|
## Prvo pokretanje — Setup Wizard
|
||||||
|
|
||||||
|
Kad se program prvi put pokrene (nema `ntech.env`), automatski se otvara **Setup Wizard** na portu `8080`. Podešava se:
|
||||||
|
|
||||||
|
1. Port servera
|
||||||
|
2. Admin korisničko ime i lozinka
|
||||||
|
3. Naziv firme
|
||||||
|
|
||||||
|
Po završetku, `ntech.env` se kreira automatski i aplikacija se restartuje.
|
||||||
|
|
||||||
|
## Produkcijski build
|
||||||
|
|
||||||
|
Interaktivna skripta:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./build.sh
|
./build.sh
|
||||||
|
# → unesi verziju (npr. 0.1.5)
|
||||||
|
# → izaberi OS (linux / windows)
|
||||||
|
# → UPX kompresija (da/ne)
|
||||||
```
|
```
|
||||||
|
|
||||||
Interaktivna skripta — biraš verziju, OS (Linux/Windows) i da li želiš UPX kompresiju.
|
|
||||||
|
|
||||||
Ručni build:
|
Ručni build:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Linux, AMD64
|
||||||
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o ntech ./cmd/ntech
|
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o ntech ./cmd/ntech
|
||||||
|
|
||||||
|
# Sa verzijom
|
||||||
|
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux \
|
||||||
|
go build -ldflags "-X main.Verzija=0.1.5" -o ntech ./cmd/ntech
|
||||||
```
|
```
|
||||||
|
|
||||||
## Prvo pokretanje
|
Build je statički (`CGO_ENABLED=0`) — binarni fajl radi svuda bez zavisnosti.
|
||||||
|
|
||||||
Pri prvom pokretanju pokreće se **Setup Wizard** gde podešavaš:
|
## Konfiguracija (`ntech.env`)
|
||||||
|
|
||||||
1. Port servera
|
|
||||||
2. Admin korisničko ime i lozinku
|
|
||||||
3. Naziv firme
|
|
||||||
|
|
||||||
Posle toga, `ntech.env` se automatski kreira i aplikacija je spremna za korišćenje.
|
|
||||||
|
|
||||||
## Demo mod
|
|
||||||
|
|
||||||
Postavljanjem `NTECH_DEMO=true` u `ntech.env`:
|
|
||||||
|
|
||||||
- Setup wizard se preskače
|
|
||||||
- Bekapi su onemogućeni
|
|
||||||
- Promena lozinke je blokirana
|
|
||||||
- TOTP aktivacija nije dozvoljena
|
|
||||||
|
|
||||||
## Konfiguracija
|
|
||||||
|
|
||||||
Sva podešavanja su u `ntech.env` fajlu:
|
|
||||||
|
|
||||||
| Promenljiva | Opis | Podrazumevano |
|
| Promenljiva | Opis | Podrazumevano |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `NTECH_PORT` | Port servera | `8080` |
|
| `NTECH_PORT` | Port servera | `8080` |
|
||||||
| `NTECH_DB` | Putanja SQLite baze | `ntech.db` |
|
| `NTECH_SQLITE` | Putanja SQLite baze | `ntech.db` |
|
||||||
| `NTECH_DEMO` | Demo mod | `false` |
|
| `NTECH_ENV` | `development` / `production` / `demo` | `development` |
|
||||||
| `NTECH_DEV` | Dev mod (disk-first šabloni) | `false` |
|
| `NTECH_DOMAIN` | Domen sajta (za kolačiće i CSP) | `localhost` |
|
||||||
|
|
||||||
|
### Režimi rada
|
||||||
|
|
||||||
|
| Režim | Šabloni | Statika | Migracije |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `development` | Sa diska (hot-reload) | Sa diska | Sa diska |
|
||||||
|
| `production` | Embed | Embed (immutable keš) | Embed |
|
||||||
|
| `demo` | Embed | Embed (immutable keš) | Embed |
|
||||||
|
|
||||||
|
### Demo mod (`NTECH_ENV=demo`)
|
||||||
|
|
||||||
|
- Setup wizard se preskače — `ntech.env` se automatski kreira prazan
|
||||||
|
- Backup je onemogućen
|
||||||
|
- Promena lozinke je blokirana
|
||||||
|
- TOTP aktivacija nije dozvoljena
|
||||||
|
- Kolačići su Secure (kao u produkciji)
|
||||||
|
|
||||||
|
## Pokretanje
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# U produkciji
|
||||||
|
NTECH_ENV=production ./ntech
|
||||||
|
|
||||||
|
# Sa custom portom
|
||||||
|
NTECH_PORT=9090 ./ntech
|
||||||
|
|
||||||
|
# Demo mod
|
||||||
|
NTECH_ENV=demo ./ntech
|
||||||
|
```
|
||||||
|
|
||||||
|
## Struktura foldera posle build-a
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── ntech # binarni fajl (sve ugrađeno)
|
||||||
|
├── ntech.env # konfiguracija
|
||||||
|
├── ntech.db # SQLite baza (kreira se automatski)
|
||||||
|
└── logs/ # auth.log (opciono)
|
||||||
|
```
|
||||||
|
|
||||||
|
Ništa više nije potrebno — jedan fajl, jedna konfiguracija, jedna baza.
|
||||||
|
|||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
# Razvoj
|
||||||
|
|
||||||
|
## Okruženje
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Kloniraj
|
||||||
|
git clone git@git.vm-net.in.rs:Dasko/GoNtech.git
|
||||||
|
cd GoNtech
|
||||||
|
|
||||||
|
# 2. Podesi ENV
|
||||||
|
export $(grep -v '^#' ntech.env | xargs)
|
||||||
|
|
||||||
|
# 3. Pokreni u dev modu (šabloni sa diska — hot-reload nije ugrađen)
|
||||||
|
go run ./cmd/ntech
|
||||||
|
```
|
||||||
|
|
||||||
|
## Struktura koda po konvenciji
|
||||||
|
|
||||||
|
- **Handleri** — grupisani po domenu u `internal/handler/` (jedan fajl = jedan domen)
|
||||||
|
- **Modeli** — `internal/model/` — čiste strukture, nula poslovne logike
|
||||||
|
- **Middleware** — stacked na chi ruter u `main.go`
|
||||||
|
- **Šabloni** — komponentna struktura u `web/templates/komponente/`
|
||||||
|
- **Migracije** — numerisane prefiksom (`001_`, `002_`, ...), **append-only**, nikad ne renumber-ovati
|
||||||
|
|
||||||
|
## Konvencije koda
|
||||||
|
|
||||||
|
- **Indentacija:** tabovi (Go standard)
|
||||||
|
- **Formatiranje:** `gofmt -s -w .` pre svakog commit-a
|
||||||
|
- **Imenovanje:**
|
||||||
|
- Izvezeni: PascalCase (`MagacinHandler`, `ListaArtikala`)
|
||||||
|
- Neizvezeni: camelCase (`renderStranicu`, `parsirajFormu`)
|
||||||
|
- Fajlovi: `snake_case.go` po domenu (`magacin.go`, `servis.go`)
|
||||||
|
- **Importi:** stdlib → third-party → internal (blank-line razdvajanje)
|
||||||
|
- **Error handling:** eksplicitna provera, wrap kroz `fmt.Errorf("ntech: ...: %w", err)`
|
||||||
|
|
||||||
|
## Jezik
|
||||||
|
|
||||||
|
| Šta | Jezik |
|
||||||
|
|---|---|
|
||||||
|
| Korisnički interfejs | srpski (ekavica, latinica) |
|
||||||
|
| Poruke o greškama | srpski |
|
||||||
|
| Komentari u kodu | srpski |
|
||||||
|
| Git commit poruke | srpski |
|
||||||
|
| Dokumentacija | srpski |
|
||||||
|
| Imena promenljivih/funkcija | engleski |
|
||||||
|
| Nazivi SQL kolona/tabela | engleski |
|
||||||
|
| Nazivi ENV promenljivih | engleski |
|
||||||
|
|
||||||
|
## Testiranje
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Svi testovi
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Bez keša
|
||||||
|
go test ./... -v -count=1
|
||||||
|
|
||||||
|
# Specifični test
|
||||||
|
go test ./internal/handler/ -run TestPrijava -v
|
||||||
|
```
|
||||||
|
|
||||||
|
Test fajlovi su pored koda koji testiraju:
|
||||||
|
- `internal/handler/prijava_test.go`
|
||||||
|
- `internal/handler/parseformu_test.go`
|
||||||
|
- `internal/model/model_test.go`
|
||||||
|
- `internal/db/sqlite/izvestaj_test.go`
|
||||||
|
|
||||||
|
## Build i provera
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Formatiranje
|
||||||
|
gofmt -s -w .
|
||||||
|
|
||||||
|
# Statička analiza
|
||||||
|
go vet ./...
|
||||||
|
|
||||||
|
# Build
|
||||||
|
CGO_ENABLED=0 go build ./cmd/ntech
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migracije
|
||||||
|
|
||||||
|
Migracije su u `migrations/`, numerisane trocifrenim prefiksom:
|
||||||
|
|
||||||
|
```
|
||||||
|
001_kategorije.sql
|
||||||
|
002_artikli.sql
|
||||||
|
...
|
||||||
|
061_backfill_kategorija_kod.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pravila:**
|
||||||
|
- **Append-only** — nove migracije se dodaju, postojeće se nikad ne menjaju
|
||||||
|
- **Idempotentne** — koriste `CREATE TABLE IF NOT EXISTS`, `ALTER TABLE ... ADD COLUMN` (SQLite)
|
||||||
|
- **Redosled** — izvršavaju se po imenu fajla, rastuće
|
||||||
|
- **Transakcije** — SQLite implicitno, `migration` tabela prati koje su izvršene
|
||||||
|
|
||||||
|
## Grane i workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Nova funkcionalnost
|
||||||
|
git switch -c feature/kratak-opis
|
||||||
|
|
||||||
|
# Posle izmene
|
||||||
|
gofmt -s -w .
|
||||||
|
go vet ./...
|
||||||
|
git commit -m "Kratak opis promene"
|
||||||
|
git push -u origin feature/kratak-opis
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commit poruke
|
||||||
|
|
||||||
|
Format: srpski, kratak opis (max 50 karaktera), opciono detaljnije u telu.
|
||||||
|
|
||||||
|
Primeri:
|
||||||
|
```
|
||||||
|
Magacin: modal za premeštanje + ujednačen slajder na svim brauzerima
|
||||||
|
|
||||||
|
Bezbednosni audit i refaktoring: HP popravke, RBAC, flash poruke, go:embed, CSP
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dodavanje novog modula
|
||||||
|
|
||||||
|
1. **Model** — struktura u `internal/model/novi_modul.go`
|
||||||
|
2. **Repozitorijum** — interfejs u `internal/db/repository.go`, implementacija u `internal/db/sqlite/novi_modul.go`
|
||||||
|
3. **Migracija** — `migrations/062_novi_modul.sql`
|
||||||
|
4. **Handler** — `internal/handler/novi_modul.go`
|
||||||
|
5. **Rute** — dodati u `cmd/ntech/main.go`
|
||||||
|
6. **Dozvole** — dodati u `internal/middleware/dozvole.go`
|
||||||
|
7. **Šabloni** — `web/templates/stranice/novi_modul.html`
|
||||||
|
8. **CSS** — dodati u `web/static/main.css` (ako treba novi stil)
|
||||||
|
|
||||||
|
## Zavisnosti (go.mod)
|
||||||
|
|
||||||
|
| Direktna | Verzija | Uloga |
|
||||||
|
|---|---|---|
|
||||||
|
| `go-chi/chi/v5` | 5.3.0 | HTTP ruter + middleware |
|
||||||
|
| `google/uuid` | 1.6.0 | UUID generator (tokeni sesije) |
|
||||||
|
| `joho/godotenv` | 1.5.1 | `.env` fajl loader |
|
||||||
|
| `pquerna/otp` | 1.5.0 | TOTP 2FA |
|
||||||
|
| `x/crypto` | 0.52.0 | bcrypt |
|
||||||
|
| `modernc.org/sqlite` | 1.51.0 | SQLite bez CGO |
|
||||||
|
|
||||||
|
Sve ostalo je **Go standardna biblioteka**: `net/http`, `database/sql`, `html/template`, `crypto/rand`, `embed`, `io/fs`, `log/slog`.
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
# Wiki
|
||||||
|
|
||||||
|
- [Početna](Home)
|
||||||
|
- [Instalacija](Instalacija)
|
||||||
|
- [Funkcionalnosti](Funkcionalnosti)
|
||||||
|
- [Arhitektura](Arhitektura)
|
||||||
|
- [Bezbednost](Bezbednost)
|
||||||
|
- [Razvoj](Razvoj)
|
||||||
Reference in New Issue
Block a user