dal-license-server/CLAUDE.md
djuka dc0114e4b7 Inicijalni commit: kompletna implementacija + dokumentacija + testovi
- Kompletna Go implementacija licencnog servera (19 Go fajlova)
- Klijentski API: activate, deactivate, validate
- Admin API: CRUD licence, stats, audit log
- Admin dashboard: htmx + Go templates
- RSA-2048 potpisivanje licencnih podataka
- Rate limiting i API key autentifikacija
- MySQL migracije i seed podaci (ESIR, ARV, LIGHT_TICKET)
- Unit testovi: keygen, crypto, model, middleware (24 testa)
- Dokumentacija: SPEC.md, ARCHITECTURE.md, SETUP.md, API.md, TESTING.md, README.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 07:42:25 +00:00

26 KiB

DAL License Server — CLAUDE.md

Pregled

Univerzalni licencni server za sve DAL proizvode: ESIR, ARV, Light-Ticket, i buduće aplikacije.

Zamenjuje stari esir-license-server (koji niko ne koristi u produkciji). Arhitektura prema ARV licencnoj dokumentaciji (/root/projects/arv/TASKS/Licenciranje-Starter.md), ali univerzalna — podržava N proizvoda.

Princip: License Server je jedini koji ima RSA private key. Klijentske aplikacije imaju samo public key. Server potpisuje licence, klijenti verifikuju potpis. Niko osim servera ne može da kreira validnu licencu.

KRITIČNO — Testiranje: Ova aplikacija je osnova budućeg poslovanja DAL-a. Mora biti rigorozno testirana svim poznatim tipovima testova:

  • Unit testovi — svaki servis, model, helper, middleware
  • Integration testovi — kompletni API flow-ovi (activate → validate → deactivate → re-activate)
  • Security testovi — SQL injection, brute force, API key bypass, tampered signatures, invalid inputs
  • Edge case testovi — expired licence, grace period granice, perpetual licence, race conditions
  • Regression testovi — svaki bug fix mora imati test koji potvrdjuje ispravku
  • Load testovi — rate limiting pod pritiskom, konkurentni zahtevi
  • Nikad ne smanjivati pokrivenost testovima. Svaka nova funkcionalnost MORA imati test pre merge-a.

Kako radi

┌─────────────────────────────────────────────────────┐
│                 DAL LICENSE SERVER                    │
│                 port: 8090                            │
│                                                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────┐  │
│  │ Admin API │  │ Klijent  │  │ Admin Dashboard  │  │
│  │ (CRUD)   │  │ API      │  │ (htmx)           │  │
│  └──────────┘  └──────────┘  └──────────────────┘  │
│        │              │               │              │
│        └──────────────┼───────────────┘              │
│                       │                              │
│              ┌────────┴────────┐                     │
│              │   MySQL baza    │                     │
│              │  license_db     │                     │
│              └─────────────────┘                     │
│                                                      │
│  RSA-2048 Private Key (samo ovde, nikad ne izlazi)  │
└─────────────────────────────────────────────────────┘
        ▲               ▲               ▲
        │ HTTPS         │ HTTPS         │ HTTPS
        │               │               │
   ┌────┴────┐    ┌────┴────┐    ┌────┴────────┐
   │  ESIR   │    │   ARV   │    │ Light-Ticket │
   │ klijent │    │ klijent │    │   klijent    │
   └─────────┘    └─────────┘    └──────────────┘
   Svaki ima RSA public key ugrađen u binary
   Svaki čuva license.enc lokalno

Tok — od kupovine do rada

1. Admin kreira licencu

Admin → Dashboard → Nova licenca
  → Bira proizvod (ESIR / ARV / LIGHT_TICKET)
  → Unosi: firma, email, tip (MONTHLY/ANNUAL/PERPETUAL), limiti
  → Server generiše ključ: {PREFIX}-XXXX-XXXX-XXXX-XXXX
  → Ključ se šalje klijentu (email ili ručno)

2. Klijent aktivira

Klijent instalira aplikaciju
  → Unese licencni ključ u Settings
  → Aplikacija šalje serveru: ključ + machine_fingerprint + app_version + OS
  → Server proverava: ključ validan? nije aktiviran drugde? nije istekao?
  → Server potpisuje licencne podatke RSA private key-em
  → Vraća: licencni JSON + RSA potpis
  → Klijent kreira license.enc (AES-256-GCM + RSA potpis)
  → Aplikacija radi OFFLINE dok licenca važi

3. Svakodnevni rad (offline)

Aplikacija se pokrene
  → Čita license.enc sa diska
  → Dekriptuje (AES sa machine fingerprint)
  → Proverava RSA potpis (public key ugrađen u binary)
  → Proverava fingerprint (isti računar?)
  → Proverava rok (nije istekao?)
  → SVE OK → normalan rad
  → Internet NIJE potreban za svakodnevni rad

4. Opciona online provera

Jednom dnevno (ako ima internet):
  → Aplikacija šalje: ključ + fingerprint
  → Server proverava: nije revocirana?
  → Ako je revocirana na serveru → klijent invalidira lokalnu licencu
  → Ako nema interneta → preskače, radi sa lokalnim fajlom

Tech stack

Komponenta Tehnologija
Backend Go + net/http (Go 1.22+ routing)
Admin UI htmx + Go html/template
Baza MySQL 8.0
Kripto RSA-2048 (potpis), AES-256-GCM (enkripcija na klijentima)
Auth API key za admin API, session za dashboard

Go struktura projekta

dal-license-server/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── config/
│   │   └── config.go              # .env konfiguracija
│   ├── model/
│   │   ├── license.go             # License, Product, LicenseType
│   │   ├── activation.go          # Activation model
│   │   ├── audit.go               # Audit log model
│   │   └── request.go             # Request/Response structs
│   ├── repository/
│   │   ├── license_repo.go        # License CRUD
│   │   ├── activation_repo.go     # Activation CRUD
│   │   └── audit_repo.go          # Audit log
│   ├── service/
│   │   ├── license_service.go     # Poslovna logika
│   │   ├── activation_service.go  # Aktivacija/deaktivacija
│   │   ├── crypto_service.go      # RSA potpisivanje
│   │   └── keygen.go              # Generisanje ključeva
│   ├── handler/
│   │   ├── client_handler.go      # API za klijentske app-e (activate, deactivate, validate)
│   │   ├── admin_handler.go       # Admin CRUD API
│   │   ├── dashboard_handler.go   # Admin dashboard (htmx)
│   │   └── helpers.go             # JSON/error helpers
│   ├── middleware/
│   │   ├── auth.go                # API key + session auth
│   │   └── ratelimit.go           # Rate limiting
│   └── router/
│       └── router.go
├── templates/
│   ├── layout/
│   │   └── base.html
│   ├── pages/
│   │   ├── login.html
│   │   ├── dashboard.html         # Statistike po proizvodu
│   │   ├── licenses.html          # Lista licenci + CRUD
│   │   ├── license-detail.html    # Detalji licence + aktivacije
│   │   └── audit.html             # Audit log pregled
│   └── partials/
│       ├── license-row.html
│       └── stats.html
├── crypto/
│   ├── private.pem                # RSA private key (NIKAD u git-u!)
│   └── public.pem                 # RSA public key (deli se sa klijentima)
├── migrations/
│   ├── 001_create_tables.sql
│   └── 002_seed_products.sql
├── .env.example
├── .gitignore
├── go.mod
├── README.md
├── TESTING.md
└── CLAUDE.md

Baza podataka (MySQL)

Tabele

products

Kolona Tip Opis
id BIGINT PK AUTO_INCREMENT
code VARCHAR(20) UNIQUE ESIR, ARV, LIGHT_TICKET
name VARCHAR(100) Puno ime proizvoda
key_prefix VARCHAR(10) ESIR-, ARV-, LT-
default_limits JSON Default limiti za taj proizvod
available_features JSON Sve moguće features za taj proizvod
active BOOLEAN DEFAULT TRUE
created_at TIMESTAMP

Seed podaci:

INSERT INTO products (code, name, key_prefix, default_limits, available_features) VALUES
('ESIR', 'ESIR Fiskalizacija', 'ESIR-',
  '{"max_installations": 1}',
  '["FISCALIZATION", "REPORTS"]'),
('ARV', 'ARV Evidencija RV', 'ARV-',
  '{"max_employees": 50, "max_readers": 4}',
  '["TIME_ATTENDANCE", "BASIC_REPORTS", "EMPLOYEE_MANAGEMENT", "SHIFTS", "HR_MODULE", "ACCESS_CONTROL"]'),
('LIGHT_TICKET', 'Light-Ticket', 'LT-',
  '{"max_operators": 3}',
  '["TICKET_VALIDATION", "REPORTS", "EXCEL_EXPORT", "LIVE_FEED"]');

Light-Ticket paketi licenci (odlučeno 03.03.2026):

  • Admin ručno upisuje max_operators prilikom kreiranja licence
  • Nema eksplicitnog edition polja — paketi su samo konvencija:
Paket max_operators Tip licence Features
Starter 3 MONTHLY ili PERPETUAL Sve
Pro 10 MONTHLY ili PERPETUAL Sve
Enterprise 0 (neograničeno) MONTHLY ili PERPETUAL Sve
  • Default u seed-u je max_operators: 3 (Starter)
  • Sve features su iste za sve pakete — razlikuje se SAMO max_operators
  • Tipovi licence za LT: MONTHLY (mesečna) i PERPETUAL (trajna). TRIAL i ANNUAL nisu predviđeni za V1.

licenses

Kolona Tip Opis
id BIGINT PK AUTO_INCREMENT
product_id BIGINT FK(products.id) Koji proizvod
license_key VARCHAR(25) UNIQUE {PREFIX}-XXXX-XXXX-XXXX-XXXX
license_type VARCHAR(20) NOT NULL TRIAL / MONTHLY / ANNUAL / PERPETUAL
customer_name VARCHAR(255) NOT NULL Naziv firme
customer_pib VARCHAR(20) PIB (opciono)
customer_email VARCHAR(255) Email
limits JSON NOT NULL {"max_employees": 50, "max_readers": 4}
features JSON NOT NULL ["TIME_ATTENDANCE", "BASIC_REPORTS"]
issued_at TIMESTAMP DEFAULT NOW()
expires_at TIMESTAMP NULL NULL = neograničena (PERPETUAL)
grace_days INT DEFAULT 30 Koliko dana grace period
active BOOLEAN DEFAULT TRUE
revoked BOOLEAN DEFAULT FALSE
revoked_at TIMESTAMP NULL
revoked_reason TEXT
notes TEXT Interne beleške
created_at TIMESTAMP
updated_at TIMESTAMP

Indeksi:

  • idx_licenses_key na (license_key) — UNIQUE
  • idx_licenses_product na (product_id)
  • idx_licenses_customer na (customer_name)
  • idx_licenses_expires na (expires_at)

activations

Kolona Tip Opis
id BIGINT PK AUTO_INCREMENT
license_id BIGINT FK(licenses.id)
machine_fingerprint VARCHAR(100) NOT NULL sha256:...
hostname VARCHAR(100) Ime računara
os_info VARCHAR(50) windows / linux
app_version VARCHAR(20) Verzija aplikacije
ip_address VARCHAR(45) IP pri aktivaciji
activated_at TIMESTAMP DEFAULT NOW()
deactivated_at TIMESTAMP NULL NULL = aktivna
is_active BOOLEAN DEFAULT TRUE
last_seen_at TIMESTAMP Poslednja online provera

Indeksi:

  • idx_activations_license na (license_id)
  • idx_activations_fingerprint na (machine_fingerprint)
  • idx_activations_active na (license_id, is_active)

audit_log

Kolona Tip Opis
id BIGINT PK AUTO_INCREMENT
license_id BIGINT FK(licenses.id) NULL
action VARCHAR(30) NOT NULL ACTIVATE, DEACTIVATE, VALIDATE, REVOKE, FORCE_RELEASE, CREATE, UPDATE
ip_address VARCHAR(45)
details JSON Dodatni podaci (fingerprint, hostname, error, itd.)
created_at TIMESTAMP DEFAULT NOW()

Indeksi:

  • idx_audit_license na (license_id)
  • idx_audit_action na (action)
  • idx_audit_created na (created_at)

API endpointi

Klijentski API (za ESIR, ARV, Light-Ticket aplikacije)

Metoda Putanja Auth Opis
POST /api/v1/activate - Aktivacija licence
POST /api/v1/deactivate - Deaktivacija (transfer)
POST /api/v1/validate - Opciona online provera
GET /api/v1/check-update - Provera za update aplikacije

POST /api/v1/activate

// Request (od klijentske aplikacije)
{
  "license_key": "LT-K7M2-9P4N-R3W8-J6T1",
  "machine_fingerprint": "sha256:a1b2c3d4e5f6...",
  "app_version": "1.0.0",
  "os": "windows",
  "hostname": "FIRMA-PC"
}

// Response 200
{
  "license": {
    "license_key": "LT-K7M2-9P4N-R3W8-J6T1",
    "product": "LIGHT_TICKET",
    "license_type": "MONTHLY",
    "issued_at": "2026-03-01T00:00:00Z",
    "expires_at": "2026-04-01T00:00:00Z",
    "activated_at": "2026-03-03T10:00:00Z",
    "machine_fingerprint": "sha256:a1b2c3d4e5f6...",
    "grace_days": 30,
    "limits": {
      "max_operators": 3
    },
    "features": ["TICKET_VALIDATION", "REPORTS", "EXCEL_EXPORT", "LIVE_FEED"],
    "customer": {
      "name": "Firma DOO",
      "email": "admin@firma.rs"
    }
  },
  "signature": "RSA-SHA256:base64encodedSignature..."
}

// Response 400
{
  "error": {
    "code": "ALREADY_ACTIVATED",
    "message": "Licenca je već aktivirana na drugom računaru",
    "details": {
      "activated_on": "DRUGA-PC",
      "activated_at": "2026-02-15T08:00:00Z"
    }
  }
}

Error kodovi: INVALID_KEY, ALREADY_ACTIVATED, KEY_EXPIRED, KEY_REVOKED, PRODUCT_MISMATCH

POST /api/v1/deactivate

// Request
{
  "license_key": "LT-K7M2-9P4N-R3W8-J6T1",
  "machine_fingerprint": "sha256:a1b2c3d4e5f6..."
}

// Response 200
{
  "message": "Licenca uspešno deaktivirana",
  "can_reactivate": true
}

POST /api/v1/validate

// Request
{
  "license_key": "LT-K7M2-9P4N-R3W8-J6T1",
  "machine_fingerprint": "sha256:a1b2c3d4e5f6..."
}

// Response 200
{
  "valid": true,
  "expires_at": "2026-04-01T00:00:00Z",
  "revoked": false
}

Admin API

Metoda Putanja Auth Opis
GET /api/v1/admin/products API key Lista proizvoda
GET /api/v1/admin/licenses API key Lista licenci (filter po proizvodu)
POST /api/v1/admin/licenses API key Kreiraj licencu
GET /api/v1/admin/licenses/{id} API key Detalji licence
PUT /api/v1/admin/licenses/{id} API key Izmeni licencu
POST /api/v1/admin/licenses/{id}/revoke API key Opozovi licencu
POST /api/v1/admin/licenses/{id}/release API key Force release (računar crkao)
GET /api/v1/admin/licenses/{id}/activations API key Aktivacije za licencu
GET /api/v1/admin/audit API key Audit log
GET /api/v1/admin/stats API key Statistike

Admin Dashboard (htmx)

Metoda Putanja Auth Opis
GET /login - Login
POST /login - Login submit
GET /dashboard Session Početna — statistike po proizvodu
GET /licenses Session Tabela licenci + filteri
GET /licenses/new Session Forma za novu licencu
POST /licenses Session Kreiraj licencu
GET /licenses/{id} Session Detalji + aktivacije
POST /licenses/{id}/revoke Session Opozovi
POST /licenses/{id}/release Session Force release
GET /audit Session Audit log

RSA ključevi

Generisanje (jednom, čuva se zauvek)

# Generiši private key (ČUVAJ TAJNO)
openssl genrsa -out crypto/private.pem 2048

# Izvuci public key (deli se sa klijentima)
openssl rsa -in crypto/private.pem -pubout -out crypto/public.pem

Korišćenje

  • License Server: koristi private.pem za potpisivanje licenci pri aktivaciji
  • Klijenti (ARV, ESIR, LT): imaju public.pem ugrađen u Go binary (embed) za verifikaciju potpisa
  • Isti par ključeva za sve proizvode — jedan server, jedan ključ

Šta se potpisuje

Server prima activate request
  → Kreira licencni JSON (bez signature polja)
  → Potpisuje JSON sa RSA-SHA256 (private key)
  → Dodaje potpis u response
  → Klijent prima JSON + potpis
  → Klijent verifikuje potpis (public key)
  → Klijent enkriptuje JSON sa AES-256-GCM (ključ = machine_fingerprint + app_secret)
  → Klijent sačuva kao license.enc

Licencni ključ — format

{PREFIX}-XXXX-XXXX-XXXX-XXXX

Primeri:
  ESIR-K7M2-9P4N-R3W8-J6T1
  ARV-A3B5-C8D2-E7F4-G9H6
  LT-M4N8-P2Q6-R5S3-T7U9
  • Prefix po proizvodu: čita se iz products.key_prefix
  • 4 grupe po 4 alfanumerička karaktera
  • Karakteri: A-H, J-N, P-Y, 2-9 (bez O/0/I/1 konfuzije)
  • Generisanje: crypto/rand

Tipovi licenci

Tip Trajanje Obnova Primer
TRIAL 30 dana Nema Besplatno testiranje
MONTHLY 30 dana Automatski ili ručno Light-Ticket
ANNUAL 365 dana Ručno ARV, ESIR
PERPETUAL Zauvek Nema (expires_at = NULL) Kupljena zauvek

Grace period

  • Default: 30 dana posle isteka
  • Konfigurisano per licenca (grace_days kolona)
  • Tokom grace perioda: pun rad + upozorenje na klijentskoj app
  • Posle grace-a: read-only režim (GET dozvoljen, POST/PUT/DELETE blokiran)

Dashboard — šta prikazuje

Početna strana

  • Po proizvodu: broj aktivnih / isteklih / u grace-u / trial
  • Ukupno: sve licence, aktivne aktivacije
  • Alarm: licence koje ističu u narednih 7 dana
  • Poslednja aktivnost: zadnjih 10 akcija iz audit loga

Lista licenci

  • Filter: proizvod, status (active/expired/revoked/trial), pretraga po firmi
  • Kolone: ključ, firma, proizvod, tip, ističe, aktivacija, status
  • Sortiranje: po datumu isteka (najhitnije prvo)

Detalji licence

  • Sve informacije o licenci
  • Lista aktivacija (hostname, OS, verzija, IP, poslednji put viđen)
  • Audit log za tu licencu
  • Akcije: produži, opozovi, force release

Migracija sa ESIR License Server-a

Pošto stari server niko ne koristi u produkciji, migracija je jednostavna:

  1. Kreiraj novi dal-license-server projekat
  2. Kopiraj korisnu logiku iz esir-license-server (keygen, verify flow, helpers)
  3. Proširi model (product, features, limits, RSA)
  4. Zameni port 8090 (isti port, drop-in replacement)
  5. Stari esir-license-server → arhiviraj

Implementacioni taskovi

Faza 0: Priprema

  • T0-01: Go projekat (go mod init, struktura foldera)
  • T0-02: .env.example, .gitignore, README.md
  • T0-03: Git repo na Gitea
  • T0-04: Generisanje RSA ključeva (private.pem, public.pem)

Faza 1: Baza

  • T1-01: Config modul (.env)
  • T1-02: MySQL konekcija
  • T1-03: Migracija 001_create_tables.sql (products, licenses, activations, audit_log)
  • T1-04: Migracija 002_seed_products.sql (ESIR, ARV, LIGHT_TICKET)
  • T1-05: Repository sloj (license_repo, activation_repo, audit_repo)

Faza 2: Core servis

  • T2-01: Keygen — generisanje ključa sa prefix-om po proizvodu
  • T2-02: Crypto service — RSA potpisivanje licencnog JSON-a
  • T2-03: License service — CRUD, validacija, revoke
  • T2-04: Activation service — activate, deactivate, force release, validate
  • T2-05: Audit logging — svaka akcija se loguje

Faza 3: Klijentski API

  • T3-01: POST /api/v1/activate
  • T3-02: POST /api/v1/deactivate
  • T3-03: POST /api/v1/validate
  • T3-04: Rate limiting na klijentske endpointe

Faza 4: Admin API

  • T4-01: Auth middleware (API key)
  • T4-02: CRUD endpointi za licence
  • T4-03: Revoke + Force release endpointi
  • T4-04: Aktivacije i audit endpointi
  • T4-05: Statistike endpoint

Faza 5: Admin Dashboard (htmx)

  • T5-01: Login stranica + session auth
  • T5-02: Dashboard — statistike po proizvodu
  • T5-03: Lista licenci — tabela, filteri, pretraga
  • T5-04: Nova licenca — forma sa izborom proizvoda
  • T5-05: Detalji licence — info, aktivacije, audit, akcije
  • T5-06: Audit log stranica

Faza 6: Testovi

  • T6-01: Unit — keygen (format, prefix, uniqueness)
  • T6-02: Unit — crypto (RSA sign/verify, tampered data)
  • T6-03: Unit — license service (create, expire, grace, revoke)
  • T6-04: Unit — activation service (activate, deactivate, already_activated, force_release)
  • T6-05: Integration — activate flow (full roundtrip)
  • T6-06: Integration — deactivate flow
  • T6-07: Integration — expired license
  • T6-08: Integration — revoked license
  • T6-09: Security — SQL injection na svim inputima
  • T6-10: Security — rate limiting (brute force key)
  • T6-11: Security — API key validation
  • T6-12: TESTING.md

Faza 7: DevOps

  • T7-01: Gitea CI workflow
  • T7-02: Systemd service fajl
  • T7-03: Backup skripta za bazu

Konfiguracija (.env)

# Server
APP_PORT=8090
APP_ENV=development

# MySQL
DB_HOST=localhost
DB_PORT=3306
DB_NAME=dal_license_db
DB_USER=license
DB_PASS=OBAVEZNO-PROMENITI

# Auth
ADMIN_API_KEY=OBAVEZNO-GENERISATI
SESSION_SECRET=OBAVEZNO-PROMENITI

# RSA
RSA_PRIVATE_KEY_PATH=./crypto/private.pem

# Rate limiting
RATE_LIMIT_ACTIVATE=10/min
RATE_LIMIT_VALIDATE=60/min

# Logging
LOG_LEVEL=info
LOG_FILE=./log/server.log

Konvencije

  • Go net/http (bez framework-a, isto kao ESIR)
  • Go 1.22+ method routing (POST /api/v1/activate)
  • database/sql + raw SQL (bez ORM-a)
  • RSA-2048 za potpis, SHA-256 za fingerprint
  • API key u X-API-Key header-u za admin
  • Sve akcije se loguju u audit_log
  • Error wrapping: fmt.Errorf("activate: %w", err)
  • Licencni ključ se NIKAD ne loguje ceo — samo prefix + poslednja 4 karaktera

Bezbednost

  • private.pem NIKAD u git-u (u .gitignore)
  • private.pem permisije: 600 (samo owner čita)
  • Admin API key min 32 karaktera
  • Rate limiting na activate/validate (zaštita od brute force)
  • Audit log za svaku akciju
  • HTTPS u produkciji (TLS termination na reverse proxy)
  • Licencni ključ u logovima maskiran: LT-K7M2-****-****-J6T1

Status implementacije (mart 2026)

Kompletno implementiran — server je funkcionalan, testiran (22/22 testova prošlo).

Implementirano

  • Go projekat inicijalizovan (go.mod, go.sum)
  • MySQL baza dal_license_db kreirana, user license
  • Migracije (001_create_tables.sql, 002_seed_products.sql)
  • RSA-2048 ključevi generisani (crypto/private.pem, crypto/public.pem)
  • Seed podaci: 3 proizvoda (ESIR, ARV, LIGHT_TICKET)
  • Client API: activate, deactivate, validate
  • Admin API: CRUD licence, stats, audit log
  • Dashboard: login, pregled licenci, kreiranje, detalji, audit log
  • Rate limiting (in-memory sliding window)
  • API key autentifikacija (X-API-Key header)
  • RSA-SHA256 potpisivanje licencnih podataka
  • Audit log za sve akcije
  • Specifikacija kompletna (ovaj CLAUDE.md)

Struktura fajlova

cmd/server/main.go              — Entry point, MySQL konekcija, migracije, wire-up
internal/config/config.go       — .env loading, DSN builder (multiStatements=true)
internal/model/
  license.go                    — Product, License, LicenseWithActivation modeli
  activation.go                 — Activation model
  audit.go                      — AuditEntry model
  request.go                    — Request/Response strukture (API)
internal/repository/
  license_repo.go               — License CRUD, product queries, stats
  activation_repo.go            — Activation CRUD, deactivate, force release
  audit_repo.go                 — Audit log insert/list
internal/service/
  license_service.go            — License CRUD biznis logika, expiry kalkulacija
  activation_service.go         — Activate, Deactivate, Validate, ForceRelease
  crypto_service.go             — RSA-2048 potpisivanje (SHA-256, PKCS1v15)
  keygen.go                     — Generisanje ključeva: {PREFIX}-XXXX-XXXX-XXXX-XXXX
internal/handler/
  client_handler.go             — POST /api/v1/activate, deactivate, validate
  admin_handler.go              — Admin CRUD API endpointi
  dashboard_handler.go          — Dashboard stranice, in-memory sesije
  helpers.go                    — writeJSON, writeError, clientIP
internal/middleware/
  auth.go                       — API key auth (X-API-Key header)
  ratelimit.go                  — In-memory sliding window rate limiter
internal/router/router.go       — Sve rute (client, admin, dashboard)
migrations/
  001_create_tables.sql          — products, licenses, activations, audit_log
  002_seed_products.sql          — ESIR, ARV, LIGHT_TICKET
templates/
  layout/base.html               — Glavni layout sa navbar-om
  pages/login.html               — Login stranica
  pages/dashboard.html           — Dashboard sa statistikama
  pages/licenses.html            — Lista licenci sa filterima
  pages/license-new.html         — Forma za novu licencu
  pages/license-detail.html      — Detalji licence, aktivacije, audit
  pages/audit.html               — Globalni audit log
static/css/style.css             — Kompletni CSS
static/js/htmx.min.js           — htmx biblioteka
crypto/private.pem               — RSA-2048 privatni ključ (chmod 600)
crypto/public.pem                — RSA javni ključ

Konfiguracija (.env)

APP_PORT=8090
DB_HOST=localhost / DB_PORT=3306
DB_NAME=dal_license_db
DB_USER=license / DB_PASS=license_pass_2026
ADMIN_API_KEY=dal-admin-key-2026-supersecret-change-me
ADMIN_PASSWORD=DalAdmin2026!
RSA_PRIVATE_KEY_PATH=./crypto/private.pem

DSN napomena

DSN sadrži multiStatements=true — neophodno za izvršavanje migracija sa više SQL naredbi u jednom db.Exec() pozivu.

Testirano (22/22)

  1. Health endpoint
  2. Products list
  3. Create license (sva 3 proizvoda)
  4. List licenses
  5. License detail
  6. Activate license
  7. Already activated (drugi hardver)
  8. Deactivate license
  9. Re-activate (novi hardver)
  10. Validate license
  11. Revoke license
  12. Validate revoked (odbijeno)
  13. Invalid license key
  14. API key auth (401 bez ključa)
  15. Stats endpoint
  16. Audit log
  17. Dashboard login
  18. Dashboard index
  19. Licenses page
  20. New license form
  21. License detail page
  22. Audit page

Sledeći koraci

  • Faza 8 (Light-Ticket): Integracija sa license serverom
  • ARV: Integracija sa license serverom
  • ESIR: Integracija sa license serverom

Kreirano: mart 2026 Ažurirano: 04.03.2026 — Kompletna implementacija, 22/22 testova Autor: Nenad Đukić / DAL d.o.o.