Merge: spoji dokumentaciju (asp-terminia/loggerservice) sa zatecenim produkcijskim radom
This commit is contained in:
commit
74d52e05c1
49
CLAUDE.md
49
CLAUDE.md
@ -736,6 +736,55 @@ DSN sadrži `multiStatements=true` — neophodno za izvršavanje migracija sa vi
|
|||||||
- Faza 8 (Light-Ticket): Integracija sa license serverom
|
- Faza 8 (Light-Ticket): Integracija sa license serverom
|
||||||
- ARV: Integracija sa license serverom
|
- ARV: Integracija sa license serverom
|
||||||
- ESIR: Integracija sa license serverom
|
- ESIR: Integracija sa license serverom
|
||||||
|
- **ASP-Terminia (4. proizvod):** vidi `docs/asp-terminia/` — radni paket Z1–Z6
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Radni paket: ASP-Terminia (ESP32 terminal)
|
||||||
|
|
||||||
|
Četvrti proizvod na ovom serveru — **ASP-Terminia** (Autonomni Sistem Prolaza),
|
||||||
|
ESP32-S3 terminal za kontrolu prolaza (bazeni/spa). Licenca je vezana za **MAC**
|
||||||
|
čipa (eFuse) preko `machine_fingerprint`, a ne za računar.
|
||||||
|
|
||||||
|
**Klijentska strana je već gotova** — firmware modul `licenca.h/.cpp` (od FW
|
||||||
|
0.10.0) radi aktivaciju, offline verifikaciju RSA potpisa i UI. Čeka samo da
|
||||||
|
proizvod i ključ postoje ovde na serveru.
|
||||||
|
|
||||||
|
Kompletan paket (postojeće stanje, tačan API ugovor koji firmware očekuje,
|
||||||
|
zadaci Z1–Z6, pravila rada na produkciji, enforcement pitanja za vlasnika):
|
||||||
|
|
||||||
|
**[`docs/asp-terminia/README.md`](docs/asp-terminia/README.md)** — čitati prvo.
|
||||||
|
|
||||||
|
Razlika u odnosu na ESIR/ARV/LIGHT_TICKET koja se mora poštovati:
|
||||||
|
- fingerprint je MAC uređaja, ne hash računara;
|
||||||
|
- uređaj radi **offline** — mrežna greška NIKAD ne sme da ga obori;
|
||||||
|
- postojeći proizvodi su ŽIVI u produkciji (~140 licenci) — ništa se ne lomi.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Radni paket: LoggerService (kontrolna ravan za flote terminala)
|
||||||
|
|
||||||
|
**Proširenje ovog servera**, ne novi projekat — RBAC stablo organizacija
|
||||||
|
(vendor → kupac → objekat, proizvoljna dubina), uređaji + heartbeat, servisni
|
||||||
|
grantovi, provisioning i katalog firmvera za ASP liniju terminala. Jedan Go
|
||||||
|
backend + baza, **dva lica** po hostu: `terminia.net` (vendor pogled) i
|
||||||
|
`loggerservice.com` (serviserski brend — kasnije samostalna aplikacija za
|
||||||
|
servisne firme, uređaji su joj samo modul).
|
||||||
|
|
||||||
|
RBAC model je **ZAKLJUČEN** sa vlasnikom (2026-07-24) — nije predlog za
|
||||||
|
raspravu, implementacione odluke (kolone/indeksi/Go slojevi) su slobodne.
|
||||||
|
|
||||||
|
Kompletan paket (vizija/MVP, RBAC šema i invarijante, heartbeat/discovery
|
||||||
|
formati koje uređaj već šalje, provisioning i katalog `.bin`-ova, zadaci
|
||||||
|
S1–S7, pravila rada na produkciji):
|
||||||
|
|
||||||
|
**[`docs/loggerservice/README.md`](docs/loggerservice/README.md)** — čitati prvo,
|
||||||
|
pa **[`docs/loggerservice/00-vizija-i-mvp.md`](docs/loggerservice/00-vizija-i-mvp.md)**
|
||||||
|
(merodavan nad ostalim fajlovima u paketu gde se razlikuju).
|
||||||
|
|
||||||
|
**Koordinacija sa `docs/asp-terminia/`:** isti backend, ista baza — zadaci S1–S2
|
||||||
|
(migracije RBAC šeme) idu POSLE Z1 (ASP proizvod na licenci) da se migracije ne
|
||||||
|
sudare; `devices.license_id` referencira licencu iz tog paketa.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
97
docs/asp-terminia/01-postojece-stanje.md
Normal file
97
docs/asp-terminia/01-postojece-stanje.md
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Postojeće stanje (provereno na serveru 2026-07-24, read-only)
|
||||||
|
|
||||||
|
## 1. Server 151 — pristup i raspored
|
||||||
|
|
||||||
|
- **SSH:** `ssh root@157.180.119.151` (host se javlja kao `MCLaud`, Ubuntu; ključ
|
||||||
|
`~/.ssh/id_ed25519` sa razvojne mašine; samo root). Hetzner, van LAN-a.
|
||||||
|
- Na ISTOM serveru žive i drugi sistemi — **ne dirati**:
|
||||||
|
- `terminia` SaaS platforma za klubove (portovi 8091/8092) — IME SLIČNO, ali
|
||||||
|
NIJE naš proizvod;
|
||||||
|
- Gitea;
|
||||||
|
- ESIR backend (fiskalizacija).
|
||||||
|
|
||||||
|
## 2. `dal-license-server` (naš deo)
|
||||||
|
|
||||||
|
- **Kod:** `/root/projects/dal-license-server` — Go, bez frameworka, MySQL.
|
||||||
|
- **Port:** 8090 lokalno; javno preko nginx sajta **`arv`**.
|
||||||
|
- **Javni URL (ovo firmware već koristi):** `https://157.180.119.151/license/api/v1/`
|
||||||
|
- HTTPS 443, cert `/etc/nginx/ssl/arv.crt`, `server_name` = IP → **self-signed**
|
||||||
|
(zato firmware koristi `setInsecure()`; odluku štiti RSA potpis odgovora).
|
||||||
|
- Na portu 80 je izložen samo validator API; ostalo redirect na 443.
|
||||||
|
- **Produkcioni obim:** 3 proizvoda (ESIR, ARV, LIGHT_TICKET — seed u
|
||||||
|
`migrations/002_seed_products.sql`), ~140 licenci, ~19 aktivacija. **ŽIVO.**
|
||||||
|
|
||||||
|
### Kripto
|
||||||
|
|
||||||
|
- **RSA-2048**, potpis **RSA-SHA256**.
|
||||||
|
- Privatni ključ: na serveru (`crypto/` + `.env` konfiguracija) — **NE čitati, NE
|
||||||
|
kopirati, NE logovati.**
|
||||||
|
- Javni ključ: `crypto/public.pem` — VEĆ ugrađen u firmware (`licenca.cpp`,
|
||||||
|
`LIC_PUBKEY`). Ako se ključ ikad rotira, MORA i novi firmware — to je krupna
|
||||||
|
odluka, ne radi se usput.
|
||||||
|
- Potpis stiže kao string: `"signature": "RSA-SHA256:<base64>"` uz `license` objekat.
|
||||||
|
**Potpisuje se serijalizacija license objekta kakvu server pošalje** — klijent
|
||||||
|
verifikuje SIROVE bajtove (ne re-serijalizuje). Svaka izmena serijalizacije na
|
||||||
|
serveru lomi sve klijente u polju.
|
||||||
|
|
||||||
|
### Klijentski API (bez autentikacije, rate-limited)
|
||||||
|
|
||||||
|
| Ruta | Telo zahteva | Odgovor |
|
||||||
|
|---|---|---|
|
||||||
|
| `POST /api/v1/activate` | `{license_key, machine_fingerprint, app_version, os, hostname}` | `{license:{... expires_at, grace_days, limits, features, customer ...}, signature}` |
|
||||||
|
| `POST /api/v1/validate` | `{license_key, machine_fingerprint}` | `{valid, expires_at, revoked}` |
|
||||||
|
| `POST /api/v1/deactivate` | (transfer/oslobađanje) | — |
|
||||||
|
|
||||||
|
Greške (JSON `error` polje): `INVALID_KEY`, `ALREADY_ACTIVATED`, `KEY_EXPIRED`,
|
||||||
|
`KEY_REVOKED`, `PRODUCT_MISMATCH`.
|
||||||
|
|
||||||
|
### Admin API
|
||||||
|
|
||||||
|
- Header `X-API-Key` (ključ u `.env` — od vlasnika se traži ključ ili da on sam
|
||||||
|
klikne u dashboardu; agent NE vadi ključ iz `.env` bez izričite dozvole).
|
||||||
|
- Kreira proizvode i licence. Postoji i htmx admin dashboard.
|
||||||
|
|
||||||
|
## 3. Firmware klijent — GOTOV (od FW 0.10.0)
|
||||||
|
|
||||||
|
Fajlovi u firmware repou (`firmware/10_terminia/`):
|
||||||
|
|
||||||
|
| Šta | Gde |
|
||||||
|
|---|---|
|
||||||
|
| Modul | `licenca.h` / `licenca.cpp` |
|
||||||
|
| Ugrađen javni ključ | `licenca.cpp` → `LIC_PUBKEY` (PEM) |
|
||||||
|
| NVS | namespace `lic`: `raw` (sirovi license JSON) + `sig`; ključ/URL u configu: `licKey`, `licUrl` (podrazumevano `https://157.180.119.151/license/api/v1`) |
|
||||||
|
| Web API (SPA) | `/api/license` (status), `/api/license-save` (ključ/URL), `/api/license-activate`, `/api/license-check` — sve samo admin |
|
||||||
|
| UI | SPA → Podešavanja → Uređaj → kartica „Licenca" (status, izdanje, korisnik, važi do, otisak uređaja, unos ključa, Aktiviraj, Proveri na serveru) |
|
||||||
|
|
||||||
|
### Kako klijent radi (bitno za server stranu)
|
||||||
|
|
||||||
|
1. **Otisak:** `machine_fingerprint = "sha256:" + sha256hex(MAC)` gde je MAC oblika
|
||||||
|
`AA:BB:CC:DD:EE:FF` (velika slova, dvotačke). ⚠️ **PRETPOSTAVKA** — proveriti u
|
||||||
|
Go kodu servera kako računa fingerprint za poređenje; ako se razlikuje, uskladiti
|
||||||
|
NA SERVERU dokumentaciju/verifikaciju za ASP proizvod ILI javiti pa menjamo FW
|
||||||
|
(zadatak Z2).
|
||||||
|
2. **Aktivacija:** POST `/activate`; iz odgovora isecₐ SIROVE bajtove `license`
|
||||||
|
objekta (brojanje zagrada, string-safe), verifikuje `signature` RSA‑SHA256
|
||||||
|
javnim ključem, čuva sirovo+potpis u NVS.
|
||||||
|
3. **Svaki start (offline):** ponovo verifikuje potpis iz NVS + poredi
|
||||||
|
`machine_fingerprint` iz licence sa svojim + proverava `expires_at`
|
||||||
|
(poređenje po datumu `YYYY-MM-DD`; prazno = trajna; nepouzdan sat → grejs,
|
||||||
|
nikad pad).
|
||||||
|
4. **Statusi na uređaju:** `nema / vazi / grejs / los-potpis / drugi-uredjaj / istekla`.
|
||||||
|
5. **ENFORCEMENT NE POSTOJI** — uređaj samo prikazuje status. Gašenje funkcija bez
|
||||||
|
licence je odluka vlasnika (zadatak Z4).
|
||||||
|
|
||||||
|
### Polja koja klijent čita iz `license` objekta
|
||||||
|
|
||||||
|
`edition`, `customer` (ili `customer_name`), `expires_at`, `license_id` (ili `key`),
|
||||||
|
`grace_days`, `machine_fingerprint`. Nepoznata dodatna polja ne smetaju (parsira se
|
||||||
|
ciljano). `limits`/`features` se JOŠ ne čitaju — planirano uz enforcement.
|
||||||
|
|
||||||
|
## 4. Šta NE postoji (i pripada zadacima)
|
||||||
|
|
||||||
|
- Proizvod **„ASP-Terminia"** na serveru — ne postoji (zadatak Z1).
|
||||||
|
- Nijedan ASP ključ — ne postoji (Z1/Z2).
|
||||||
|
- Domen `licenca.terminia.net` + Let's Encrypt — ne postoji (Z3; domen kupuje vlasnik).
|
||||||
|
- Enforcement na uređaju + `features`/`limits` semantika za ASP (Z4/Z5).
|
||||||
|
- FW potpisivanje `.bin` + katalog firmvera (Z6; preduslov za auto-OTA, vidi i
|
||||||
|
ASP repo: `serviseri/03-provisioning-i-fw-katalog.md`).
|
||||||
72
docs/asp-terminia/02-api-ugovor-firmware.md
Normal file
72
docs/asp-terminia/02-api-ugovor-firmware.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# API ugovor koji FIRMWARE očekuje (merodavno za server stranu)
|
||||||
|
|
||||||
|
> Ovo je ugovor kakav je VEĆ implementiran u firmveru (0.10.0+). Server strana se
|
||||||
|
> usklađuje sa OVIM (ili se promena dogovara pa se menja i firmware — nikad tiho).
|
||||||
|
|
||||||
|
## 1. Aktivacija
|
||||||
|
|
||||||
|
```
|
||||||
|
POST {licUrl}/activate
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"license_key": "ASP-XXXX-XXXX-XXXX",
|
||||||
|
"machine_fingerprint": "sha256:<sha256hex od 'AA:BB:CC:DD:EE:FF'>",
|
||||||
|
"app_version": "0.11.0",
|
||||||
|
"os": "esp32s3",
|
||||||
|
"hostname": "TERM-001"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Očekivan odgovor `200`:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"license": {
|
||||||
|
... polja licence, među njima:
|
||||||
|
"machine_fingerprint": "sha256:...", ← ako postoji, MORA biti otisak uređaja
|
||||||
|
"expires_at": "2027-01-01T00:00:00Z", ← ili prazno/odsutno = trajna
|
||||||
|
"grace_days": 14,
|
||||||
|
"edition": "...", "customer": "...", "license_id": "..."
|
||||||
|
},
|
||||||
|
"signature": "RSA-SHA256:<base64 potpisa nad SIROVIM bajtovima license objekta>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Kritično:** potpis se računa nad TAČNIM bajtovima `license` objekta onako kako su
|
||||||
|
poslati u HTTP telu (od `{` do para `}`). Firmware iseca taj isečak iz sirovog tela
|
||||||
|
(string-safe brojanje zagrada) i verifikuje. **Nikakvo „lepše" formatiranje odgovora
|
||||||
|
naknadno** — menja bajtove → lomi potpis.
|
||||||
|
|
||||||
|
## 2. Provera
|
||||||
|
|
||||||
|
```
|
||||||
|
POST {licUrl}/validate
|
||||||
|
{ "license_key": "...", "machine_fingerprint": "sha256:..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
Očekivano: `{"valid": true/false, "expires_at": "...", "revoked": true/false}`.
|
||||||
|
Firmware tumači: `revoked` → poruka „licenca OPOZVANA na serveru"; `!valid` →
|
||||||
|
„server kaže da licenca ne važi". Mrežna/HTTP greška NE dira lokalno stanje.
|
||||||
|
|
||||||
|
## 3. Greške
|
||||||
|
|
||||||
|
HTTP ≠ 200 → firmware čita JSON polje `"error"` i prikazuje ga korisniku uz HTTP kod
|
||||||
|
(npr. `HTTP 403 (KEY_REVOKED)`). Poželjno je da server uvek vraća `{"error":"KOD"}`.
|
||||||
|
|
||||||
|
## 4. Ponašanje klijenta koje server sme da računa
|
||||||
|
|
||||||
|
- Klijent NIKAD ne šalje privatne podatke — samo gore navedena polja.
|
||||||
|
- Klijent poštuje odgovor samo ako je potpis valjan; HTTP 200 bez valjanog potpisa
|
||||||
|
se odbacuje (zaštita i od MITM na self-signed kanalu).
|
||||||
|
- Nakon uspešne aktivacije klijent NE zove server pri svakom startu — offline
|
||||||
|
verifikacija iz NVS. `validate` se zove ručno iz UI (kasnije: periodično uz sync).
|
||||||
|
|
||||||
|
## 5. Otvorena pitanja za usklađivanje (uneti odgovore ovde)
|
||||||
|
|
||||||
|
| # | Pitanje | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| P1 | Tačna formula `machine_fingerprint` u Go kodu (ulazni string, velika/mala slova, dvotačke?) | ⬜ proveriti u kodu servera |
|
||||||
|
| P2 | Da li `activate` na VEĆ aktiviran ključ + ISTI fingerprint vraća licencu ponovo (re-aktivacija posle brisanja NVS) ili `ALREADY_ACTIVATED`? Za teren nam treba: isti uređaj sme ponovo. | ⬜ proveriti/podesiti |
|
||||||
|
| P3 | Tačan skup polja `license` objekta za ASP proizvod (limits/features imena) | ⬜ definisati uz Z1 |
|
||||||
|
| P4 | `expires_at` format (ISO8601?) — firmware poredi prvih 10 znakova (`YYYY-MM-DD`) | ⬜ potvrditi |
|
||||||
102
docs/asp-terminia/03-zadaci.md
Normal file
102
docs/asp-terminia/03-zadaci.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# Zadaci (redom; svaki ima kriterijum prihvatanja)
|
||||||
|
|
||||||
|
> Redosled je namerno ovakav: prvo ono što odblokira kraj-do-kraja test sa pravim
|
||||||
|
> uređajem, pa infrastruktura (domen), pa odluke, pa katalog firmvera.
|
||||||
|
> **Pre svakog write-a na produkciju: backup + izričita dozvola vlasnika**
|
||||||
|
> (vidi 04-pravila-rada.md).
|
||||||
|
|
||||||
|
## Z1 — Proizvod „ASP-Terminia" na serveru
|
||||||
|
|
||||||
|
**Šta:** kroz migraciju (SQL u `migrations/`, po uzoru na `002_seed_products.sql`)
|
||||||
|
ili admin API/dashboard napraviti proizvod:
|
||||||
|
|
||||||
|
- `code`/`name`: **ASP-Terminia** (linija: ASP — Autonomni Sistem Prolaza)
|
||||||
|
- `key_prefix`: **`ASP-`**
|
||||||
|
- limiti (predlog, potvrditi sa vlasnikom): `max_citaca` (npr. basic 2 / pro 8),
|
||||||
|
`max_kartica` (basic 2000 / pro 30000)
|
||||||
|
- features (predlog bitova, uskladiti sa ASP repo docs/18 §6.3): provera karte, izdavanje,
|
||||||
|
štampa, katalog-sa-servera, anti-passback, sync, OTA
|
||||||
|
|
||||||
|
**Kako:** pogledati kako su postojeći proizvodi definisani (tabela `products` +
|
||||||
|
seed migracija) i uraditi ISTIM šablonom. Ne menjati postojeće redove.
|
||||||
|
|
||||||
|
**Prihvatanje:** proizvod vidljiv u dashboardu; postojeći proizvodi netaknuti
|
||||||
|
(COUNT pre = posle za njihove licence/aktivacije); backup fajl postoji.
|
||||||
|
|
||||||
|
## Z2 — Test ključ + kraj-do-kraja aktivacija sa uređajem
|
||||||
|
|
||||||
|
**Šta:** izdati 1 test licencu ASP-Terminia (trajnu ili 90 dana) i proći ceo tok sa
|
||||||
|
PRAVIM uređajem (MAC `1C:DB:D4:A6:A4:60`, dostupan vlasniku):
|
||||||
|
|
||||||
|
1. Odgovoriti na P1–P4 iz 02-api-ugovor-firmware.md (čitanjem Go koda).
|
||||||
|
2. Ako se fingerprint formula razlikuje od FW pretpostavke → zapisati TAČNU formulu
|
||||||
|
u 02 i javiti (menja se FW, jedna linija).
|
||||||
|
3. Vlasnik unese ključ u SPA (Podešavanja → Uređaj → Licenca) → Aktiviraj.
|
||||||
|
4. Proveriti: status „važi" na uređaju; restart uređaja → i dalje „važi" (offline);
|
||||||
|
`Proveri na serveru` → potvrda.
|
||||||
|
5. Negativni testovi: pogrešan ključ (INVALID_KEY prikazan), opozvan ključ
|
||||||
|
(revoked prikazan), ključ aktiviran na drugom fingerprint-u (PRODUCT/ALREADY
|
||||||
|
ponašanje po P2).
|
||||||
|
|
||||||
|
**Prihvatanje:** svih 5 koraka dokumentovano (šta je poslato/vraćeno, bez tajni).
|
||||||
|
|
||||||
|
## Z3 — Domen `licenca.terminia.net` + Let's Encrypt
|
||||||
|
|
||||||
|
**Preduslov:** vlasnik kupi/podesi `terminia.net` (A rekord → 157.180.119.151).
|
||||||
|
Dok toga nema, zadatak stoji — NE blokira Z1/Z2 (ide se preko IP-a).
|
||||||
|
|
||||||
|
**Šta:**
|
||||||
|
- nginx: novi `server_name licenca.terminia.net` blok koji proksira ISTI backend
|
||||||
|
(port 8090) — **ne dirati postojeći `arv` sajt** (postojeći klijenti ostaju na IP).
|
||||||
|
- certbot (Let's Encrypt) za taj domen; auto-obnova.
|
||||||
|
- Provera da `https://licenca.terminia.net/api/v1/validate` radi sa validnim lancem.
|
||||||
|
- U firmware repou: `cfg.licUrl` podrazumevana vrednost se menja na domen, a
|
||||||
|
`setInsecure()` se zamenjuje validacijom lanca (ugraditi ISRG root CA) — to je
|
||||||
|
FW izmena: pripremiti tačan diff/uputstvo, primenu radi firmware strana.
|
||||||
|
|
||||||
|
**Prihvatanje:** domen služi API sa validnim certom; stari IP pristup i dalje radi.
|
||||||
|
|
||||||
|
## Z4 — Odluke o enforcement-u (PRIPREMA, vlasnik odlučuje)
|
||||||
|
|
||||||
|
**Šta:** pripremiti kratak dokument sa predlogom (na bazi ASP repo docs/18 §5) i KONKRETNIM
|
||||||
|
pitanjima za vlasnika:
|
||||||
|
|
||||||
|
1. Bez licence: potpuno odbijanje ILI ograničen rad (predlog: web/konfiguracija/ručno
|
||||||
|
upravljanje rade; izdavanje+štampa+provera karata ne rade uz jasnu poruku)?
|
||||||
|
2. Grejs: 14 dana posle isteka (podesivo)?
|
||||||
|
3. Nepouzdan sat = grejs (nikad pad) — potvrda?
|
||||||
|
4. Koje funkcije vezati za `features` bite, koje za `limits`?
|
||||||
|
|
||||||
|
**Prihvatanje:** dokument `05-enforcement-odluke.md` u ovom folderu sa odgovorima
|
||||||
|
vlasnika upisanim (ne pretpostavkama). Implementacija enforcement-a u FW ide TEK
|
||||||
|
posle toga (firmware strana).
|
||||||
|
|
||||||
|
## Z5 — Obnova/opoziv tok (sloj B)
|
||||||
|
|
||||||
|
**Šta:** definisati i (posle Z4) implementirati periodičnu proveru na uređaju:
|
||||||
|
`validate` na N sati (podesivo), `revoked` → od kad važi ograničenje (posle grejsa),
|
||||||
|
server može vratiti NOVU licencu (produženje) — proveriti da li postojeći server to
|
||||||
|
podržava kroz `activate` re-poziv ili treba dorada (`check` endpoint po ASP repo docs/18 §4).
|
||||||
|
|
||||||
|
**Prihvatanje:** opisan tačan tok + potvrđeno na test ključu (produženje + opoziv).
|
||||||
|
|
||||||
|
## Z6 — Potpisivanje firmvera + veza sa katalogom
|
||||||
|
|
||||||
|
**Šta:** `.bin` slike se potpisuju RSA lancem (ODVOJEN ključ od licenci — preporuka
|
||||||
|
ASP repo docs/18 §7.1; ako vlasnik odluči isti, dokumentovati). Server strana: skripta za
|
||||||
|
potpis + čuvanje potpisa uz artefakt. Katalog (nginx `fw.terminia.net`) i
|
||||||
|
kratkotrajni download URL su u paketu `ASP repo: serviseri/03` — ovde samo potpisni deo.
|
||||||
|
|
||||||
|
**Prihvatanje:** komanda za potpis + format potpisa dokumentovan + probni potpisan
|
||||||
|
`.bin` verifikovan javnim ključem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Redosled i zavisnosti (sažetak)
|
||||||
|
|
||||||
|
```
|
||||||
|
Z1 (proizvod) ──► Z2 (kraj-do-kraja) ──► Z4 (odluke) ──► [FW enforcement — firmware strana]
|
||||||
|
└──► Z5 (obnova/opoziv)
|
||||||
|
Z3 (domen) — čim vlasnik obezbedi DNS (nezavisno)
|
||||||
|
Z6 (potpis .bin) — pre auto-OTA kataloga (koordinacija sa ASP repo: serviseri/)
|
||||||
|
```
|
||||||
41
docs/asp-terminia/04-pravila-rada.md
Normal file
41
docs/asp-terminia/04-pravila-rada.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Pravila rada — PROČITATI PRE PRVOG SSH-a
|
||||||
|
|
||||||
|
> Server 151 je **PRODUKCIJA** sa živim klijentima (fiskalizacija ESIR, ARV,
|
||||||
|
> LIGHT_TICKET licence, terminia SaaS). Greška ovde košta prave korisnike.
|
||||||
|
|
||||||
|
## Tvrda pravila (bez izuzetka)
|
||||||
|
|
||||||
|
1. **Read-only dok se ne odobri write.** Istraživanje (čitanje koda, šeme,
|
||||||
|
nginx konfiga) je slobodno. SVAKI write na produkciju (SQL, fajl, nginx reload,
|
||||||
|
restart servisa) traži **izričito DA vlasnika za taj konkretan korak**.
|
||||||
|
2. **Backup pre svake izmene.** Minimum:
|
||||||
|
- baza: `mysqldump` ciljanih tabela (ili cele baze) u `/root/backup/` sa datumom;
|
||||||
|
- fajl/konfig: kopija sa `.bak-YYYYMMDD` sufiksom pre izmene.
|
||||||
|
U izveštaju navesti tačnu putanju backupa.
|
||||||
|
3. **Migracije, ne ad-hoc SQL.** Izmene šeme/podataka idu kao numerisan fajl u
|
||||||
|
`migrations/` (šablon = postojeće migracije), idempotentno gde je moguće.
|
||||||
|
4. **Tajne se ne diraju:** `.env`, privatni ključevi (`crypto/` osim `public.pem`),
|
||||||
|
lozinke — ne čitati sadržaj, ne ispisivati u logove/izveštaje. Ako je za posao
|
||||||
|
potreban admin API ključ — **tražiti od vlasnika**, ne vaditi ga iz `.env`.
|
||||||
|
5. **Ne dirati tuđe servise:** `terminia` SaaS (8091/8092), Gitea, ESIR — ni
|
||||||
|
konfiguraciju, ni bazu, ni portove. Nginx: menjati SAMO dodavanjem novih
|
||||||
|
server blokova; postojeći `arv` sajt ostaje kakav jeste.
|
||||||
|
6. **Nikad destruktivno:** bez DROP/DELETE nad postojećim podacima; bez rotacije
|
||||||
|
ključeva; bez gašenja servisa duže od reload-a.
|
||||||
|
7. **Kompatibilnost klijenata u polju:** serijalizacija `license` objekta i format
|
||||||
|
potpisa se NE menjaju (lome verifikaciju na uređajima). Nova polja se DODAJU,
|
||||||
|
postojeća se ne preimenuju.
|
||||||
|
|
||||||
|
## Kako se testira bez rizika
|
||||||
|
|
||||||
|
- Novi proizvod (ASP-Terminia) i test ključevi su IZOLOVANI redovi — ne dodiruju
|
||||||
|
postojeće. Test aktivacije gađaju samo ASP ključeve.
|
||||||
|
- Za probe API-ja koristiti `curl` sa test ključem; nikad tuđe ključeve.
|
||||||
|
- Ako postoji mogućnost lokalnog pokretanja (Go build + test MySQL šema) — poželjno
|
||||||
|
prvo lokalno, pa migracija na produkciju.
|
||||||
|
|
||||||
|
## Izveštavanje
|
||||||
|
|
||||||
|
Posle svake sesije: šta je pročitano, šta je menjano (tačne putanje/tabele),
|
||||||
|
gde je backup, šta je sledeće, otvorena pitanja za vlasnika. Odgovore na pitanja
|
||||||
|
upisivati u `02-api-ugovor-firmware.md` §5 i `05-enforcement-odluke.md`.
|
||||||
16
docs/asp-terminia/05-enforcement-odluke.md
Normal file
16
docs/asp-terminia/05-enforcement-odluke.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Enforcement — odluke vlasnika (popunjava se, NE pretpostavlja)
|
||||||
|
|
||||||
|
> Uređaj trenutno licencu samo PRIKAZUJE. Pre nego što ijedna funkcija sme da se
|
||||||
|
> gasi bez licence, vlasnik odgovara na sledeće (predlozi iz ASP repo docs/18 §5 u zagradi):
|
||||||
|
|
||||||
|
| # | Pitanje | Predlog | ODLUKA VLASNIKA |
|
||||||
|
|---|---|---|---|
|
||||||
|
| E1 | Ponašanje bez važeće licence? | Ograničen rad: web/konfiguracija/ručno upravljanje izlazima RADE (bezbednost + mogućnost unosa licence); izdavanje karata, štampa i provera karata NE RADE uz jasnu poruku „Uređaj nije licenciran". Alternativa: potpuno odbijanje (rizik zaglavljene rampe). | ⬜ |
|
||||||
|
| E2 | Grejs period posle isteka? | 14 dana, podesivo (`licGraceDays`); tokom grejsa pun rad + uporno upozorenje u UI i logu. | ⬜ |
|
||||||
|
| E3 | Nepouzdan sat (RTC bez baterije)? | Tretirati kao grejs (radi + upozorava), NIKAD pad — inače nestanak struje bez NTP obara ispravan terminal. | ⬜ |
|
||||||
|
| E4 | `features` biti — šta se licencira? | provera-karte / izdavanje / štampa / katalog-sa-servera / anti-passback / sync / OTA (servisni kanal, AP, web, prijava NIKAD ne zavise od licence). | ⬜ |
|
||||||
|
| E5 | `limits` — šta se ograničava? | max_kartica u katalogu, max_citaca u registru; „basic" vs „pro" izdanje. | ⬜ |
|
||||||
|
| E6 | Model prodaje? | Trajna (expires prazno) i pretplata (30/60/90 d uz obnovu kroz validate/check) — podržati oba. | ⬜ |
|
||||||
|
|
||||||
|
Kad su odluke upisane: implementacija ide u firmware (`licenca` modul + provere u
|
||||||
|
`tickets/cards/printer` putanjama) — radi firmware strana, uz host testove.
|
||||||
60
docs/asp-terminia/README.md
Normal file
60
docs/asp-terminia/README.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# ASP-Terminia — licenciranje (radni paket)
|
||||||
|
|
||||||
|
> **Misija:** dovesti licenciranje ASP‑Terminia terminala do produkcije na
|
||||||
|
> postojećem `dal-license-server`-u (server 151), bez lomljenja ijednog
|
||||||
|
> postojećeg klijenta (ESIR/ARV/LIGHT_TICKET su ŽIVI u produkciji).
|
||||||
|
>
|
||||||
|
> **Jezik projekta:** srpski (ekavica), latinica. Kod/komentari na serveru su Go —
|
||||||
|
> komentari srpski ASCII.
|
||||||
|
|
||||||
|
> **Poreklo:** paket je nastao u firmverskom repou (`asp-terminia/licence/`) i
|
||||||
|
> prebačen ovde 2026-07-24, jer je sav posao serverski. Firmverska strana je
|
||||||
|
> gotova i ne dira se. „**ASP repo**" u tekstu znači
|
||||||
|
> <https://git.terminia.net/djuka/asp-terminia>.
|
||||||
|
|
||||||
|
## Sadržaj foldera
|
||||||
|
|
||||||
|
| Fajl | Šta je unutra |
|
||||||
|
|---|---|
|
||||||
|
| [01-postojece-stanje.md](01-postojece-stanje.md) | šta VEĆ postoji: server (Go/MySQL/nginx/kripto) i firmware klijent (gotov!) |
|
||||||
|
| [02-api-ugovor-firmware.md](02-api-ugovor-firmware.md) | tačan ugovor koji firmware očekuje — polja, potpis, greške |
|
||||||
|
| [03-zadaci.md](03-zadaci.md) | šta treba uraditi, redom, sa kriterijumima prihvatanja |
|
||||||
|
| [04-pravila-rada.md](04-pravila-rada.md) | **OBAVEZNO pročitati prvo** — produkcija, backup, tajne, granice |
|
||||||
|
| [05-enforcement-odluke.md](05-enforcement-odluke.md) | pitanja E1–E6 za vlasnika: šta uređaj radi bez licence |
|
||||||
|
|
||||||
|
## Kontekst u 5 redova
|
||||||
|
|
||||||
|
1. **Šta se licencira:** ESP32‑S3 terminal za kontrolu prolaza (bazeni/spa), proizvod
|
||||||
|
**„ASP‑Terminia"** (Autonomni Sistem Prolaza). Licenca je vezana za **MAC** čipa
|
||||||
|
(eFuse, nepromenljiv) preko `machine_fingerprint`.
|
||||||
|
2. **Server već postoji:** `dal-license-server` (Go, MySQL, RSA‑2048/SHA‑256) na
|
||||||
|
`157.180.119.151`, u produkciji sa 3 proizvoda / ~140 licenci / ~19 aktivacija.
|
||||||
|
3. **Firmware klijent je GOTOV** (modul `licenca.h/.cpp` u firmveru, od verzije 0.10.0):
|
||||||
|
aktivacija, offline verifikacija potpisa javnim ključem, UI. Čeka samo proizvod
|
||||||
|
i ključ na serveru.
|
||||||
|
4. **Dva sloja:** potpisana lokalna licenca (radi offline) + serverska provera/obnova
|
||||||
|
(opoziv). Mrežna greška NIKAD ne obara uređaj.
|
||||||
|
5. **Domeni (kupuje vlasnik):** `licenca.terminia.net` → 157.180.119.151 (Let's
|
||||||
|
Encrypt). Dok domena nema, uređaj radi preko IP‑a sa `setInsecure()` — odluku
|
||||||
|
ionako štiti RSA potpis, ne TLS kanal.
|
||||||
|
|
||||||
|
## Šta agent NE radi
|
||||||
|
|
||||||
|
- Ne donosi odluku o **ponašanju uređaja bez licence** (gašenje funkcija) — to je
|
||||||
|
odluka vlasnika (pitanja u 03-zadaci.md §Z4). Priprema se predlog, čeka se DA.
|
||||||
|
- Ne dira postojeće proizvode (ESIR, ARV, LIGHT_TICKET) niti `terminia` SaaS
|
||||||
|
(portovi 8091/8092 na istom serveru — TO NIJE OVAJ POSAO).
|
||||||
|
- Ne čita niti loguje tajne (`.env`, privatni ključ) — vidi 04-pravila-rada.md.
|
||||||
|
|
||||||
|
## Veze ka ostatku projekta
|
||||||
|
|
||||||
|
- **Firmware repo (ASP repo):** <https://git.terminia.net/djuka/asp-terminia>
|
||||||
|
(klijent: `firmware/10_terminia/licenca.h`, `licenca.cpp`; javni ključ ugrađen).
|
||||||
|
Firmverska strana je GOTOVA od FW 0.10.0 — ovaj paket je ne dira.
|
||||||
|
- Dizajn/istorijat: `ASP repo docs/18-licenciranje-po-mac.md` (pisan PRE nego što je pravi
|
||||||
|
API poznat — gde se razlikuju, **ovaj folder je merodavan**), `ASP repo docs/21-platforma-i-provisioning.md`.
|
||||||
|
- Serviserska strana (RBAC, uređaji, LoggerService): `ASP repo: serviseri/` —
|
||||||
|
isti backend, drugi radni paket; koordinacija preko šeme baze (vidi tamo 01).
|
||||||
|
*(Taj paket se seli u ovaj repo posebno — vidi dogovor od 2026-07-24.)*
|
||||||
|
- Serverski kod i konvencije: [../../CLAUDE.md](../../CLAUDE.md),
|
||||||
|
[../ARCHITECTURE.md](../ARCHITECTURE.md), [../../API.md](../../API.md).
|
||||||
63
docs/loggerservice/00-vizija-i-mvp.md
Normal file
63
docs/loggerservice/00-vizija-i-mvp.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# LoggerService — vizija i MVP (DOGOVORENO sa vlasnikom, 2026-07-24 uveče)
|
||||||
|
|
||||||
|
> Ovaj dokument je ISPRED svih ostalih u folderu: definiše ŠTA se gradi.
|
||||||
|
> Ostali fajlovi (01–05) opisuju delove; gde se razlikuju, ovaj je merodavan.
|
||||||
|
|
||||||
|
## Vizija
|
||||||
|
|
||||||
|
**LoggerService je samostalna aplikacija za servisne firme** — praćenje opreme,
|
||||||
|
ljudi i posla. **Kreće interno (vlasnikova firma), kasnije se PRODAJE drugim
|
||||||
|
servisnim firmama** (od prvog dana više-firmi u bazi — RBAC stablo iz 01).
|
||||||
|
|
||||||
|
Naši pametni terminali su **MODUL „Uređaji"** koji se **uključuje u opcijama**
|
||||||
|
po firmi — nisu centar aplikacije.
|
||||||
|
|
||||||
|
## Jezgro (uvek prisutno)
|
||||||
|
|
||||||
|
| # | Celina | Dogovor |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | **Firme/nalozi** | vlasnikova firma prva; multi-tenant od starta (orgs stablo + 4 role iz `01-rbac-model.md`) |
|
||||||
|
| 2 | **Oprema — KIČMA aplikacije** | tačno se prati **SVAKI komad** (serijalizovan): identitet, istorija, kod kog klijenta je ugrađen. Zalihe/delovi (količine) takođe postoje, ali serijalizovana oprema je suština |
|
||||||
|
| 3 | **Klijenti** | zavode se **pri otvaranju PRVOG naloga** za tog klijenta; uz klijenta se vezuju oprema, reklamacije i servisi |
|
||||||
|
| 4 | **Nalozi za posao** | tipovi: **ugradnja / servis / reklamacija** → dodeljen serviseru → ishod se upisuje u ISTORIJU OPREME |
|
||||||
|
| 5 | **Zaduženja** | ko je od servisera šta poneo/vratio (naslanja se na opremu/robu) |
|
||||||
|
| — | Evidencija rada (sati/obračun) | **KASNIJE** — vlasnik već ima softver za to |
|
||||||
|
|
||||||
|
## Modul „Uređaji" (uključiv u opcijama) — MVP obim
|
||||||
|
|
||||||
|
Za sada **najosnovnije**, sa dve tvrde odrednice od vlasnika:
|
||||||
|
|
||||||
|
1. **Upravljanje firmverom**: biće **VIŠE firmvera** (linija ASP: ASP-Terminia,
|
||||||
|
parking, garderoba…) — modul zna koji proizvod/verzija ide na koji uređaj i
|
||||||
|
omogućava podešavanje/dodelu FW (naslanja se na katalog `fw.terminia.net`,
|
||||||
|
vidi `03-provisioning-i-fw-katalog.md`).
|
||||||
|
2. **Direktno vezan za licencni server** (`dal-license-server`): modul
|
||||||
|
komunicira sa njim (proizvodi, licence po MAC-u, entitlements) — NE gradi
|
||||||
|
svoju paralelnu evidenciju licenci. Vidi paket [`docs/asp-terminia/`](../asp-terminia/README.md).
|
||||||
|
|
||||||
|
Uz to iz postojećeg: uređaji se sami javljaju (heartbeat/discovery formati u
|
||||||
|
`02-uredjaji-heartbeat-discovery.md`) → status u modulu.
|
||||||
|
|
||||||
|
**Prirodna sprega sa jezgrom:** terminal JE serijalizovana oprema (celina 2) —
|
||||||
|
modul „Uređaji" je specijalizacija komada opreme koji se uz to i sam javlja.
|
||||||
|
|
||||||
|
## Šta ovo menja u zadacima (04-zadaci.md)
|
||||||
|
|
||||||
|
- S1 (šema) se PROŠIRUJE jezgrom: `oprema` (serijalizovana, istorija),
|
||||||
|
`klijenti`, `nalozi` (ugradnja/servis/reklamacija), `zaduzenja`, `zalihe` —
|
||||||
|
pored već dogovorenih orgs/users/devices/entitlements/grants.
|
||||||
|
- „Dashboard" (S4) = MVP LoggerService aplikacije (jezgro), ne samo pregled
|
||||||
|
uređaja; modul „Uređaji" = toggle po firmi (entitlements).
|
||||||
|
- Infrastruktura je POSTAVLJENA 2026-07-24: `loggerservice.com` (+www) služi sa
|
||||||
|
151 (`/var/www/loggerservice`, nginx sajt `loggerservice`, Let's Encrypt);
|
||||||
|
`licenca.terminia.net` → backend 8090; `fw.terminia.net` → `/var/www/fw`.
|
||||||
|
|
||||||
|
## Redosled gradnje (predlog za agenta)
|
||||||
|
|
||||||
|
1. Šema jezgra (migracije, multi-tenant od starta) — uz postojeći MySQL.
|
||||||
|
2. Minimalni tok koji vlasnikova firma odmah koristi: **klijent (pri prvom
|
||||||
|
nalogu) → nalog (ugradnja) → komad opreme zaveden kod klijenta → istorija**.
|
||||||
|
3. Zaduženja.
|
||||||
|
4. Modul „Uređaji" (toggle): lista terminala + status + dodela FW + veza na
|
||||||
|
licencni server.
|
||||||
|
5. Prodajna spremnost (onboarding drugih firmi) — tek kad interno proradi.
|
||||||
81
docs/loggerservice/01-rbac-model.md
Normal file
81
docs/loggerservice/01-rbac-model.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# RBAC model — ZAKLJUČENO sa vlasnikom 2026-07-24
|
||||||
|
|
||||||
|
> Ovo NIJE predlog za raspravu — model je dogovoren. Slobodne su implementacione
|
||||||
|
> odluke (imena kolona, indeksi, Go slojevi), ne semantika.
|
||||||
|
|
||||||
|
## 1. Stablo organizacija
|
||||||
|
|
||||||
|
Hijerarhijsko stablo proizvoljne dubine; **koren = Terminia (vendor)**. Primer:
|
||||||
|
|
||||||
|
```
|
||||||
|
Terminia (vendor, koren)
|
||||||
|
├─ DAL (kupac) ← DAL je KUPAC, ne vendor!
|
||||||
|
│ ├─ Bazen Gradski (objekat/pod-firma)
|
||||||
|
│ └─ Spa Centar X
|
||||||
|
├─ Distributer Jug
|
||||||
|
│ └─ Firma Y
|
||||||
|
│ └─ Bazen Z
|
||||||
|
└─ ServisPlus (org tipa "serviser") ← eksterni serviseri kao zaseban org
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tabele (dogovorene; tipove/indekse odrediti po MySQL praksi projekta)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
orgs(id, parent_id NULL za koren, tip ENUM('vendor','distributer','firma','objekat','serviser',...),
|
||||||
|
naziv, status)
|
||||||
|
|
||||||
|
users(id, org_id, rola ENUM('owner','admin','operator','viewer'), ime, email, auth...)
|
||||||
|
|
||||||
|
devices(id, org_id, mac UNIQUE, product_code, fw_verzija, status, license_id NULL,
|
||||||
|
naziv, poslednji_heartbeat, ip)
|
||||||
|
|
||||||
|
entitlements(id, org_id, product_code, max_uredjaja, fw_kanal, vazi_do)
|
||||||
|
|
||||||
|
service_grants(id, serviser_org_id, ciljni_org_id NULL / ciljni_device_id NULL,
|
||||||
|
dozvole SET('registruj','flesuj_fw','odrzavanje'), vazi_do)
|
||||||
|
|
||||||
|
sale_grants(id, prodavac_user_id ili org_id, plan/proizvod opseg, vazi_do)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Role (iste na SVAKOM čvoru stabla)
|
||||||
|
|
||||||
|
| Rola | Prava |
|
||||||
|
|---|---|
|
||||||
|
| **Owner** | pun vlasnik org-a i CELOG podstabla |
|
||||||
|
| **Admin** | upravljanje unutar org-a |
|
||||||
|
| **Operator** | operativni rad (kasir na terminalu / interni serviser) |
|
||||||
|
| **Viewer** | samo pregled |
|
||||||
|
|
||||||
|
## 3. Invarijante (moraju biti sprovedene u kodu, ne samo dogovoru)
|
||||||
|
|
||||||
|
1. **Delegacija sa podskupom:** dete-org dobija PODSKUP prava roditelja; zbir
|
||||||
|
entitlements dece ≤ roditeljev max (`max_uredjaja` itd.).
|
||||||
|
2. **Vidljivost (row-level):** org vidi SEBE + celo svoje PODSTABLO. Ništa iznad,
|
||||||
|
ništa bočno. Svaki upit ide kroz filter podstabla (CTE po `parent_id` ili
|
||||||
|
materijalizovana putanja — implementaciona odluka).
|
||||||
|
3. **Serviser ≠ vlasnik:** service_grant daje PRISTUP (registruj/flešuj/održavaj)
|
||||||
|
nad tuđim org-om/uređajima, sa rokom — NE daje vlasništvo ni uvid u naplatu.
|
||||||
|
4. **Prodavac ne kreira** zone/cenovnik (to su entiteti objekta na uređaju) —
|
||||||
|
`sale_grants` samo kaže KOJE planove sme da prodaje.
|
||||||
|
|
||||||
|
## 4. Odnos sa licencama
|
||||||
|
|
||||||
|
- `devices.license_id` → licenca iz postojećeg licencnog sistema ([`docs/asp-terminia/`](../asp-terminia/README.md)).
|
||||||
|
- `entitlements` je „koliko čega sme org" — licencni server pri izdavanju ključa
|
||||||
|
proverava entitlement (npr. max_uredjaja).
|
||||||
|
- **Kill-switch lanac:** opoziv licence (revoked) + istek + offline grejs na
|
||||||
|
uređaju + sigurno stanje — mehanika na uređaju, okidač na serveru.
|
||||||
|
|
||||||
|
## 5. Dva lica, jedan mozak
|
||||||
|
|
||||||
|
- **Terminia lice** (`terminia.net`): vendor pogled — proizvodi, licence, katalog FW.
|
||||||
|
- **LoggerService lice** (`loggerservice.com`): serviserski pogled — moji grantovi,
|
||||||
|
uređaji do kojih smem, registracija/flešovanje/održavanje, nalozi servisera.
|
||||||
|
- Tehnički: isti Go backend + baza; lice se bira po hostu (nginx `server_name`) i/ili
|
||||||
|
po tipu org-a prijavljenog korisnika. Brendiranje (logo/boje/naziv) po licu.
|
||||||
|
|
||||||
|
## 6. Pristup „sve odmah / UI inkrementalno"
|
||||||
|
|
||||||
|
Dogovor: **pun MODEL/šema odmah** (migracije za sve tabele iznad), a **UI dashboard
|
||||||
|
inkrementalno** (prvo orgs+devices+users, pa grantovi). Uređaji teku paralelno —
|
||||||
|
heartbeat prijem (02) može i pre punog UI-ja.
|
||||||
60
docs/loggerservice/02-uredjaji-heartbeat-discovery.md
Normal file
60
docs/loggerservice/02-uredjaji-heartbeat-discovery.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Uređaji: šta terminal VEĆ šalje (formati) i šta server treba da primi
|
||||||
|
|
||||||
|
> Firmware strana je GOTOVA (FW 0.10.0+, modul `firmware/10_terminia/svc.cpp`).
|
||||||
|
> Server strana (prijemnik) NE postoji — to je zadatak u 04.
|
||||||
|
|
||||||
|
## 1. LAN discovery (za servisera na licu mesta / PC alat)
|
||||||
|
|
||||||
|
- Uređaj UVEK sluša **UDP port 46464**.
|
||||||
|
- Upit: paket čiji sadržaj počinje ASCII stringom `TERMINIA?` (može broadcast).
|
||||||
|
- Odgovor (unicast pošiljaocu), JSON u jednom paketu:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"app":"TERMINIA","name":"TERM-001","mac":"1C:DB:D4:A6:A4:60",
|
||||||
|
"ip":"192.168.123.1","fw":"0.11.0","serial":"1CDBD4A6A460"}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `serial` = korisnički serijski broj ili MAC bez dvotačaka ako serijski nije zadat.
|
||||||
|
- `ip` = adresa interfejsa (ETH ima prednost nad WiFi ako je povezan).
|
||||||
|
- Provereno uživo 2026-07-24 (PowerShell UDP upit → odgovor za <100 ms).
|
||||||
|
|
||||||
|
**Upotreba:** serviserski PC/telefon alat „nađi sve uređaje na mreži" —
|
||||||
|
broadcast `TERMINIA?` na 255.255.255.255:46464 i skupi odgovore.
|
||||||
|
|
||||||
|
## 2. Heartbeat (za `devices.status` / `poslednji_heartbeat`)
|
||||||
|
|
||||||
|
- Uređaj šalje na **`cfg.svcLogHost` (`ip:port`, UDP)** na **`cfg.svcHbSeconds`**
|
||||||
|
sekundi (0 = isključeno). Podešava se u SPA: Podešavanja → Servis.
|
||||||
|
- Format = isti identitet kao discovery + oznaka:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"app":"TERMINIA","name":"...","mac":"...","ip":"...","fw":"...","serial":"...","hb":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Server treba:** UDP prijemnik (port po dogovoru, npr. 46465 javno) koji:
|
||||||
|
1. parsira JSON, ključ = `mac`;
|
||||||
|
2. upsert u `devices` (`fw_verzija`, `ip`, `poslednji_heartbeat = NOW()`);
|
||||||
|
3. status: `online` ako je heartbeat mlađi od 3×interval, inače `offline`
|
||||||
|
(interval po uređaju čuvati ili pretpostaviti default).
|
||||||
|
|
||||||
|
⚠️ Heartbeat trenutno gađa LOKALNI prijemnik (LAN). Za javni server preko
|
||||||
|
interneta: NAT izlaz radi (UDP out), ali treba javna IP/port + odluka da li UDP
|
||||||
|
ili prelazak na HTTPS POST uz sync (v. „Zahtevi ka firmveru" u 04 ako se menja).
|
||||||
|
|
||||||
|
## 3. Dnevnik rada (dbg) na server
|
||||||
|
|
||||||
|
- Isti `cfg.svcLogHost` prima i tok dbg linija (tekst, UDP, više linija po paketu,
|
||||||
|
do ~1 KB) kada je uključeno „Šalji dnevnik rada na server".
|
||||||
|
- Namena: teren-dijagnostika („šta se dešava kad pravi problem").
|
||||||
|
- **Server treba:** isti prijemnik upisuje sirove linije u fajl po MAC-u/danu
|
||||||
|
(rotacija!) — NE u bazu (obim). Toggle je na uređaju, podrazumevano isključen —
|
||||||
|
server ne sme pretpostaviti da tok uvek stiže.
|
||||||
|
|
||||||
|
## 4. Identitet uređaja — izvor istine
|
||||||
|
|
||||||
|
- **MAC iz eFuse** (`1C:DB:D4:A6:A4:60` je razvojni uređaj) — nepromenljiv,
|
||||||
|
ključ za `devices.mac` i za licencu.
|
||||||
|
- `serial` je korisnički (promenljiv) — samo za prikaz.
|
||||||
|
- Registracija uređaja u org (ko ga poseduje) ide kroz RBAC/provisioning (03),
|
||||||
|
ne kroz heartbeat — heartbeat samo ažurira POZNATE uređaje (nepoznat MAC:
|
||||||
|
zabeleži u „neregistrovani" listu za kasnije usvajanje, ne pravi org sam).
|
||||||
62
docs/loggerservice/03-provisioning-i-fw-katalog.md
Normal file
62
docs/loggerservice/03-provisioning-i-fw-katalog.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Provisioning i katalog firmvera
|
||||||
|
|
||||||
|
## 1. Dvofazni dogovor (ASP repo `docs/21-platforma-i-provisioning.md` §4)
|
||||||
|
|
||||||
|
| Faza | Model | Stanje |
|
||||||
|
|---|---|---|
|
||||||
|
| MVP | **Model B** — USB flešer alat na PC-u | alat ne postoji (zadatak); bootstrap FW postoji i pokriva veći deo potrebe |
|
||||||
|
| Kasnije | **Model A** — bootstrap FW + mrežni OTA sa kataloga | bootstrap URAĐEN (lokalni upload); auto-download čeka domen |
|
||||||
|
|
||||||
|
## 2. Šta VEĆ postoji u firmware repou
|
||||||
|
|
||||||
|
### Inicijalni (bootstrap) FW — `firmware/00_inicijalni/`
|
||||||
|
|
||||||
|
- Flešuje se USB-om JEDNOM na novu ploču (isti fqbn/particije kao pun FW:
|
||||||
|
`app3M_fat9M_16MB` → ota_0 + ota_1 + ffat).
|
||||||
|
- Ponašanje: digne otvoren AP **`terminia-setup`** → `http://10.10.10.10` →
|
||||||
|
stranica prikaže **MAC** (identitet za licencu/registraciju) i primi PUN firmware
|
||||||
|
kao `.bin` (OTA upload) → restart u TERMINIA.
|
||||||
|
- BEZ interneta/domena/prijave (fizički pristup = servisni pristup; AP živi samo
|
||||||
|
dok je bootstrap aktivan).
|
||||||
|
- **Model A dorada (kad proradi `fw.terminia.net`):** bootstrap dobija i opciju
|
||||||
|
„preuzmi sa kataloga" — javi se mozgu sa MAC-om, dobije potpisan URL, skine,
|
||||||
|
verifikuje potpis, upiše. (Zahtev ka firmveru — zapisati u 04 kad server bude spreman.)
|
||||||
|
|
||||||
|
### Pun FW — OTA upload već radi
|
||||||
|
|
||||||
|
- SPA → Podešavanja → Uređaj → „Nadogradnja firmvera": upload `.bin`
|
||||||
|
(ruta `/api/ota`, samo admin). `.bin` artefakt: `build/ota/10_terminia.ino.bin`.
|
||||||
|
|
||||||
|
## 3. Katalog firmvera — dogovorena arhitektura („opcija C — sredina")
|
||||||
|
|
||||||
|
- **Mozak = licencni/entitlement server:** na zahtev uređaja (MAC + licenca +
|
||||||
|
entitlement `fw_kanal`) izdaje **kratkotrajni potpisani download URL**.
|
||||||
|
- **Mišić = statični nginx** na `fw.terminia.net`: servira **RSA-potpisane `.bin`**
|
||||||
|
artefakte. Kasnije `CNAME → CDN` bez izmene na uređaju.
|
||||||
|
- **Uređaj UVEK verifikuje RSA potpis slike** — URL/kanal je samo usmeravanje.
|
||||||
|
- **Pravilo (tvrdo):** FW potpisivanje je OBAVEZNO PRE puštanja kataloga/auto-OTA.
|
||||||
|
Potpisni deo je u paketu [`docs/asp-terminia/03-zadaci.md`](../asp-terminia/03-zadaci.md) §Z6 (isti kripto-lanac tim).
|
||||||
|
|
||||||
|
### Predlog rasporeda kataloga (implementaciona sloboda)
|
||||||
|
|
||||||
|
```
|
||||||
|
fw.terminia.net/
|
||||||
|
asp-terminia/
|
||||||
|
stable/ 10_terminia-0.11.0.bin 10_terminia-0.11.0.sig manifest.json
|
||||||
|
beta/ ...
|
||||||
|
<sledeci-proizvod>/...
|
||||||
|
```
|
||||||
|
|
||||||
|
`manifest.json`: verzija, sha256, potpis, min-bootstrap, changelog — tačan format
|
||||||
|
uskladiti sa firmware stranom pre implementacije (zapisati u 04 „Zahtevi ka firmveru").
|
||||||
|
|
||||||
|
## 4. Model B alat (USB flešer) — obim
|
||||||
|
|
||||||
|
PC alat (može PowerShell/Python skript u prvoj verziji) koji:
|
||||||
|
1. detektuje COM port ploče,
|
||||||
|
2. flešuje **inicijalni FW** (`esptool`/`arduino-cli upload`),
|
||||||
|
3. ispiše MAC (čita ga sa serijskog boot ispisa) za registraciju/licencu,
|
||||||
|
4. (opciono) odmah otpremi i pun `.bin` kroz `terminia-setup` AP.
|
||||||
|
|
||||||
|
Prihvatanje: nova ploča od kutije do „terminia" AP-a za < 5 minuta bez ručnih koraka
|
||||||
|
osim kablova.
|
||||||
82
docs/loggerservice/04-zadaci.md
Normal file
82
docs/loggerservice/04-zadaci.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Zadaci (redom; svaki sa kriterijumom prihvatanja)
|
||||||
|
|
||||||
|
> ⚠️ **AŽURIRANO 2026-07-24 uveče:** vizija je proširena — LoggerService je
|
||||||
|
> aplikacija za servisne firme (vidi `00-vizija-i-mvp.md`, MERODAVNO). Zadaci
|
||||||
|
> ispod važe uz izmene: S1 šema se širi jezgrom (oprema/klijenti/nalozi/
|
||||||
|
> zaduženja/zalihe), S4 „dashboard" = MVP aplikacije (jezgro + tok
|
||||||
|
> klijent→nalog→oprema→istorija), modul „Uređaji" = toggle po firmi, minimalno:
|
||||||
|
> lista+status+dodela FW (više FW proizvoda!) + direktna veza na licencni server.
|
||||||
|
|
||||||
|
> Koordinacija: zadaci S1–S2 (šema) idu POSLE [`docs/asp-terminia/03-zadaci.md`](../asp-terminia/03-zadaci.md) Z1
|
||||||
|
> (ASP proizvod) da se ne sudaraju migracije — ili u dogovoru sa tim agentom
|
||||||
|
> (isti `migrations/` folder, numeracija se nastavlja).
|
||||||
|
> **Svaki write na produkciju: backup + izričita dozvola (05-pravila-rada.md).**
|
||||||
|
|
||||||
|
## S1 — Migracije RBAC šeme (pun model odmah)
|
||||||
|
|
||||||
|
**Šta:** migracije za tabele iz `01-rbac-model.md`: `orgs`, `users` (proširenje ili
|
||||||
|
nova, zavisno od postojeće admin auth šeme — PROVERITI šta postoji), `devices`,
|
||||||
|
`entitlements`, `service_grants`, `sale_grants`. Seed: koren `Terminia (vendor)` +
|
||||||
|
org `DAL (kupac)` pod njim.
|
||||||
|
|
||||||
|
**Prihvatanje:** migracije prolaze na kopiji baze pa na produkciji; postojeće
|
||||||
|
tabele/podaci netaknuti; ER skica (tekstualna) u ovom folderu.
|
||||||
|
|
||||||
|
## S2 — Registracija razvojnog uređaja
|
||||||
|
|
||||||
|
**Šta:** ubaciti razvojni terminal u `devices`: MAC `1C:DB:D4:A6:A4:60`,
|
||||||
|
product_code ASP-Terminia, org DAL (ili test org), fw_verzija tekuća.
|
||||||
|
|
||||||
|
**Prihvatanje:** red postoji; vidljiv u (privremenom) pregledu/upitu po podstablu.
|
||||||
|
|
||||||
|
## S3 — Heartbeat prijemnik
|
||||||
|
|
||||||
|
**Šta:** UDP servis (Go, deo istog procesa ili poseban systemd unit) po
|
||||||
|
`02-uredjaji-heartbeat-discovery.md` §2: prima JSON, upsert `devices`
|
||||||
|
(fw/ip/poslednji_heartbeat), nepoznat MAC → tabela/lista „neregistrovani".
|
||||||
|
Dbg tok (§3): upis u fajlove po MAC/danu sa rotacijom (logrotate ili u kodu).
|
||||||
|
|
||||||
|
**Prihvatanje:** sa razvojnog uređaja (vlasnik podesi Podešavanja → Servis na
|
||||||
|
javnu adresu:port) stiže heartbeat i `devices.poslednji_heartbeat` se pomera;
|
||||||
|
status online/offline izračunljiv.
|
||||||
|
|
||||||
|
## S4 — Minimalni dashboard (LoggerService lice, inkrement 1)
|
||||||
|
|
||||||
|
**Šta:** na postojeći htmx admin sloj dodati: lista org-stabla, lista uređaja
|
||||||
|
(status/fw/heartbeat), lista neregistrovanih + dugme „usvoji u org".
|
||||||
|
Vidljivost striktno po podstablu prijavljenog (invarijanta 2 iz 01).
|
||||||
|
|
||||||
|
**Prihvatanje:** vlasnik (koren) vidi sve; test user u DAL vidi samo DAL podstablo.
|
||||||
|
|
||||||
|
## S5 — Service grants (inkrement 2)
|
||||||
|
|
||||||
|
**Šta:** CRUD za `service_grants` + provera u middleware-u (serviser vidi/dira
|
||||||
|
samo ciljeve granta, u roku važenja). Org tipa „serviser" + test serviser nalog.
|
||||||
|
|
||||||
|
**Prihvatanje:** serviser nalog vidi TAČNO grantovane uređaje; posle isteka
|
||||||
|
`vazi_do` — ništa.
|
||||||
|
|
||||||
|
## S6 — Katalog firmvera (posle Z6 potpisa)
|
||||||
|
|
||||||
|
**Šta:** nginx `fw.terminia.net` statika (ili do domena: putanja na postojećem
|
||||||
|
sajtu) + mozak-endpoint „daj mi URL za moju verziju" (MAC+licenca → potpisan
|
||||||
|
kratkotrajni URL). Format manifesta USKLADITI sa firmware stranom pre implementacije.
|
||||||
|
|
||||||
|
**Prihvatanje:** curl tok: uređaj-identitet → URL → download `.bin` + `.sig`;
|
||||||
|
verifikacija potpisa javnim ključem prolazi.
|
||||||
|
|
||||||
|
## S7 — Model B alat (USB flešer)
|
||||||
|
|
||||||
|
Po `03-provisioning-i-fw-katalog.md` §4. Može i ranije (nezavisan od servera).
|
||||||
|
|
||||||
|
**Prihvatanje:** nova ploča → `terminia-setup` AP za <5 min, MAC ispisan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zahtevi ka firmveru (dopisivati; radi firmware strana, ne ovaj agent)
|
||||||
|
|
||||||
|
| # | Zahtev | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| F1 | Heartbeat na JAVNU adresu: potvrditi UDP kroz NAT u realnom okruženju ili preći na HTTPS POST (upisati odluku) | ⬜ |
|
||||||
|
| F2 | Manifest format za auto-OTA (bootstrap Model A) — dogovoriti polja | ⬜ |
|
||||||
|
| F3 | (po potrebi) registracioni token pri prvom javljanju uređaja | ⬜ |
|
||||||
33
docs/loggerservice/05-pravila-rada.md
Normal file
33
docs/loggerservice/05-pravila-rada.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Pravila rada — PROČITATI PRE PRVOG SSH-a
|
||||||
|
|
||||||
|
> Server 151 (`157.180.119.151`, SSH `root@`, ključ `~/.ssh/id_ed25519`) je
|
||||||
|
> **PRODUKCIJA**: dal-license-server (3 proizvoda, ~140 licenci), terminia SaaS
|
||||||
|
> (8091/8092), ESIR, Gitea. Greška košta prave korisnike.
|
||||||
|
|
||||||
|
## Tvrda pravila (identična paketu [`docs/asp-terminia/04-pravila-rada.md`](../asp-terminia/04-pravila-rada.md))
|
||||||
|
|
||||||
|
1. **Read-only dok se ne odobri write.** Svaki write (SQL/fajl/nginx/systemd)
|
||||||
|
traži izričito DA vlasnika za taj konkretan korak.
|
||||||
|
2. **Backup pre izmene:** `mysqldump` u `/root/backup/` sa datumom; fajlovi kao
|
||||||
|
`.bak-YYYYMMDD`. Putanju backupa navesti u izveštaju.
|
||||||
|
3. **Migracije, ne ad-hoc SQL** — numerisano u `migrations/`, uskladiti brojeve sa
|
||||||
|
agentom paketa paket `docs/asp-terminia/` (isti repo, drugi folder).
|
||||||
|
4. **Tajne:** `.env`, privatni ključevi, lozinke — ne čitati, ne ispisivati.
|
||||||
|
Admin API ključ tražiti od vlasnika.
|
||||||
|
5. **Ne dirati:** terminia SaaS, ESIR, Gitea, postojeći nginx `arv` sajt
|
||||||
|
(samo DODAVATI nove server blokove).
|
||||||
|
6. **Nikad destruktivno**; bez restartovanja tuđih servisa.
|
||||||
|
7. **Kompatibilnost:** postojeći licencni API i serijalizacija se ne menjaju.
|
||||||
|
|
||||||
|
## Specifično za ovaj paket
|
||||||
|
|
||||||
|
- RBAC middleware NE sme da zaključa postojeći admin dashboard (vendor koren mora
|
||||||
|
uvek moći da uđe) — dodavati permisivno pa stezati.
|
||||||
|
- UDP prijemnik: otvoriti port kroz firewall SAMO za potreban port; ne otvarati
|
||||||
|
opsege.
|
||||||
|
- Fajlovi dbg dnevnika: rotacija obavezna (ne pustiti da napune disk).
|
||||||
|
|
||||||
|
## Izveštavanje
|
||||||
|
|
||||||
|
Posle sesije: šta je čitano/menjano (putanje/tabele), backup putanje, sledeći korak,
|
||||||
|
pitanja za vlasnika. Odluke upisivati u odgovarajući md ovde (ne ostavljati u ćaskanju).
|
||||||
61
docs/loggerservice/README.md
Normal file
61
docs/loggerservice/README.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# LoggerService — radni paket za agenta (kontrolna ravan)
|
||||||
|
|
||||||
|
> **Misija:** izgraditi serversku kontrolnu ravan za flote terminala — RBAC stablo
|
||||||
|
> organizacija, uređaji + heartbeat, servisni grantovi, provisioning i katalog
|
||||||
|
> firmvera — kao **proširenje postojećeg `dal-license-server`-a** (server 151).
|
||||||
|
> Jedan backend, **dva lica**: `terminia.net` = vendor (licence/fw),
|
||||||
|
> **`loggerservice.com` = serviserski brend** (isti mozak, drugi izgled).
|
||||||
|
>
|
||||||
|
> **Jezik projekta:** srpski (ekavica), latinica; Go komentari srpski ASCII.
|
||||||
|
|
||||||
|
> **Poreklo:** paket je nastao u firmverskom repou (`asp-terminia/serviseri/`) i
|
||||||
|
> prebačen ovde 2026-07-25 — isti backend/baza kao paket `docs/asp-terminia/`
|
||||||
|
> (licenciranje), koordinacija migracija mora ići kroz jedan repo. Firmverska
|
||||||
|
> strana (`svc.cpp`, discovery/heartbeat) je gotova i ostaje u firmverskom repou.
|
||||||
|
> „**ASP repo**" u tekstu znači <https://git.terminia.net/djuka/asp-terminia>.
|
||||||
|
|
||||||
|
## Sadržaj foldera
|
||||||
|
|
||||||
|
| Fajl | Šta je unutra |
|
||||||
|
|---|---|
|
||||||
|
| [00-vizija-i-mvp.md](00-vizija-i-mvp.md) | **ČITATI PRVO** — LoggerService je aplikacija za servisne firme (oprema/klijenti/nalozi/zaduženja); uređaji su MODUL; MVP obim (dogovor 2026-07-24 uveče) |
|
||||||
|
| [01-rbac-model.md](01-rbac-model.md) | ZAKLJUČEN model: stablo orgs, 4 role, grantovi, invarijante, šeme tabela |
|
||||||
|
| [02-uredjaji-heartbeat-discovery.md](02-uredjaji-heartbeat-discovery.md) | šta uređaj VEĆ šalje (UDP formati) i šta server treba da primi |
|
||||||
|
| [03-provisioning-i-fw-katalog.md](03-provisioning-i-fw-katalog.md) | Model B/A, postojeći bootstrap FW, katalog `.bin`-ova, potpisani URL |
|
||||||
|
| [04-zadaci.md](04-zadaci.md) | zadaci redom sa kriterijumima prihvatanja |
|
||||||
|
| [05-pravila-rada.md](05-pravila-rada.md) | **OBAVEZNO prvo** — produkcija, backup, granice |
|
||||||
|
|
||||||
|
## Kontekst u 6 redova
|
||||||
|
|
||||||
|
1. **Proizvod:** ESP32‑S3 terminali (linija ASP; prvi proizvod ASP‑Terminia —
|
||||||
|
kontrola prolaza za bazene/spa). Terminal je autonoman i radi offline;
|
||||||
|
platforma je **opcioni sloj** za flote (više vlasnika, servis, prodaja, OTA).
|
||||||
|
2. **Hijerarhija:** **Terminia = vendor/koren stabla** (vlasnik svega);
|
||||||
|
**DAL = KUPAC** (org pod Terminijom) — ne vendor.
|
||||||
|
3. **Serviseri NISU nivo u stablu** nego **grant preko stabla** (razdvaja
|
||||||
|
vlasništvo od servisnog pristupa) — detalji u 01.
|
||||||
|
4. **Server:** isti `dal-license-server` (Go/MySQL, produkcija!) — RBAC se DODAJE
|
||||||
|
migracijama; koordinacija sa paketom [`docs/asp-terminia/`](../asp-terminia/README.md) (deli bazu i admin sloj).
|
||||||
|
5. **Uređaj već emituje:** LAN discovery (UDP 46464, upit `TERMINIA?` → JSON
|
||||||
|
identitet), heartbeat (UDP JSON na podesiv interval), dbg dnevnik (UDP) —
|
||||||
|
formati u 02. `devices.status` na serveru se hrani iz heartbeat-a.
|
||||||
|
6. **Brend:** `loggerservice.com` domen kupuje vlasnik (kredencijali kod njega);
|
||||||
|
do tada se sve gradi i testira na IP-u / internim rutama.
|
||||||
|
|
||||||
|
## Šta agent NE radi
|
||||||
|
|
||||||
|
- Ne kupuje/podešava domene (vlasnik) — ali priprema nginx blokove da se uključe.
|
||||||
|
- Ne dira postojeće proizvode/licence, `terminia` SaaS, ESIR, Gitea-u.
|
||||||
|
- Ne implementira ništa na SAMOM uređaju (firmware) — uređaj je gotov za ovaj obim;
|
||||||
|
ako zatreba izmena FW, zapisati ZAHTEV u 04 (sekcija „Zahtevi ka firmveru").
|
||||||
|
|
||||||
|
## Veze
|
||||||
|
|
||||||
|
- **Firmware repo (ASP repo):** <https://git.terminia.net/djuka/asp-terminia>
|
||||||
|
(servisni kanal: `firmware/10_terminia/svc.cpp`; bootstrap: `firmware/00_inicijalni/`).
|
||||||
|
Firmverska strana je GOTOVA za ovaj obim — ovaj paket je ne dira.
|
||||||
|
- Istorijat odluka: ASP repo `docs/21-platforma-i-provisioning.md` (dogovor 2026-07-24).
|
||||||
|
- Licencna strana: [`docs/asp-terminia/`](../asp-terminia/README.md) (isti server; Z-zadaci tamo idu PRE RBAC UI-ja
|
||||||
|
jer odblokiraju uređaje na terenu).
|
||||||
|
- Serverski kod i konvencije: [../../CLAUDE.md](../../CLAUDE.md),
|
||||||
|
[../ARCHITECTURE.md](../ARCHITECTURE.md), [../../API.md](../../API.md).
|
||||||
Loading…
Reference in New Issue
Block a user