Password complexity: custom validator requiring uppercase, lowercase, digit (min 8 chars)
Token expiry: 90-day token lifetime with refresh endpoint (60-90 day renewal window)
Health check: /api/health/ now pings Postgres + Redis, returns 503 on failure
Audit logging: async audit_log table for auth events (login, register, delete, etc.)
Circuit breaker: APNs/FCM push sends wrapped with 5-failure threshold, 30s recovery
FK indexes: 27 missing foreign key indexes across all tables (migration 017)
CSP header: default-src 'none'; frame-ancestors 'none'
Gzip compression: level 5 with media endpoint skipper
Prometheus metrics: /metrics endpoint using existing monitoring service
External timeouts: 15s push, 30s SMTP, context timeouts on all external calls
Migrations: 016 (token created_at), 017 (FK indexes), 018 (audit_log)
Tests: circuit breaker (15), audit service (8), token refresh (7), health (4),
middleware expiry (5), validator (new)
198 lines
5.9 KiB
Go
198 lines
5.9 KiB
Go
package responses
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/treytartt/honeydue-api/internal/models"
|
|
)
|
|
|
|
// UserResponse represents a user in API responses
|
|
type UserResponse struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
IsActive bool `json:"is_active"`
|
|
Verified bool `json:"verified"`
|
|
DateJoined time.Time `json:"date_joined"`
|
|
LastLogin *time.Time `json:"last_login,omitempty"`
|
|
}
|
|
|
|
// UserProfileResponse represents a user profile in API responses
|
|
type UserProfileResponse struct {
|
|
ID uint `json:"id"`
|
|
UserID uint `json:"user_id"`
|
|
Verified bool `json:"verified"`
|
|
Bio string `json:"bio"`
|
|
PhoneNumber string `json:"phone_number"`
|
|
DateOfBirth *time.Time `json:"date_of_birth,omitempty"`
|
|
ProfilePicture string `json:"profile_picture"`
|
|
}
|
|
|
|
// LoginResponse represents the login response
|
|
type LoginResponse struct {
|
|
Token string `json:"token"`
|
|
User UserResponse `json:"user"`
|
|
}
|
|
|
|
// RegisterResponse represents the registration response
|
|
type RegisterResponse struct {
|
|
Token string `json:"token"`
|
|
User UserResponse `json:"user"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// CurrentUserResponse represents the /auth/me/ response
|
|
type CurrentUserResponse struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
IsActive bool `json:"is_active"`
|
|
DateJoined time.Time `json:"date_joined"`
|
|
LastLogin *time.Time `json:"last_login,omitempty"`
|
|
Profile *UserProfileResponse `json:"profile,omitempty"`
|
|
AuthProvider string `json:"auth_provider"`
|
|
}
|
|
|
|
// VerifyEmailResponse represents the email verification response
|
|
type VerifyEmailResponse struct {
|
|
Message string `json:"message"`
|
|
Verified bool `json:"verified"`
|
|
}
|
|
|
|
// ForgotPasswordResponse represents the forgot password response
|
|
type ForgotPasswordResponse struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// VerifyResetCodeResponse represents the verify reset code response
|
|
type VerifyResetCodeResponse struct {
|
|
Message string `json:"message"`
|
|
ResetToken string `json:"reset_token"`
|
|
}
|
|
|
|
// ResetPasswordResponse represents the reset password response
|
|
type ResetPasswordResponse struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// RefreshTokenResponse represents the token refresh response
|
|
type RefreshTokenResponse struct {
|
|
Token string `json:"token"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// MessageResponse represents a simple message response
|
|
type MessageResponse struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// ErrorResponse represents an error response
|
|
type ErrorResponse struct {
|
|
Error string `json:"error"`
|
|
Details map[string]string `json:"details,omitempty"`
|
|
}
|
|
|
|
// NewUserResponse creates a UserResponse from a User model
|
|
func NewUserResponse(user *models.User) UserResponse {
|
|
verified := false
|
|
if user.Profile != nil {
|
|
verified = user.Profile.Verified
|
|
}
|
|
return UserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
FirstName: user.FirstName,
|
|
LastName: user.LastName,
|
|
IsActive: user.IsActive,
|
|
Verified: verified,
|
|
DateJoined: user.DateJoined,
|
|
LastLogin: user.LastLogin,
|
|
}
|
|
}
|
|
|
|
// NewUserProfileResponse creates a UserProfileResponse from a UserProfile model
|
|
func NewUserProfileResponse(profile *models.UserProfile) *UserProfileResponse {
|
|
if profile == nil {
|
|
return nil
|
|
}
|
|
return &UserProfileResponse{
|
|
ID: profile.ID,
|
|
UserID: profile.UserID,
|
|
Verified: profile.Verified,
|
|
Bio: profile.Bio,
|
|
PhoneNumber: profile.PhoneNumber,
|
|
DateOfBirth: profile.DateOfBirth,
|
|
ProfilePicture: profile.ProfilePicture,
|
|
}
|
|
}
|
|
|
|
// NewCurrentUserResponse creates a CurrentUserResponse from a User model
|
|
func NewCurrentUserResponse(user *models.User, authProvider string) CurrentUserResponse {
|
|
return CurrentUserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
FirstName: user.FirstName,
|
|
LastName: user.LastName,
|
|
IsActive: user.IsActive,
|
|
DateJoined: user.DateJoined,
|
|
LastLogin: user.LastLogin,
|
|
Profile: NewUserProfileResponse(user.Profile),
|
|
AuthProvider: authProvider,
|
|
}
|
|
}
|
|
|
|
// NewLoginResponse creates a LoginResponse
|
|
func NewLoginResponse(token string, user *models.User) LoginResponse {
|
|
return LoginResponse{
|
|
Token: token,
|
|
User: NewUserResponse(user),
|
|
}
|
|
}
|
|
|
|
// NewRegisterResponse creates a RegisterResponse
|
|
func NewRegisterResponse(token string, user *models.User) RegisterResponse {
|
|
return RegisterResponse{
|
|
Token: token,
|
|
User: NewUserResponse(user),
|
|
Message: "Registration successful. Please check your email to verify your account.",
|
|
}
|
|
}
|
|
|
|
// AppleSignInResponse represents the Apple Sign In response
|
|
type AppleSignInResponse struct {
|
|
Token string `json:"token"`
|
|
User UserResponse `json:"user"`
|
|
IsNewUser bool `json:"is_new_user"`
|
|
}
|
|
|
|
// NewAppleSignInResponse creates an AppleSignInResponse
|
|
func NewAppleSignInResponse(token string, user *models.User, isNewUser bool) AppleSignInResponse {
|
|
return AppleSignInResponse{
|
|
Token: token,
|
|
User: NewUserResponse(user),
|
|
IsNewUser: isNewUser,
|
|
}
|
|
}
|
|
|
|
// GoogleSignInResponse represents the Google Sign In response
|
|
type GoogleSignInResponse struct {
|
|
Token string `json:"token"`
|
|
User UserResponse `json:"user"`
|
|
IsNewUser bool `json:"is_new_user"`
|
|
}
|
|
|
|
// NewGoogleSignInResponse creates a GoogleSignInResponse
|
|
func NewGoogleSignInResponse(token string, user *models.User, isNewUser bool) GoogleSignInResponse {
|
|
return GoogleSignInResponse{
|
|
Token: token,
|
|
User: NewUserResponse(user),
|
|
IsNewUser: isNewUser,
|
|
}
|
|
}
|