package server import ( "database/sql" "errors" "time" "gitea.perlbach24.de/scripte/wireguard-ui-multi/internal/database" ) // Server represents a single, independent WireGuard interface. type Server struct { ID int64 Name string InterfaceName string ListenPort int PrivateKey string PublicKey string AddressRange string DNS string MTU int Enabled bool CreatedAt time.Time UpdatedAt time.Time } // Peer represents a WireGuard client belonging to a Server. type Peer struct { ID int64 ServerID int64 Name string Email string PublicKey string PrivateKey string PresharedKey string AllowedIPs string Endpoint string PersistentKeepalive int Enabled bool ExpiresAt *time.Time CreatedAt time.Time UpdatedAt time.Time } var ErrNotFound = errors.New("not found") // Store provides CRUD access to servers and peers. type Store struct { db *database.DB } func NewStore(db *database.DB) *Store { return &Store{db: db} } func (s *Store) CreateServer(srv *Server) (int64, error) { res, err := s.db.Exec(`INSERT INTO servers (name, interface_name, listen_port, private_key, public_key, address_range, dns, mtu, enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, srv.Name, srv.InterfaceName, srv.ListenPort, srv.PrivateKey, srv.PublicKey, srv.AddressRange, srv.DNS, srv.MTU, boolToInt(srv.Enabled)) if err != nil { return 0, err } return res.LastInsertId() } func (s *Store) UpdateServer(srv *Server) error { _, err := s.db.Exec(`UPDATE servers SET name = ?, interface_name = ?, listen_port = ?, private_key = ?, public_key = ?, address_range = ?, dns = ?, mtu = ?, enabled = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, srv.Name, srv.InterfaceName, srv.ListenPort, srv.PrivateKey, srv.PublicKey, srv.AddressRange, srv.DNS, srv.MTU, boolToInt(srv.Enabled), srv.ID) return err } func (s *Store) DeleteServer(id int64) error { _, err := s.db.Exec(`DELETE FROM servers WHERE id = ?`, id) return err } func (s *Store) GetServer(id int64) (*Server, error) { row := s.db.QueryRow(`SELECT id, name, interface_name, listen_port, private_key, public_key, address_range, dns, mtu, enabled, created_at, updated_at FROM servers WHERE id = ?`, id) return scanServer(row) } func (s *Store) ListServers() ([]*Server, error) { rows, err := s.db.Query(`SELECT id, name, interface_name, listen_port, private_key, public_key, address_range, dns, mtu, enabled, created_at, updated_at FROM servers ORDER BY name`) if err != nil { return nil, err } defer rows.Close() var out []*Server for rows.Next() { srv, err := scanServerRows(rows) if err != nil { return nil, err } out = append(out, srv) } return out, rows.Err() } func (s *Store) CreatePeer(p *Peer) (int64, error) { res, err := s.db.Exec(`INSERT INTO peers (server_id, name, email, public_key, private_key, preshared_key, allowed_ips, endpoint, persistent_keepalive, enabled, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, p.ServerID, p.Name, p.Email, p.PublicKey, p.PrivateKey, p.PresharedKey, p.AllowedIPs, p.Endpoint, p.PersistentKeepalive, boolToInt(p.Enabled), p.ExpiresAt) if err != nil { return 0, err } return res.LastInsertId() } func (s *Store) UpdatePeer(p *Peer) error { _, err := s.db.Exec(`UPDATE peers SET name = ?, email = ?, public_key = ?, preshared_key = ?, allowed_ips = ?, endpoint = ?, persistent_keepalive = ?, enabled = ?, expires_at = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, p.Name, p.Email, p.PublicKey, p.PresharedKey, p.AllowedIPs, p.Endpoint, p.PersistentKeepalive, boolToInt(p.Enabled), p.ExpiresAt, p.ID) return err } func (s *Store) DeletePeer(id int64) error { _, err := s.db.Exec(`DELETE FROM peers WHERE id = ?`, id) return err } func (s *Store) GetPeer(id int64) (*Peer, error) { row := s.db.QueryRow(`SELECT id, server_id, name, email, public_key, private_key, preshared_key, allowed_ips, endpoint, persistent_keepalive, enabled, expires_at, created_at, updated_at FROM peers WHERE id = ?`, id) return scanPeer(row) } func (s *Store) ListPeersByServer(serverID int64) ([]*Peer, error) { rows, err := s.db.Query(`SELECT id, server_id, name, email, public_key, private_key, preshared_key, allowed_ips, endpoint, persistent_keepalive, enabled, expires_at, created_at, updated_at FROM peers WHERE server_id = ? ORDER BY name`, serverID) if err != nil { return nil, err } defer rows.Close() var out []*Peer for rows.Next() { p, err := scanPeerRows(rows) if err != nil { return nil, err } out = append(out, p) } return out, rows.Err() } type scanner interface { Scan(dest ...any) error } func scanServer(row scanner) (*Server, error) { var srv Server var enabled int if err := row.Scan(&srv.ID, &srv.Name, &srv.InterfaceName, &srv.ListenPort, &srv.PrivateKey, &srv.PublicKey, &srv.AddressRange, &srv.DNS, &srv.MTU, &enabled, &srv.CreatedAt, &srv.UpdatedAt); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, err } srv.Enabled = enabled != 0 return &srv, nil } func scanServerRows(rows *sql.Rows) (*Server, error) { return scanServer(rows) } func scanPeer(row scanner) (*Peer, error) { var p Peer var enabled int if err := row.Scan(&p.ID, &p.ServerID, &p.Name, &p.Email, &p.PublicKey, &p.PrivateKey, &p.PresharedKey, &p.AllowedIPs, &p.Endpoint, &p.PersistentKeepalive, &enabled, &p.ExpiresAt, &p.CreatedAt, &p.UpdatedAt); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, err } p.Enabled = enabled != 0 return &p, nil } func scanPeerRows(rows *sql.Rows) (*Peer, error) { return scanPeer(rows) } func boolToInt(b bool) int { if b { return 1 } return 0 }