Files
BoostAI/Backend/internal/handlers/api/shared/shared.go
2026-05-25 17:05:06 +01:00

160 lines
2.8 KiB
Go

// 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 &timestamp
}
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
}