package models import ( "database/sql" "fmt" "strings" "time" _ "modernc.org/sqlite" ) // License represents a license record in the database. type License struct { ID string Key string CustomerName string CustomerEmail string ProductName string Type string // expiry | usage ExpiresAt *time.Time MaxUses *int UsedCount int MachineID string Active bool CreatedAt time.Time SearchText string // pre-built lowercase search string for the UI } // Activation records each machine that activated a license. type Activation struct { ID string LicenseID string MachineID string Hostname string OS string ActivatedAt time.Time } // DB wraps the SQLite connection with helper methods. type DB struct { conn *sql.DB } // Open opens (or creates) the SQLite database at path. func Open(path string) (*DB, error) { conn, err := sql.Open("sqlite", path+"?_journal=WAL&_timeout=5000") if err != nil { return nil, err } db := &DB{conn: conn} return db, db.migrate() } func (db *DB) migrate() error { _, err := db.conn.Exec(` CREATE TABLE IF NOT EXISTS licenses ( id TEXT PRIMARY KEY, key TEXT UNIQUE NOT NULL, customer_name TEXT NOT NULL, customer_email TEXT NOT NULL, product_name TEXT NOT NULL, type TEXT NOT NULL, expires_at DATETIME, max_uses INTEGER, used_count INTEGER NOT NULL DEFAULT 0, machine_id TEXT, active INTEGER NOT NULL DEFAULT 1, created_at DATETIME NOT NULL ); CREATE TABLE IF NOT EXISTS activations ( id TEXT PRIMARY KEY, license_id TEXT NOT NULL REFERENCES licenses(id), machine_id TEXT NOT NULL, hostname TEXT, os TEXT, activated_at DATETIME NOT NULL ); CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, created_at DATETIME NOT NULL ); `) return err } // --- License CRUD --- func (db *DB) CreateLicense(l *License) error { _, err := db.conn.Exec(` INSERT INTO licenses (id, key, customer_name, customer_email, product_name, type, expires_at, max_uses, machine_id, active, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`, l.ID, l.Key, l.CustomerName, l.CustomerEmail, l.ProductName, l.Type, l.ExpiresAt, l.MaxUses, l.MachineID, l.Active, l.CreatedAt, ) return err } func (db *DB) ListLicenses() ([]License, error) { rows, err := db.conn.Query(` SELECT id, key, customer_name, customer_email, product_name, type, expires_at, max_uses, used_count, machine_id, active, created_at FROM licenses ORDER BY created_at DESC`) if err != nil { return nil, err } defer rows.Close() var licenses []License for rows.Next() { var l License if err := rows.Scan(&l.ID, &l.Key, &l.CustomerName, &l.CustomerEmail, &l.ProductName, &l.Type, &l.ExpiresAt, &l.MaxUses, &l.UsedCount, &l.MachineID, &l.Active, &l.CreatedAt); err != nil { return nil, err } l.SearchText = strings.ToLower(fmt.Sprintf("%s %s %s %s %s", l.Key, l.CustomerName, l.CustomerEmail, l.ProductName, l.MachineID)) licenses = append(licenses, l) } return licenses, nil } func (db *DB) GetLicenseByKey(key string) (*License, error) { var l License err := db.conn.QueryRow(` SELECT id, key, customer_name, customer_email, product_name, type, expires_at, max_uses, used_count, machine_id, active, created_at FROM licenses WHERE key = ?`, key). Scan(&l.ID, &l.Key, &l.CustomerName, &l.CustomerEmail, &l.ProductName, &l.Type, &l.ExpiresAt, &l.MaxUses, &l.UsedCount, &l.MachineID, &l.Active, &l.CreatedAt) if err == sql.ErrNoRows { return nil, nil } return &l, err } func (db *DB) RevokeLicense(id string) error { _, err := db.conn.Exec(`UPDATE licenses SET active = 0 WHERE id = ?`, id) return err } func (db *DB) ActivateLicense(id string) error { _, err := db.conn.Exec(`UPDATE licenses SET active = 1 WHERE id = ?`, id) return err } func (db *DB) IncrementUsage(id string) error { _, err := db.conn.Exec(`UPDATE licenses SET used_count = used_count + 1 WHERE id = ?`, id) return err } // --- Activations --- func (db *DB) RecordActivation(a *Activation) error { _, err := db.conn.Exec(` INSERT OR REPLACE INTO activations (id, license_id, machine_id, hostname, os, activated_at) VALUES (?,?,?,?,?,?)`, a.ID, a.LicenseID, a.MachineID, a.Hostname, a.OS, a.ActivatedAt, ) return err } func (db *DB) ListActivations(licenseID string) ([]Activation, error) { rows, err := db.conn.Query(` SELECT id, license_id, machine_id, hostname, os, activated_at FROM activations WHERE license_id = ? ORDER BY activated_at DESC`, licenseID) if err != nil { return nil, err } defer rows.Close() var acts []Activation for rows.Next() { var a Activation rows.Scan(&a.ID, &a.LicenseID, &a.MachineID, &a.Hostname, &a.OS, &a.ActivatedAt) acts = append(acts, a) } return acts, nil } // --- Users --- func (db *DB) CreateUser(id, email, hash string) error { _, err := db.conn.Exec(` INSERT INTO users (id, email, password_hash, created_at) VALUES (?,?,?,?)`, id, email, hash, time.Now()) return err } func (db *DB) GetUserByEmail(email string) (id, hash string, err error) { err = db.conn.QueryRow( `SELECT id, password_hash FROM users WHERE email = ?`, email). Scan(&id, &hash) return } func (db *DB) UserCount() int { var n int db.conn.QueryRow(`SELECT COUNT(*) FROM users`).Scan(&n) return n }