24 KiB
IGNY8 Account, User, Plan, Sites, Credits Documentation
Version: 1.0
Last Updated: 2025-01-XX
Purpose: Complete documentation of multi-tenancy architecture, access control, plans, credits, and related systems.
Table of Contents
- Overview
- Account Model
- User Model
- Plan Model
- Subscription Model
- Site Model
- Sector Model
- Industry & IndustrySector Models
- Access Control
- Credits System
- Plan Limits
- Multi-Tenancy Architecture
Overview
The IGNY8 platform uses a multi-account architecture with the following core entities:
- Account: Top-level organization/workspace
- User: Individual user accounts with roles
- Plan: Subscription plan templates with limits
- Subscription: Active subscription linking Account to Plan
- Site: Workspace within an Account (1-N relationship)
- Sector: Content category within a Site (1-5 per site)
- Industry: Global industry templates
- IndustrySector: Industry sector templates
Key Relationships
Account (1) ──< (N) User
Account (1) ──< (1) Subscription ──> (1) Plan
Account (1) ──< (N) Site
Site (1) ──< (1-5) Sector
Industry (1) ──< (N) IndustrySector
Site (1) ──> (1) Industry (optional)
Sector (1) ──> (1) IndustrySector (optional, template reference)
Account Model
File: backend/igny8_core/auth/models.py
Table: igny8_accounts
Fields
id: Primary keyname: CharField - Account nameslug: SlugField - Unique slug identifierowner: ForeignKey to User - Account ownerstripe_customer_id: CharField - Stripe customer ID (optional)plan: ForeignKey to Plan - Current subscription plancredits: IntegerField - Current credit balance (default: 0)status: CharField - Account status (choices: active, suspended, trial, cancelled)created_at: DateTimeField - Creation timestampupdated_at: DateTimeField - Last update timestamp
Status Values
active: Normal operationsuspended: Access temporarily revokedtrial: Limited trial accesscancelled: Account terminated
Methods
is_system_account()
Returns: True if account is a system account (aws-admin, default-account, default)
Purpose: System accounts bypass all filtering restrictions
Relationships
- One-to-Many: Users (via
user.account) - One-to-One: Subscription (via
account.subscription) - One-to-Many: Sites (via
site.account)
User Model
File: backend/igny8_core/auth/models.py
Table: igny8_users
Inherits: AbstractUser (Django's built-in user model)
Fields
id: Primary key (inherited from AbstractUser)email: EmailField - Unique, USERNAME_FIELDusername: CharField - Usernamepassword: Hashed password (inherited)account: ForeignKey to Account - User's account (null=True, blank=True)role: CharField - User role (choices: developer, owner, admin, editor, viewer, system_bot)created_at: DateTimeField - Creation timestampupdated_at: DateTimeField - Last update timestamp
Role Hierarchy (from highest to lowest)
- developer: Full system access, bypasses all restrictions
- owner: Full access to account, can manage users and billing
- admin: Admin access to account, can manage content and users
- editor: Can edit content, manage clusters/tasks
- viewer: Read-only access
- system_bot: System automation user
Methods
has_role(*roles)
Parameters: *roles - Variable number of role strings
Returns: True if user has any of the specified roles
is_owner_or_admin()
Returns: True if role is owner or admin
is_developer()
Returns: True if role is developer or is_superuser
is_admin_or_developer()
Returns: True if role is admin or developer (bypasses restrictions)
Purpose: Admin/Developer users bypass account/site/sector filtering
is_system_account_user()
Returns: True if user belongs to system account
Purpose: System account users bypass all filtering restrictions
get_accessible_sites()
Returns: QuerySet of sites user can access
Access Control Logic:
- System Account Users: All active sites across all accounts
- Developers: All active sites across all accounts
- Owners/Admins: All sites in their account
- Editors/Viewers: Only sites explicitly granted via
SiteUserAccess
Relationships
- Many-to-One: Account (via
user.account) - Many-to-Many: Sites (via
SiteUserAccess)
Plan Model
File: backend/igny8_core/auth/models.py
Table: igny8_plans
Fields
Plan Information
id: Primary keyname: CharField - Plan name (e.g., "Free", "Starter", "Growth")slug: SlugField - Unique slug identifierprice: DecimalField - Monthly pricebilling_cycle: CharField - Billing cycle (choices: monthly, annual)features: JSONField - Array of feature strings (e.g., ['ai_writer', 'image_gen', 'auto_publish'])is_active: BooleanField - Whether plan is available for subscription
User/Site/Scope Limits
max_users: IntegerField - Total users allowed per account (default: 1, min: 1)max_sites: IntegerField - Maximum sites allowed (default: 1, min: 1)max_industries: IntegerField - Optional limit for industries/sectors (nullable)max_author_profiles: IntegerField - Limit for saved writing styles (default: 5)
Planner Limits
max_keywords: IntegerField - Total keywords allowed globally (default: 1000)max_clusters: IntegerField - Total clusters allowed globally (default: 100)max_content_ideas: IntegerField - Total content ideas allowed globally (default: 300)daily_cluster_limit: IntegerField - Max clusters per day (default: 10)daily_keyword_import_limit: IntegerField - Seed keywords import limit per day (default: 100)monthly_cluster_ai_credits: IntegerField - AI credits for clustering (default: 50)
Writer Limits
daily_content_tasks: IntegerField - Max content tasks per day (default: 10)daily_ai_requests: IntegerField - Total AI executions per day (default: 50)monthly_word_count_limit: IntegerField - Monthly word limit for generated content (default: 50000)monthly_content_ai_credits: IntegerField - AI credit pool for content generation (default: 200)
Image Generation Limits
monthly_image_count: IntegerField - Max images per month (default: 100)daily_image_generation_limit: IntegerField - Max images per day (default: 25)monthly_image_ai_credits: IntegerField - AI credit pool for images (default: 100)max_images_per_task: IntegerField - Max images per content task (default: 4, min: 1)image_model_choices: JSONField - Allowed image models (e.g., ['dalle3', 'hidream'])
AI Request Controls
daily_ai_request_limit: IntegerField - Global daily AI request cap (default: 100)monthly_ai_credit_limit: IntegerField - Unified credit ceiling per month (default: 500)
Billing & Credits
included_credits: IntegerField - Monthly credits included (default: 0)extra_credit_price: DecimalField - Price per additional credit (default: 0.01)allow_credit_topup: BooleanField - Can user purchase more credits? (default: True)auto_credit_topup_threshold: IntegerField - Auto top-up trigger point (nullable)auto_credit_topup_amount: IntegerField - Credits to auto-buy (nullable)
Stripe Integration
stripe_product_id: CharField - Stripe product ID (nullable)stripe_price_id: CharField - Stripe price ID (nullable)
Legacy Fields
credits_per_month: IntegerField - DEPRECATED: Use included_credits instead (default: 0)
Methods
clean()
Purpose: Validates plan limits
Validations:
max_sitesmust be >= 1included_creditsmust be >= 0
get_effective_credits_per_month()
Returns: included_credits if set, otherwise credits_per_month (backward compatibility)
Relationships
- One-to-Many: Accounts (via
account.plan)
Plan Limits Enforcement
Current Status: Plan limits are defined but not currently enforced in AI functions.
Future Implementation: Plan limits should be checked before allowing operations.
Subscription Model
File: backend/igny8_core/auth/models.py
Table: igny8_subscriptions
Fields
id: Primary keyaccount: OneToOneField to Account - Account subscriptionstripe_subscription_id: CharField - Unique Stripe subscription IDstatus: CharField - Subscription status (choices: active, past_due, canceled, trialing)current_period_start: DateTimeField - Current billing period startcurrent_period_end: DateTimeField - Current billing period endcancel_at_period_end: BooleanField - Cancel at period end flag (default: False)created_at: DateTimeField - Creation timestampupdated_at: DateTimeField - Last update timestamp
Status Values
active: Subscription is active and paidpast_due: Payment failed, subscription past duecanceled: Subscription canceledtrialing: In trial period
Methods
Relationships
- One-to-One: Account (via
account.subscription)
Site Model
File: backend/igny8_core/auth/models.py
Table: igny8_sites
Inherits: AccountBaseModel
Fields
id: Primary keyaccount: ForeignKey to Account - Account this site belongs toname: CharField - Site nameslug: SlugField - Unique slug per accountdomain: URLField - Primary domain URL (optional)description: TextField - Site description (optional)industry: ForeignKey to Industry - Industry this site belongs to (optional)is_active: BooleanField - Whether site is active (default: True)status: CharField - Site status (choices: active, inactive, suspended, default: active)wp_url: URLField - WordPress site URL (optional, for WordPress integration)wp_username: CharField - WordPress username (optional)wp_app_password: CharField - WordPress app password (optional)created_at: DateTimeField - Creation timestampupdated_at: DateTimeField - Last update timestamp
Unique Constraint
(account, slug)- Slug unique per account
Methods
get_active_sectors_count()
Returns: Count of active sectors for this site
can_add_sector()
Returns: True if site can add another sector (max 5 sectors per site)
Relationships
- Many-to-One: Account (via
site.account) - Many-to-One: Industry (via
site.industry, optional) - One-to-Many: Sectors (via
sector.site) - Many-to-Many: Users (via
SiteUserAccess)
Site Activation
Important: Multiple sites can be active simultaneously. The previous restriction of "only 1 site can be active at one time" has been removed.
Sector Model
File: backend/igny8_core/auth/models.py
Table: igny8_sectors
Inherits: AccountBaseModel
Fields
id: Primary keyaccount: ForeignKey to Account - Account this sector belongs tosite: ForeignKey to Site - Site this sector belongs toindustry_sector: ForeignKey to IndustrySector - Reference to industry sector template (optional)name: CharField - Sector nameslug: SlugField - Unique slug per sitedescription: TextField - Sector description (optional)is_active: BooleanField - Whether sector is active (default: True)status: CharField - Sector status (choices: active, inactive, default: active)created_at: DateTimeField - Creation timestampupdated_at: DateTimeField - Last update timestamp
Unique Constraint
(site, slug)- Slug unique per site
Validation
On Save:
- Automatically sets
accountfromsite.account - Validates that
industry_sector.industrymatchessite.industry(if both set) - Validates sector limit: Maximum 5 active sectors per site
Methods
industry (property)
Returns: Industry for this sector (from industry_sector.industry if set)
Relationships
- Many-to-One: Account (via
sector.account) - Many-to-One: Site (via
sector.site) - Many-to-One: IndustrySector (via
sector.industry_sector, optional, template reference) - One-to-Many: Keywords, Clusters, ContentIdeas, Tasks (via their
sectorfield)
Industry & IndustrySector Models
Industry Model
File: backend/igny8_core/auth/models.py
Table: igny8_industries
Purpose: Global industry templates.
Fields
id: Primary keyname: CharField - Industry name (unique)slug: SlugField - Unique slug identifierdescription: TextField - Industry description (optional)is_active: BooleanField - Whether industry is active (default: True)created_at: DateTimeField - Creation timestampupdated_at: DateTimeField - Last update timestamp
Relationships
- One-to-Many: Sites (via
site.industry) - One-to-Many: IndustrySectors (via
industry_sector.industry)
IndustrySector Model
File: backend/igny8_core/auth/models.py
Table: igny8_industry_sectors
Purpose: Sector templates within industries.
Fields
id: Primary keyindustry: ForeignKey to Industry - Industry this sector belongs toname: CharField - Sector nameslug: SlugField - Unique slug per industrydescription: TextField - Sector description (optional)suggested_keywords: JSONField - List of suggested keywords for this sector templateis_active: BooleanField - Whether sector is active (default: True)created_at: DateTimeField - Creation timestampupdated_at: DateTimeField - Last update timestamp
Unique Constraint
(industry, slug)- Slug unique per industry
Relationships
- Many-to-One: Industry (via
industry_sector.industry) - One-to-Many: Sectors (via
sector.industry_sector, template reference) - One-to-Many: SeedKeywords (via
seed_keyword.sector)
Access Control
Account Isolation
Principle: All data is isolated by account.
Implementation:
- All models inherit
AccountBaseModel(hasaccountForeignKey) - All ViewSets inherit
AccountModelViewSet(filters byrequest.account) - Middleware sets
request.accountfrom JWT token
Access Control:
- Admin/Developer users: Bypass account filtering (see all accounts)
- System account users: Bypass account filtering (see all accounts)
- Regular users: Only see data from their account
Site Access Control
Principle: Users can only access sites they have permission to access.
Implementation:
User.get_accessible_sites()returns sites user can access- ViewSets filter by accessible sites
SiteUserAccessmodel controls explicit site access for editors/viewers
Access Levels:
- System Account Users: All active sites across all accounts
- Developers: All active sites across all accounts
- Owners/Admins: All sites in their account
- Editors/Viewers: Only sites explicitly granted via
SiteUserAccess
SiteUserAccess Model
File: backend/igny8_core/auth/models.py
Table: igny8_site_user_access
Purpose: Many-to-many relationship between Users and Sites for explicit access control.
Fields
id: Primary keyuser: ForeignKey to Usersite: ForeignKey to Sitegranted_at: DateTimeField - When access was grantedgranted_by: ForeignKey to User - Who granted access (optional)
Unique Constraint
(user, site)- One access record per user-site pair
Usage
- Owners and Admins have automatic access to all sites in their account
- Editors and Viewers require explicit
SiteUserAccessrecords - System account users and Developers bypass this check
Role-Based Access Control (RBAC)
Role Hierarchy:
- developer: Full system access, bypasses all restrictions
- owner: Full account access, can manage users and billing
- admin: Account admin access, can manage content and users
- editor: Content editing access, can manage clusters/tasks
- viewer: Read-only access
Permission Checks:
user.has_role(*roles): Check if user has any of the specified rolesuser.is_owner_or_admin(): Check if user is owner or adminuser.is_developer(): Check if user is developeruser.is_admin_or_developer(): Check if user is admin or developer (bypasses restrictions)
Credits System
Account Credits
Field: Account.credits (IntegerField, default: 0)
Purpose: Current credit balance for the account.
Usage: Credits are deducted for AI operations and added through purchases or subscriptions.
Credit Models
CreditTransaction
File: backend/igny8_core/modules/billing/models.py
Table: igny8_credit_transactions
Purpose: Track all credit transactions (additions, deductions).
Fields
id: Primary keyaccount: ForeignKey to Accounttransaction_type: CharField - Type of transaction (choices: purchase, subscription, refund, deduction, adjustment)amount: IntegerField - Positive for additions, negative for deductionsbalance_after: IntegerField - Credit balance after this transactiondescription: CharField - Transaction descriptionmetadata: JSONField - Additional context (AI call details, etc.)created_at: DateTimeField - Creation timestamp
CreditUsageLog
File: backend/igny8_core/modules/billing/models.py
Table: igny8_credit_usage_logs
Purpose: Detailed log of credit usage per AI operation.
Fields
id: Primary keyaccount: ForeignKey to Accountoperation_type: CharField - Type of operation (choices: clustering, ideas, content, images, reparse)credits_used: IntegerField - Number of credits usedcost_usd: DecimalField - Cost in USD (optional)model_used: CharField - AI model used (optional)tokens_input: IntegerField - Input tokens (optional)tokens_output: IntegerField - Output tokens (optional)related_object_type: CharField - Related object type (optional)related_object_id: IntegerField - Related object ID (optional)metadata: JSONField - Additional metadatacreated_at: DateTimeField - Creation timestamp
CreditService
File: backend/igny8_core/modules/billing/services.py
Purpose: Service for managing credits.
Methods
check_credits(account, required_credits)
Purpose: Check if account has enough credits.
Raises: InsufficientCreditsError if account doesn't have enough credits
deduct_credits(account, amount, operation_type, description, ...)
Purpose: Deduct credits and log transaction.
Parameters:
account: Account instanceamount: Number of credits to deductoperation_type: Type of operationdescription: Description of the transactionmetadata: Optional metadata dictcost_usd: Optional cost in USDmodel_used: Optional AI model usedtokens_input: Optional input tokenstokens_output: Optional output tokensrelated_object_type: Optional related object typerelated_object_id: Optional related object ID
Returns: New credit balance
Creates:
CreditTransactionrecordCreditUsageLogrecord
add_credits(account, amount, transaction_type, description, ...)
Purpose: Add credits (purchase, subscription, etc.).
Parameters:
account: Account instanceamount: Number of credits to addtransaction_type: Type of transactiondescription: Description of the transactionmetadata: Optional metadata dict
Returns: New credit balance
Creates: CreditTransaction record
calculate_credits_for_operation(operation_type, **kwargs)
Purpose: Calculate credits needed for an operation.
Parameters:
operation_type: Type of operation**kwargs: Operation-specific parameters
Returns: Number of credits required
Raises: CreditCalculationError if calculation fails
Credit Costs:
clustering: 1 credit per 30 keywordsideas: 1 credit per ideacontent: 3 credits per content pieceimages: 1 credit per imagereparse: 1 credit per reparse
Credit Integration
Current Status: Credits system is implemented but not currently enforced in AI functions.
Future Implementation: Credits should be checked and deducted before allowing AI operations.
Plan Limits
Planner Limits
max_keywords: Total keywords allowed (not enforced)max_clusters: Total clusters allowed (not enforced)max_content_ideas: Total content ideas allowed (not enforced)daily_cluster_limit: Max clusters per day (not enforced)monthly_cluster_ai_credits: AI credits for clustering (not enforced)
Writer Limits
daily_content_tasks: Max content tasks per day (not enforced)daily_ai_requests: Total AI executions per day (not enforced)monthly_word_count_limit: Monthly word limit (not enforced)monthly_content_ai_credits: AI credits for content (not enforced)
Image Limits
monthly_image_count: Max images per month (not enforced)daily_image_generation_limit: Max images per day (not enforced)monthly_image_ai_credits: AI credits for images (not enforced)max_images_per_task: Max images per task (not enforced)image_model_choices: Allowed image models (not enforced)
Feature Flags
features: JSON array of enabled features (e.g.,['ai_writer', 'image_gen', 'auto_publish'])- Not enforced: Feature flags are not checked before allowing AI operations
Plan Limits Enforcement
Current Status: Plan limits are defined but not currently enforced in AI functions.
Future Implementation: Plan limits should be checked before allowing operations.
Multi-Tenancy Architecture
Account Isolation
Principle: All data is isolated by account.
Implementation:
- Model Level: All models inherit
AccountBaseModel(hasaccountForeignKey) - ViewSet Level: All ViewSets inherit
AccountModelViewSet(filters byrequest.account) - Middleware Level:
AccountContextMiddlewaresetsrequest.accountfrom JWT token
Access Control:
- Admin/Developer users: Bypass account filtering (see all accounts)
- System account users: Bypass account filtering (see all accounts)
- Regular users: Only see data from their account
Site/Sector Hierarchy
Structure:
Account (1) ──< (N) Site
Site (1) ──< (1-5) Sector
Sector (1) ──< (N) Keywords, Clusters, ContentIdeas, Tasks
Implementation:
- Models inherit
SiteSectorBaseModel(hassiteandsectorForeignKeys) - ViewSets inherit
SiteSectorModelViewSet(filters by accessible sites) - User access control via
User.get_accessible_sites()
Site Access Control:
- System account users: All active sites
- Developers: All active sites
- Owners/Admins: All sites in their account
- Editors/Viewers: Only sites granted via
SiteUserAccess
Data Isolation Flow
- Request Arrives: JWT token contains account ID
- Middleware:
AccountContextMiddlewareextracts account ID and setsrequest.account - ViewSet:
AccountModelViewSet.get_queryset()filters byrequest.account - Database: All queries automatically filtered by account
Override: Admin/Developer users bypass account filtering
Summary
The IGNY8 multi-tenancy architecture provides:
- Complete Account Isolation: All data isolated by account with automatic filtering
- Site/Sector Hierarchy: Hierarchical organization with access control
- Role-Based Access Control: Granular permissions based on user roles
- Plan-Based Limits: Comprehensive plan limits (defined but not enforced)
- Credits System: Credit tracking and deduction system (implemented but not enforced)
- Flexible Site Access: Multiple sites can be active simultaneously
- Industry Templates: Global industry and sector templates
This architecture ensures data security, scalability, and flexibility while providing a solid foundation for subscription management and billing.