Files
warmbly/internal/models/organization.go
T
Matthew Meszaros c822e95e7f Merge remote-tracking branch 'origin/main' into feature/danger-zone-delayed-deletions
# Conflicts:
#	cmd/backend/main.go
#	internal/api/handler/handler.go
#	internal/api/routes.go
#	internal/models/audit.go
#	internal/models/organization.go
#	internal/models/user.go
#	internal/repository/pg_organization.go
#	internal/repository/pg_user.go
2026-05-24 04:12:10 +00:00

168 lines
6.3 KiB
Go

package models
import (
"time"
"github.com/google/uuid"
)
// Organization represents a multi-user organization/workspace
type Organization struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Slug *string `json:"slug,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
OwnerUserID uuid.UUID `json:"owner_user_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Soft-delete window. When DeletionScheduledFor is non-nil the
// organization is "pending deletion" and will be hard-deleted at
// that timestamp unless cancelled.
DeletionScheduledAt *time.Time `json:"deletion_scheduled_at,omitempty"`
DeletionScheduledFor *time.Time `json:"deletion_scheduled_for,omitempty"`
// Joined data
Owner *User `json:"owner,omitempty"`
}
// IsPendingDeletion reports whether the organization is currently
// scheduled for a delayed hard delete.
func (o *Organization) IsPendingDeletion() bool {
return o.DeletionScheduledFor != nil
}
// OrganizationMember represents a user's membership in an organization
type OrganizationMember struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
UserID uuid.UUID `json:"user_id"`
Role string `json:"role"`
Permissions OrganizationPermission `json:"permissions"`
InvitedBy *uuid.UUID `json:"invited_by,omitempty"`
InvitedAt time.Time `json:"invited_at"`
AcceptedAt *time.Time `json:"accepted_at,omitempty"`
// Joined data
User *User `json:"user,omitempty"`
Organization *Organization `json:"organization,omitempty"`
}
// HasPermission checks if the member has a specific permission
func (m *OrganizationMember) HasPermission(perm OrganizationPermission) bool {
return m.Permissions.HasPermission(perm)
}
// IsOwner returns true if the member is the organization owner
func (m *OrganizationMember) IsOwner() bool {
return m.Role == string(RoleOwner)
}
// OrganizationInvitation represents a pending invitation to join an organization
type OrganizationInvitation struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
Email string `json:"email"`
Role string `json:"role"`
Permissions OrganizationPermission `json:"permissions"`
InvitedBy uuid.UUID `json:"invited_by"`
Token string `json:"-"` // Never expose token in JSON
ExpiresAt time.Time `json:"expires_at"`
CreatedAt time.Time `json:"created_at"`
// Joined data
Organization *Organization `json:"organization,omitempty"`
InvitedByUser *User `json:"invited_by_user,omitempty"`
}
// IsExpired returns true if the invitation has expired
func (i *OrganizationInvitation) IsExpired() bool {
return time.Now().After(i.ExpiresAt)
}
// EnterpriseInquiry represents a contact request for enterprise pricing
type EnterpriseInquiry struct {
ID uuid.UUID `json:"id"`
CompanyName string `json:"company_name"`
ContactName string `json:"contact_name"`
ContactEmail string `json:"contact_email"`
EstimatedVolume *int `json:"estimated_volume,omitempty"`
TeamSize *int `json:"team_size,omitempty"`
Notes string `json:"notes,omitempty"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
ProcessedAt *time.Time `json:"processed_at,omitempty"`
ProcessedBy *uuid.UUID `json:"processed_by,omitempty"`
}
// EnterpriseInquiryStatus represents the status of an enterprise inquiry
type EnterpriseInquiryStatus string
const (
EnterpriseInquiryStatusPending EnterpriseInquiryStatus = "pending"
EnterpriseInquiryStatusContacted EnterpriseInquiryStatus = "contacted"
EnterpriseInquiryStatusConverted EnterpriseInquiryStatus = "converted"
EnterpriseInquiryStatusDeclined EnterpriseInquiryStatus = "declined"
)
// CreateOrganizationRequest represents the request to create a new organization
type CreateOrganizationRequest struct {
Name string `json:"name" binding:"required,min=1,max=255"`
}
// UpdateOrganizationRequest represents the request to update an organization
type UpdateOrganizationRequest struct {
Name *string `json:"name,omitempty"`
Slug *string `json:"slug,omitempty"`
}
// InviteMemberRequest represents the request to invite a new member
type InviteMemberRequest struct {
Email string `json:"email" binding:"required,email"`
Role string `json:"role,omitempty"`
Permissions *uint16 `json:"permissions,omitempty"`
}
// UpdateMemberRequest represents the request to update a member's role/permissions
type UpdateMemberRequest struct {
Role *string `json:"role,omitempty"`
Permissions *uint16 `json:"permissions,omitempty"`
}
// TransferOwnershipRequest represents the request to transfer organization ownership
type TransferOwnershipRequest struct {
NewOwnerUserID uuid.UUID `json:"new_owner_user_id" binding:"required"`
}
// AcceptInvitationRequest represents the request to accept an invitation
type AcceptInvitationRequest struct {
Token string `json:"token" binding:"required"`
}
// OrganizationCounts represents resource counts for an organization
type OrganizationCounts struct {
TotalCampaigns int `json:"total_campaigns"`
ActiveCampaigns int `json:"active_campaigns"`
TotalContacts int `json:"total_contacts"`
TotalMembers int `json:"total_members"`
EmailAccounts int `json:"email_accounts"`
EmailsSentToday int `json:"emails_sent_today"`
}
// OrganizationLimits represents the limits for an organization based on their plan
type OrganizationLimits struct {
MaxCampaigns *int `json:"max_campaigns,omitempty"`
MaxActiveCampaigns *int `json:"max_active_campaigns,omitempty"`
MaxTeamMembers *int `json:"max_team_members,omitempty"`
MaxEmailAccounts *int `json:"max_email_accounts,omitempty"`
MaxContacts *int `json:"max_contacts,omitempty"`
DailyCampaignLimit *int `json:"daily_campaign_limit,omitempty"`
}
// OrganizationWithLimits combines organization with its limits and counts
type OrganizationWithLimits struct {
Organization
Limits *OrganizationLimits `json:"limits,omitempty"`
Counts *OrganizationCounts `json:"counts,omitempty"`
}