feat: implement core Go application with web server
- Add Go modules with required dependencies (Gin, UUID, JWT, etc.) - Implement main web server with landing page endpoint - Add comprehensive API endpoints for health and status - Include proper error handling and request validation - Set up CORS middleware and security headers
This commit is contained in:
269
output/internal/services/user_service.go
Normal file
269
output/internal/services/user_service.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/ydn/yourdreamnamehere/internal/config"
|
||||
"github.com/ydn/yourdreamnamehere/internal/models"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
db *gorm.DB
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
func NewUserService(db *gorm.DB, config *config.Config) *UserService {
|
||||
return &UserService{
|
||||
db: db,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserService) CreateUser(email, firstName, lastName, password string) (*models.User, error) {
|
||||
// Check if user already exists
|
||||
var existingUser models.User
|
||||
if err := s.db.Where("email = ?", email).First(&existingUser).Error; err == nil {
|
||||
return nil, fmt.Errorf("user with email %s already exists", email)
|
||||
}
|
||||
|
||||
// Hash password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
// Create user and customer in a transaction
|
||||
err = s.db.Transaction(func(tx *gorm.DB) error {
|
||||
user := &models.User{
|
||||
ID: uuid.New(),
|
||||
Email: email,
|
||||
FirstName: firstName,
|
||||
LastName: lastName,
|
||||
PasswordHash: string(hashedPassword),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := tx.Create(user).Error; err != nil {
|
||||
return fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
||||
// Create associated customer record for future Stripe integration
|
||||
customer := &models.Customer{
|
||||
UserID: user.ID,
|
||||
Email: email,
|
||||
Status: "pending", // Will be updated when Stripe customer is created
|
||||
}
|
||||
|
||||
if err := tx.Create(customer).Error; err != nil {
|
||||
return fmt.Errorf("failed to create customer: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return the created user
|
||||
var user models.User
|
||||
if err := s.db.Where("email = ?", email).First(&user).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve created user: %w", err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) AuthenticateUser(email, password string) (string, error) {
|
||||
var user models.User
|
||||
if err := s.db.Where("email = ?", email).First(&user).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return "", fmt.Errorf("invalid credentials")
|
||||
}
|
||||
return "", fmt.Errorf("failed to authenticate user: %w", err)
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil {
|
||||
return "", fmt.Errorf("invalid credentials")
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"user_id": user.ID.String(),
|
||||
"email": user.Email,
|
||||
"role": user.Role,
|
||||
"exp": time.Now().Add(s.config.JWT.Expiry).Unix(),
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString([]byte(s.config.JWT.Secret))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate token: %w", err)
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserByID(userID string) (*models.User, error) {
|
||||
var user models.User
|
||||
if err := s.db.Where("id = ?", userID).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) UpdateUser(userID, firstName, lastName string) (*models.User, error) {
|
||||
var user models.User
|
||||
if err := s.db.Where("id = ?", userID).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if firstName != "" {
|
||||
user.FirstName = firstName
|
||||
}
|
||||
if lastName != "" {
|
||||
user.LastName = lastName
|
||||
}
|
||||
user.UpdatedAt = time.Now()
|
||||
|
||||
if err := s.db.Save(&user).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to update user: %w", err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserDomains(userID string) ([]models.Domain, error) {
|
||||
var domains []models.Domain
|
||||
if err := s.db.Joins("JOIN customers ON domains.customer_id = customers.id").
|
||||
Where("customers.user_id = ?", userID).
|
||||
Find(&domains).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetDomainByID(userID string, domainID uuid.UUID) (*models.Domain, error) {
|
||||
var domain models.Domain
|
||||
if err := s.db.Joins("JOIN customers ON domains.customer_id = customers.id").
|
||||
Where("customers.user_id = ? AND domains.id = ?", userID, domainID).
|
||||
First(&domain).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &domain, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserVPS(userID string) ([]models.VPS, error) {
|
||||
var vpsList []models.VPS
|
||||
if err := s.db.Joins("JOIN domains ON vps.domain_id = domains.id").
|
||||
Joins("JOIN customers ON domains.customer_id = customers.id").
|
||||
Where("customers.user_id = ?", userID).
|
||||
Find(&vpsList).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vpsList, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetVPSByID(userID string, vpsID uuid.UUID) (*models.VPS, error) {
|
||||
var vps models.VPS
|
||||
if err := s.db.Joins("JOIN domains ON vps.domain_id = domains.id").
|
||||
Joins("JOIN customers ON domains.customer_id = customers.id").
|
||||
Where("customers.user_id = ? AND vps.id = ?", userID, vpsID).
|
||||
First(&vps).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vps, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserSubscriptions(userID string) ([]models.Subscription, error) {
|
||||
var subscriptions []models.Subscription
|
||||
if err := s.db.Joins("JOIN customers ON subscriptions.customer_id = customers.id").
|
||||
Where("customers.user_id = ?", userID).
|
||||
Find(&subscriptions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return subscriptions, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetDeploymentLogs(userID string, vpsID *uuid.UUID) ([]models.DeploymentLog, error) {
|
||||
var logs []models.DeploymentLog
|
||||
query := s.db.Joins("JOIN vps ON deployment_logs.vps_id = vps.id").
|
||||
Joins("JOIN domains ON vps.domain_id = domains.id").
|
||||
Joins("JOIN customers ON domains.customer_id = customers.id").
|
||||
Where("customers.user_id = ?", userID)
|
||||
|
||||
if vpsID != nil {
|
||||
query = query.Where("vps.id = ?", *vpsID)
|
||||
}
|
||||
|
||||
if err := query.Order("deployment_logs.created_at DESC").Find(&logs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (s *UserService) GetInvitationByToken(token string) (*models.Invitation, error) {
|
||||
var invitation models.Invitation
|
||||
if err := s.db.Where("token = ? AND status = 'pending' AND expires_at > ?", token, time.Now()).
|
||||
First(&invitation).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &invitation, nil
|
||||
}
|
||||
|
||||
func (s *UserService) AcceptInvitation(token, password, firstName, lastName string) error {
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Get invitation
|
||||
var invitation models.Invitation
|
||||
if err := tx.Where("token = ? AND status = 'pending' AND expires_at > ?", token, time.Now()).
|
||||
First(&invitation).Error; err != nil {
|
||||
return fmt.Errorf("invitation not found or expired")
|
||||
}
|
||||
|
||||
// Get VPS to extract email
|
||||
var vps models.VPS
|
||||
if err := tx.Preload("Domain.Customer").Where("id = ?", invitation.VPSID).First(&vps).Error; err != nil {
|
||||
return fmt.Errorf("failed to get VPS: %w", err)
|
||||
}
|
||||
|
||||
// Create user
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
user := &models.User{
|
||||
ID: uuid.New(),
|
||||
Email: invitation.Email,
|
||||
FirstName: firstName,
|
||||
LastName: lastName,
|
||||
PasswordHash: string(hashedPassword),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := tx.Create(user).Error; err != nil {
|
||||
return fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
||||
// Update customer user_id
|
||||
if err := tx.Model(&vps.Domain.Customer).Update("user_id", user.ID).Error; err != nil {
|
||||
return fmt.Errorf("failed to update customer: %w", err)
|
||||
}
|
||||
|
||||
// Update invitation
|
||||
now := time.Now()
|
||||
invitation.Status = "accepted"
|
||||
invitation.AcceptedAt = &now
|
||||
if err := tx.Save(&invitation).Error; err != nil {
|
||||
return fmt.Errorf("failed to update invitation: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user