// Path: Backend/internal/handlers/api/shared/shared.go package shared import ( "boostai-backend/internal/sqlc" "context" "errors" "fmt" "strings" "time" "github.com/jackc/pgx/v5/pgtype" "golang.org/x/crypto/bcrypt" ) const QueryTimeout = 5 * time.Second type ListResponse[T any] struct { Data []T `json:"data"` } func WithTimeout() (context.Context, context.CancelFunc) { return context.WithTimeout(context.Background(), QueryTimeout) } func IsValidAnswerStatus(status string) bool { switch sqlc.AnswerStatus(strings.TrimSpace(status)) { case sqlc.AnswerStatusNotStarted, sqlc.AnswerStatusInProgress, sqlc.AnswerStatusSubmitted, sqlc.AnswerStatusReviewed: return true default: return false } } func NullableText(value *string) pgtype.Text { if value == nil { return pgtype.Text{} } trimmed := strings.TrimSpace(*value) if trimmed == "" { return pgtype.Text{} } return pgtype.Text{String: trimmed, Valid: true} } func MaybeHashPassword(value *string) (pgtype.Text, error) { if value == nil { return pgtype.Text{}, nil } trimmed := strings.TrimSpace(*value) if trimmed == "" { return pgtype.Text{}, nil } if len(trimmed) < 8 { return pgtype.Text{}, errors.New("password must be at least 8 characters") } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(trimmed), bcrypt.DefaultCost) if err != nil { return pgtype.Text{}, err } return pgtype.Text{String: string(hashedPassword), Valid: true}, nil } func NullableTime(value *time.Time) pgtype.Timestamptz { if value == nil { return pgtype.Timestamptz{} } return pgtype.Timestamptz{Time: value.UTC(), Valid: true} } func NullableBool(value *bool) pgtype.Bool { if value == nil { return pgtype.Bool{} } return pgtype.Bool{Bool: *value, Valid: true} } func TextPointer(value pgtype.Text) *string { if !value.Valid { return nil } text := value.String return &text } func TextValue(value pgtype.Text) string { if !value.Valid { return "" } return value.String } func TimePointer(value pgtype.Timestamptz) *time.Time { if !value.Valid { return nil } timestamp := value.Time.UTC() return ×tamp } func Int64Pointer(value pgtype.Int8) *int64 { if !value.Valid { return nil } v := value.Int64 return &v } func BoolPointer(value pgtype.Bool) *bool { if !value.Valid { return nil } v := value.Bool return &v } func NullableFloat64AsNumeric(value *float64) (pgtype.Numeric, error) { if value == nil { return pgtype.Numeric{}, nil } numeric := pgtype.Numeric{} if err := numeric.ScanScientific(fmt.Sprintf("%f", *value)); err != nil { return pgtype.Numeric{}, err } return numeric, nil } func NumericPointer(value pgtype.Numeric) *float64 { if !value.Valid { return nil } floatValue, err := value.Float64Value() if err != nil || !floatValue.Valid { return nil } v := floatValue.Float64 return &v }