tailly_back_v2/internal/repository/recovery_repository.go
madipo2611 6f5298d420 v0.0.3
2025-05-03 02:37:08 +03:00

228 lines
4.2 KiB
Go

package repository
import (
"context"
"database/sql"
"errors"
"tailly_back_v2/internal/domain"
)
var (
ErrRecoveryRequestNotFound = errors.New("recovery request not found")
ErrRecoveryMethodNotFound = errors.New("recovery method not found")
)
type RecoveryRepository interface {
SaveRequest(ctx context.Context, req *domain.RecoveryRequest) error
GetRequestByToken(ctx context.Context, token string) (*domain.RecoveryRequest, error)
UpdateRequest(ctx context.Context, req *domain.RecoveryRequest) error
GetMethods(ctx context.Context, userID int) ([]*domain.RecoveryMethod, error)
SaveMethod(ctx context.Context, method *domain.RecoveryMethod) error
}
type recoveryRepository struct {
db *sql.DB
}
func NewRecoveryRepository(db *sql.DB) RecoveryRepository {
return &recoveryRepository{db: db}
}
func (r *recoveryRepository) SaveRequest(ctx context.Context, req *domain.RecoveryRequest) error {
query := `
INSERT INTO recovery_requests (
user_id,
token,
new_device_id,
status,
created_at,
expires_at
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id
`
var deviceID interface{}
if req.NewDevice != nil {
deviceID = req.NewDevice.ID
} else {
deviceID = nil
}
err := r.db.QueryRowContext(ctx, query,
req.UserID,
req.Token,
deviceID,
req.Status,
req.CreatedAt,
req.ExpiresAt,
).Scan(&req.ID)
return err
}
func (r *recoveryRepository) GetRequestByToken(ctx context.Context, token string) (*domain.RecoveryRequest, error) {
query := `
SELECT
r.id,
r.user_id,
r.token,
r.status,
r.created_at,
r.expires_at,
d.id,
d.user_id,
d.name,
d.ip_address,
d.user_agent,
d.created_at
FROM recovery_requests r
LEFT JOIN devices d ON r.new_device_id = d.id
WHERE r.token = $1
`
req := &domain.RecoveryRequest{}
var device domain.Device
var deviceID sql.NullInt64
err := r.db.QueryRowContext(ctx, query, token).Scan(
&req.ID,
&req.UserID,
&req.Token,
&req.Status,
&req.CreatedAt,
&req.ExpiresAt,
&deviceID,
&device.UserID,
&device.Name,
&device.IPAddress,
&device.UserAgent,
&device.CreatedAt,
)
if err == sql.ErrNoRows {
return nil, ErrRecoveryRequestNotFound
}
if err != nil {
return nil, err
}
if deviceID.Valid {
device.ID = int(deviceID.Int64)
req.NewDevice = &device
}
return req, nil
}
func (r *recoveryRepository) UpdateRequest(ctx context.Context, req *domain.RecoveryRequest) error {
query := `
UPDATE recovery_requests
SET
status = $1,
new_device_id = $2
WHERE id = $3
`
var deviceID interface{}
if req.NewDevice != nil {
deviceID = req.NewDevice.ID
} else {
deviceID = nil
}
_, err := r.db.ExecContext(ctx, query,
req.Status,
deviceID,
req.ID,
)
return err
}
func (r *recoveryRepository) GetMethods(ctx context.Context, userID int) ([]*domain.RecoveryMethod, error) {
query := `
SELECT
id,
user_id,
method_type,
value,
is_primary,
verified_at,
created_at
FROM recovery_methods
WHERE user_id = $1
ORDER BY is_primary DESC, created_at ASC
`
rows, err := r.db.QueryContext(ctx, query, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var methods []*domain.RecoveryMethod
for rows.Next() {
var method domain.RecoveryMethod
var verifiedAt sql.NullTime
err := rows.Scan(
&method.ID,
&method.UserID,
&method.MethodType,
&method.Value,
&method.IsPrimary,
&verifiedAt,
&method.CreatedAt,
)
if err != nil {
return nil, err
}
if verifiedAt.Valid {
method.VerifiedAt = verifiedAt.Time
}
methods = append(methods, &method)
}
if len(methods) == 0 {
return nil, ErrRecoveryMethodNotFound
}
return methods, nil
}
func (r *recoveryRepository) SaveMethod(ctx context.Context, method *domain.RecoveryMethod) error {
query := `
INSERT INTO recovery_methods (
user_id,
method_type,
value,
is_primary,
verified_at,
created_at
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id
`
var verifiedAt interface{}
if !method.VerifiedAt.IsZero() {
verifiedAt = method.VerifiedAt
} else {
verifiedAt = nil
}
err := r.db.QueryRowContext(ctx, query,
method.UserID,
method.MethodType,
method.Value,
method.IsPrimary,
verifiedAt,
method.CreatedAt,
).Scan(&method.ID)
return err
}