fncConvertGui/internal/repository/customer_repository.go
2026-02-25 23:17:08 +01:00

150 lines
3.9 KiB
Go

package repository
import (
"database/sql"
"fmt"
"time"
"fncConvertGui/internal/crypto"
"fncConvertGui/internal/models"
)
// CustomerRepository handles customer CRUD with field-level encryption.
type CustomerRepository struct {
db *sql.DB
enc *crypto.Encryptor
}
// NewCustomerRepository creates a new repository.
func NewCustomerRepository(db *sql.DB, enc *crypto.Encryptor) *CustomerRepository {
return &CustomerRepository{db: db, enc: enc}
}
// Create inserts a new customer, encrypting sensitive fields.
func (r *CustomerRepository) Create(c *models.Customer) (int64, error) {
encName, err := r.enc.Encrypt([]byte(c.Name))
if err != nil {
return 0, fmt.Errorf("encrypt name: %w", err)
}
encEmail, err := r.enc.Encrypt([]byte(c.Email))
if err != nil {
return 0, fmt.Errorf("encrypt email: %w", err)
}
encPhone, err := r.enc.Encrypt([]byte(c.Phone))
if err != nil {
return 0, fmt.Errorf("encrypt phone: %w", err)
}
now := time.Now()
res, err := r.db.Exec(
`INSERT INTO customers (name, email, phone, company, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)`,
encName, encEmail, encPhone, c.Company, now, now,
)
if err != nil {
return 0, fmt.Errorf("insert: %w", err)
}
return res.LastInsertId()
}
// GetByID fetches a customer by ID, decrypting sensitive fields.
func (r *CustomerRepository) GetByID(id int64) (*models.Customer, error) {
var c models.Customer
var encName, encEmail, encPhone []byte
err := r.db.QueryRow(
`SELECT id, name, email, phone, company, created_at, updated_at
FROM customers WHERE id = ?`, id,
).Scan(&c.ID, &encName, &encEmail, &encPhone, &c.Company, &c.CreatedAt, &c.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("query: %w", err)
}
name, err := r.enc.Decrypt(encName)
if err != nil {
return nil, fmt.Errorf("decrypt name: %w", err)
}
email, err := r.enc.Decrypt(encEmail)
if err != nil {
return nil, fmt.Errorf("decrypt email: %w", err)
}
phone, err := r.enc.Decrypt(encPhone)
if err != nil {
return nil, fmt.Errorf("decrypt phone: %w", err)
}
c.Name = string(name)
c.Email = string(email)
c.Phone = string(phone)
return &c, nil
}
// List returns all customers with decrypted fields for list views.
func (r *CustomerRepository) List() ([]models.CustomerListItem, error) {
rows, err := r.db.Query(
`SELECT id, name, email, company FROM customers ORDER BY id DESC`)
if err != nil {
return nil, fmt.Errorf("query: %w", err)
}
defer rows.Close()
var items []models.CustomerListItem
for rows.Next() {
var item models.CustomerListItem
var encName, encEmail []byte
if err := rows.Scan(&item.ID, &encName, &encEmail, &item.Company); err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
name, err := r.enc.Decrypt(encName)
if err != nil {
return nil, fmt.Errorf("decrypt name: %w", err)
}
email, err := r.enc.Decrypt(encEmail)
if err != nil {
return nil, fmt.Errorf("decrypt email: %w", err)
}
item.Name = string(name)
item.Email = string(email)
items = append(items, item)
}
return items, rows.Err()
}
// Update modifies an existing customer, re-encrypting sensitive fields.
func (r *CustomerRepository) Update(c *models.Customer) error {
encName, err := r.enc.Encrypt([]byte(c.Name))
if err != nil {
return fmt.Errorf("encrypt name: %w", err)
}
encEmail, err := r.enc.Encrypt([]byte(c.Email))
if err != nil {
return fmt.Errorf("encrypt email: %w", err)
}
encPhone, err := r.enc.Encrypt([]byte(c.Phone))
if err != nil {
return fmt.Errorf("encrypt phone: %w", err)
}
_, err = r.db.Exec(
`UPDATE customers SET name=?, email=?, phone=?, company=?, updated_at=?
WHERE id=?`,
encName, encEmail, encPhone, c.Company, time.Now(), c.ID,
)
if err != nil {
return fmt.Errorf("update: %w", err)
}
return nil
}
// Delete removes a customer by ID.
func (r *CustomerRepository) Delete(id int64) error {
_, err := r.db.Exec(`DELETE FROM customers WHERE id = ?`, id)
if err != nil {
return fmt.Errorf("delete: %w", err)
}
return nil
}