package repository import ( "context" "database/sql" "errors" "tailly_back_v2/internal/domain" ) var ( ErrDeviceNotFound = errors.New("device not found") ErrInvalidToken = errors.New("invalid confirmation token") ) type DeviceRepository interface { Save(ctx context.Context, device *domain.Device) error GetByID(ctx context.Context, id int) (*domain.Device, error) GetByToken(ctx context.Context, token string) (*domain.Device, error) GetByUser(ctx context.Context, userID int) ([]*domain.Device, error) Update(ctx context.Context, device *domain.Device) error Delete(ctx context.Context, id int) error } type deviceRepository struct { db *sql.DB } func NewDeviceRepository(db *sql.DB) DeviceRepository { return &deviceRepository{db: db} } func (r *deviceRepository) Save(ctx context.Context, device *domain.Device) error { query := ` INSERT INTO devices ( user_id, name, ip_address, user_agent, confirmation_token, expires_at, created_at ) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id ` err := r.db.QueryRowContext(ctx, query, device.UserID, device.Name, device.IPAddress, device.UserAgent, device.ConfirmationToken, device.ExpiresAt, device.CreatedAt, ).Scan(&device.ID) return err } func (r *deviceRepository) GetByID(ctx context.Context, id int) (*domain.Device, error) { query := ` SELECT id, user_id, name, ip_address, user_agent, confirmation_token, expires_at, created_at FROM devices WHERE id = $1 ` device := &domain.Device{} err := r.db.QueryRowContext(ctx, query, id).Scan( &device.ID, &device.UserID, &device.Name, &device.IPAddress, &device.UserAgent, &device.ConfirmationToken, &device.ExpiresAt, &device.CreatedAt, ) if err == sql.ErrNoRows { return nil, ErrDeviceNotFound } return device, err } func (r *deviceRepository) GetByToken(ctx context.Context, token string) (*domain.Device, error) { query := ` SELECT id, user_id, name, ip_address, user_agent, confirmation_token, expires_at, created_at FROM devices WHERE confirmation_token = $1 ` device := &domain.Device{} err := r.db.QueryRowContext(ctx, query, token).Scan( &device.ID, &device.UserID, &device.Name, &device.IPAddress, &device.UserAgent, &device.ConfirmationToken, &device.ExpiresAt, &device.CreatedAt, ) if err == sql.ErrNoRows { return nil, ErrInvalidToken } return device, err } func (r *deviceRepository) GetByUser(ctx context.Context, userID int) ([]*domain.Device, error) { query := ` SELECT id, user_id, name, ip_address, user_agent, confirmation_token, expires_at, created_at FROM devices WHERE user_id = $1 ORDER BY created_at DESC ` rows, err := r.db.QueryContext(ctx, query, userID) if err != nil { return nil, err } defer rows.Close() var devices []*domain.Device for rows.Next() { var device domain.Device err := rows.Scan( &device.ID, &device.UserID, &device.Name, &device.IPAddress, &device.UserAgent, &device.ConfirmationToken, &device.ExpiresAt, &device.CreatedAt, ) if err != nil { return nil, err } devices = append(devices, &device) } return devices, nil } func (r *deviceRepository) Update(ctx context.Context, device *domain.Device) error { query := ` UPDATE devices SET name = $1, ip_address = $2, user_agent = $3, confirmation_token = $4, expires_at = $5 WHERE id = $6 ` _, err := r.db.ExecContext(ctx, query, device.Name, device.IPAddress, device.UserAgent, device.ConfirmationToken, device.ExpiresAt, device.ID, ) return err } func (r *deviceRepository) Delete(ctx context.Context, id int) error { query := `DELETE FROM devices WHERE id = $1` _, err := r.db.ExecContext(ctx, query, id) return err }