mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-19 08:01:16 +00:00
c822e95e7f
# 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
168 lines
6.3 KiB
Go
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"`
|
|
}
|