Vlasnik izlazi iz DAL price za ovaj projekat - postaje samostalan proizvod. Preimenovanje je mehanicko (import putanje, imena fajlova/baze/UI naslova), NIJEDNA linija poslovne logike u internal/handler ili internal/service nije dirana - go build, go vet i go test ./... prolaze cisto pod novim imenom. - go.mod: module dal-license-server -> licence-server; 28 import linija u 13 .go fajlova usaglaseno - internal/config/config.go (+test): default DB_NAME licence_db - HTML sabloni (7 stranica) + 7 Playwright e2e spec fajlova: "DAL License Server" -> "Licence Server" u title/nav-brand, testovi usaglaseni u istom prolazu (nista ne moze da se razdvoji) - package.json/package-lock.json/.gitignore/.claude/project.json: ime projekta/binarnog fajla/work_dir - Usput: package.json je u repository.url nosio OTVORENU Gitea lozinku u cistom tekstu - uklonjena (URL sad bez kredencijala). Lozinka je i dalje u staroj git istoriji - preporuka: rotirati je posebno. - CLAUDE.md/README/API/TESTING/docs/SPEC/ARCHITECTURE/SETUP: naslovi, ASCII dijagrami, git clone URL, mysqldump primer, systemd predlozak u SETUP.md zamenjen stvarnim (obrazac terminia.service - journal log + graceful shutdown, ne stari minimalni predlozak) - Debrendiranje: "Univerzalni licencni server za sve DAL proizvode" -> "za vise proizvoda i klijenata"; potpis "Nenad Djukic / DAL d.o.o." -> "Nenad Djukic". NE dirano: nazivi ESIR/ARV/LIGHT_TICKET (tudji proizvodi, ne DAL brend), "DAL" kao stvaran naziv org-a u RBAC modelu (docs/loggerservice/README.md) - to je podatak, ne branding ovog servera. - docs/asp-terminia/, docs/loggerservice/: prozni pomeni imena servera Baza (dal_license_db -> licence_db) i deploy na 151 idu u posebnom koraku, posle preimenovanja Gitea repoa - vidi CLAUDE.md istorijsku belesku na dnu. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
128 lines
3.1 KiB
Go
128 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"licence-server/internal/config"
|
|
"licence-server/internal/handler"
|
|
"licence-server/internal/model"
|
|
"licence-server/internal/repository"
|
|
"licence-server/internal/router"
|
|
"licence-server/internal/service"
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func main() {
|
|
cfg := config.Load()
|
|
|
|
// Connect to MySQL
|
|
db, err := sql.Open("mysql", cfg.DSN())
|
|
if err != nil {
|
|
log.Fatal("DB open: ", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if err := db.Ping(); err != nil {
|
|
log.Fatal("DB ping: ", err)
|
|
}
|
|
|
|
// Run migrations
|
|
runMigrations(db)
|
|
|
|
// Crypto service
|
|
cryptoSvc, err := service.NewCryptoService(cfg.RSAPrivateKey)
|
|
if err != nil {
|
|
log.Fatal("Crypto: ", err)
|
|
}
|
|
|
|
// Repositories
|
|
licenseRepo := repository.NewLicenseRepo(db)
|
|
activationRepo := repository.NewActivationRepo(db)
|
|
auditRepo := repository.NewAuditRepo(db)
|
|
userRepo := repository.NewUserRepo(db)
|
|
|
|
// Seed default admin user
|
|
seedDefaultAdmin(userRepo, cfg.AdminPassword)
|
|
|
|
// Services
|
|
licenseSvc := service.NewLicenseService(licenseRepo, auditRepo)
|
|
activationSvc := service.NewActivationService(activationRepo, licenseRepo, auditRepo, cryptoSvc, licenseSvc)
|
|
|
|
// Handlers
|
|
clientHandler := handler.NewClientHandler(activationSvc)
|
|
adminHandler := handler.NewAdminHandler(licenseSvc, activationSvc, auditRepo)
|
|
dashboardHandler := handler.NewDashboardHandler(licenseSvc, activationSvc, auditRepo, userRepo, "templates")
|
|
|
|
// Rate limits
|
|
rlActivate, _ := strconv.Atoi(cfg.RateLimitActivate)
|
|
if rlActivate == 0 {
|
|
rlActivate = 10
|
|
}
|
|
rlValidate, _ := strconv.Atoi(cfg.RateLimitValidate)
|
|
if rlValidate == 0 {
|
|
rlValidate = 60
|
|
}
|
|
|
|
// Router
|
|
mux := router.Setup(clientHandler, adminHandler, dashboardHandler, cfg.AdminAPIKey, rlActivate, rlValidate)
|
|
|
|
addr := ":" + cfg.Port
|
|
fmt.Printf("DAL License Server pokrenut na http://localhost%s\n", addr)
|
|
if err := http.ListenAndServe(addr, mux); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func runMigrations(db *sql.DB) {
|
|
files := []string{
|
|
"migrations/001_create_tables.sql",
|
|
"migrations/002_seed_products.sql",
|
|
"migrations/003_create_admin_users.sql",
|
|
}
|
|
for _, f := range files {
|
|
data, err := os.ReadFile(f)
|
|
if err != nil {
|
|
log.Printf("Migration %s: %v", f, err)
|
|
continue
|
|
}
|
|
_, err = db.Exec(string(data))
|
|
if err != nil {
|
|
log.Printf("Migration %s: %v (may already exist)", f, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func seedDefaultAdmin(userRepo *repository.UserRepo, adminPassword string) {
|
|
count, err := userRepo.Count()
|
|
if err != nil {
|
|
log.Printf("Seed admin: count error: %v", err)
|
|
return
|
|
}
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
log.Fatal("Seed admin: bcrypt error: ", err)
|
|
}
|
|
|
|
user := &model.AdminUser{
|
|
Username: "admin",
|
|
PasswordHash: string(hash),
|
|
FullName: "Administrator",
|
|
Active: true,
|
|
}
|
|
if err := userRepo.Create(user); err != nil {
|
|
log.Printf("Seed admin: %v (may already exist)", err)
|
|
} else {
|
|
log.Println("Default admin korisnik kreiran (username: admin)")
|
|
}
|
|
}
|