package models import ( "time" "github.com/google/uuid" "gorm.io/gorm" ) type User struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` Email string `gorm:"uniqueIndex;not null" json:"email"` FirstName string `gorm:"not null" json:"first_name"` LastName string `gorm:"not null" json:"last_name"` PasswordHash string `gorm:"not null" json:"-"` Role string `gorm:"default:'user'" json:"role"` // user, admin IsActive bool `gorm:"default:true" json:"is_active"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // Relationships with constraints Customers []Customer `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE" json:"customers,omitempty"` } func (u *User) BeforeCreate(tx *gorm.DB) error { if u.Role == "" { u.Role = "user" } return nil } type Customer struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` UserID uuid.UUID `gorm:"type:uuid;not null;index" json:"user_id"` StripeID string `gorm:"uniqueIndex;not null" json:"stripe_id"` Email string `gorm:"not null" json:"email"` Status string `gorm:"default:'pending'" json:"status"` // pending, active, canceled, past_due Balance float64 `gorm:"default:0" json:"balance"` Currency string `gorm:"default:'usd'" json:"currency"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // Relationships with proper constraints User User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE" json:"user,omitempty"` Subscriptions []Subscription `gorm:"foreignKey:CustomerID;constraint:OnDelete:CASCADE" json:"subscriptions,omitempty"` Domains []Domain `gorm:"foreignKey:CustomerID;constraint:OnDelete:CASCADE" json:"domains,omitempty"` } func (c *Customer) BeforeCreate(tx *gorm.DB) error { if c.Status == "" { c.Status = "pending" } if c.Currency == "" { c.Currency = "usd" } return nil } type Subscription struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` CustomerID uuid.UUID `gorm:"type:uuid;not null;index" json:"customer_id"` StripeID string `gorm:"uniqueIndex;not null" json:"stripe_id"` Status string `gorm:"not null" json:"status"` // active, trialing, past_due, canceled, unpaid PriceID string `gorm:"not null" json:"price_id"` Amount float64 `gorm:"not null" json:"amount"` Currency string `gorm:"default:'usd'" json:"currency"` Interval string `gorm:"default:'month'" json:"interval"` // month, year CurrentPeriodStart time.Time `json:"current_period_start"` CurrentPeriodEnd time.Time `json:"current_period_end"` CancelAtPeriodEnd bool `gorm:"default:false" json:"cancel_at_period_end"` CanceledAt *time.Time `json:"canceled_at,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // Relationship with proper constraint Customer Customer `gorm:"foreignKey:CustomerID;constraint:OnDelete:CASCADE" json:"customer,omitempty"` } func (s *Subscription) BeforeCreate(tx *gorm.DB) error { if s.Status == "" { s.Status = "active" } if s.Currency == "" { s.Currency = "usd" } if s.Interval == "" { s.Interval = "month" } return nil } type Domain struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` CustomerID uuid.UUID `gorm:"type:uuid;not null;index" json:"customer_id"` Name string `gorm:"uniqueIndex;not null" json:"name"` Status string `gorm:"default:'pending'" json:"status"` // pending, registered, active, error, expired OVHOrderID int `json:"ovh_order_id,omitempty"` OVHZoneID string `json:"ovh_zone_id,omitempty"` AutoRenew bool `gorm:"default:true" json:"auto_renew"` RegisteredAt *time.Time `json:"registered_at,omitempty"` ExpiresAt *time.Time `json:"expires_at,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // Relationships with proper constraints Customer Customer `gorm:"foreignKey:CustomerID;constraint:OnDelete:CASCADE" json:"customer,omitempty"` VPS []VPS `gorm:"foreignKey:DomainID;constraint:OnDelete:CASCADE" json:"vps,omitempty"` } func (d *Domain) BeforeCreate(tx *gorm.DB) error { if d.Status == "" { d.Status = "pending" } d.AutoRenew = true return nil } type VPS struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` DomainID uuid.UUID `gorm:"type:uuid;not null" json:"domain_id"` OVHInstanceID string `gorm:"uniqueIndex;not null" json:"ovh_instance_id"` Name string `gorm:"not null" json:"name"` Status string `gorm:"default:'pending'" json:"status"` // pending, provisioning, active, error, terminated IPAddress string `json:"ip_address,omitempty"` SSHKey string `gorm:"not null" json:"-"` CloudronURL string `json:"cloudron_url,omitempty"` CloudronStatus string `json:"cloudron_status,omitempty"` // installing, ready, error CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` TerminatedAt *time.Time `json:"terminated_at,omitempty"` Domain Domain `gorm:"foreignKey:DomainID" json:"domain,omitempty"` } type DeploymentLog struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` VPSID uuid.UUID `gorm:"type:uuid;not null" json:"vps_id"` Step string `gorm:"not null" json:"step"` // vps_provision, cloudron_install, dns_config, etc Status string `gorm:"not null" json:"status"` // started, completed, failed Message string `gorm:"type:text" json:"message,omitempty"` Details string `gorm:"type:text" json:"details,omitempty"` CreatedAt time.Time `json:"created_at"` VPS VPS `gorm:"foreignKey:VPSID" json:"vps,omitempty"` } type Invitation struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` VPSID uuid.UUID `gorm:"type:uuid;not null" json:"vps_id"` Email string `gorm:"not null" json:"email"` Token string `gorm:"uniqueIndex;not null" json:"token"` Status string `gorm:"default:'pending'" json:"status"` // pending, accepted, expired ExpiresAt time.Time `json:"expires_at"` AcceptedAt *time.Time `json:"accepted_at,omitempty"` CreatedAt time.Time `json:"created_at"` VPS VPS `gorm:"foreignKey:VPSID" json:"vps,omitempty"` }