Files
igny8/docs/05-ACCOUNT-USER-PLAN.md
Gitea Deploy 961362e088 Add SEO fields to Tasks model, improve content generation response handling, and enhance progress bar animation
- Added primary_keyword, secondary_keywords, tags, and categories fields to Tasks model
- Updated generate_content function to handle full JSON response with all SEO fields
- Improved progress bar animation: smooth 1% increments every 300ms
- Enhanced step detection for content generation vs clustering vs ideas
- Fixed progress modal to show correct messages for each function type
- Added comprehensive logging to Keywords and Tasks pages for AI functions
- Fixed error handling to show meaningful error messages instead of generic failures
2025-11-09 21:22:34 +00:00

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

  1. Overview
  2. Account Model
  3. User Model
  4. Plan Model
  5. Subscription Model
  6. Site Model
  7. Sector Model
  8. Industry & IndustrySector Models
  9. Access Control
  10. Credits System
  11. Plan Limits
  12. 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 key
  • name: CharField - Account name
  • slug: SlugField - Unique slug identifier
  • owner: ForeignKey to User - Account owner
  • stripe_customer_id: CharField - Stripe customer ID (optional)
  • plan: ForeignKey to Plan - Current subscription plan
  • credits: IntegerField - Current credit balance (default: 0)
  • status: CharField - Account status (choices: active, suspended, trial, cancelled)
  • created_at: DateTimeField - Creation timestamp
  • updated_at: DateTimeField - Last update timestamp

Status Values

  • active: Normal operation
  • suspended: Access temporarily revoked
  • trial: Limited trial access
  • cancelled: 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_FIELD
  • username: CharField - Username
  • password: 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 timestamp
  • updated_at: DateTimeField - Last update timestamp

Role Hierarchy (from highest to lowest)

  1. developer: Full system access, bypasses all restrictions
  2. owner: Full access to account, can manage users and billing
  3. admin: Admin access to account, can manage content and users
  4. editor: Can edit content, manage clusters/tasks
  5. viewer: Read-only access
  6. 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 key
  • name: CharField - Plan name (e.g., "Free", "Starter", "Growth")
  • slug: SlugField - Unique slug identifier
  • price: DecimalField - Monthly price
  • billing_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_sites must be >= 1
  • included_credits must 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 key
  • account: OneToOneField to Account - Account subscription
  • stripe_subscription_id: CharField - Unique Stripe subscription ID
  • status: CharField - Subscription status (choices: active, past_due, canceled, trialing)
  • current_period_start: DateTimeField - Current billing period start
  • current_period_end: DateTimeField - Current billing period end
  • cancel_at_period_end: BooleanField - Cancel at period end flag (default: False)
  • created_at: DateTimeField - Creation timestamp
  • updated_at: DateTimeField - Last update timestamp

Status Values

  • active: Subscription is active and paid
  • past_due: Payment failed, subscription past due
  • canceled: Subscription canceled
  • trialing: 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 key
  • account: ForeignKey to Account - Account this site belongs to
  • name: CharField - Site name
  • slug: SlugField - Unique slug per account
  • domain: 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 timestamp
  • updated_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 key
  • account: ForeignKey to Account - Account this sector belongs to
  • site: ForeignKey to Site - Site this sector belongs to
  • industry_sector: ForeignKey to IndustrySector - Reference to industry sector template (optional)
  • name: CharField - Sector name
  • slug: SlugField - Unique slug per site
  • description: 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 timestamp
  • updated_at: DateTimeField - Last update timestamp

Unique Constraint

  • (site, slug) - Slug unique per site

Validation

On Save:

  1. Automatically sets account from site.account
  2. Validates that industry_sector.industry matches site.industry (if both set)
  3. 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 sector field)

Industry & IndustrySector Models

Industry Model

File: backend/igny8_core/auth/models.py

Table: igny8_industries

Purpose: Global industry templates.

Fields

  • id: Primary key
  • name: CharField - Industry name (unique)
  • slug: SlugField - Unique slug identifier
  • description: TextField - Industry description (optional)
  • is_active: BooleanField - Whether industry is active (default: True)
  • created_at: DateTimeField - Creation timestamp
  • updated_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 key
  • industry: ForeignKey to Industry - Industry this sector belongs to
  • name: CharField - Sector name
  • slug: SlugField - Unique slug per industry
  • description: TextField - Sector description (optional)
  • suggested_keywords: JSONField - List of suggested keywords for this sector template
  • is_active: BooleanField - Whether sector is active (default: True)
  • created_at: DateTimeField - Creation timestamp
  • updated_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 (has account ForeignKey)
  • All ViewSets inherit AccountModelViewSet (filters by request.account)
  • Middleware sets request.account from 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
  • SiteUserAccess model 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 key
  • user: ForeignKey to User
  • site: ForeignKey to Site
  • granted_at: DateTimeField - When access was granted
  • granted_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 SiteUserAccess records
  • System account users and Developers bypass this check

Role-Based Access Control (RBAC)

Role Hierarchy:

  1. developer: Full system access, bypasses all restrictions
  2. owner: Full account access, can manage users and billing
  3. admin: Account admin access, can manage content and users
  4. editor: Content editing access, can manage clusters/tasks
  5. viewer: Read-only access

Permission Checks:

  • user.has_role(*roles): Check if user has any of the specified roles
  • user.is_owner_or_admin(): Check if user is owner or admin
  • user.is_developer(): Check if user is developer
  • user.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 key
  • account: ForeignKey to Account
  • transaction_type: CharField - Type of transaction (choices: purchase, subscription, refund, deduction, adjustment)
  • amount: IntegerField - Positive for additions, negative for deductions
  • balance_after: IntegerField - Credit balance after this transaction
  • description: CharField - Transaction description
  • metadata: 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 key
  • account: ForeignKey to Account
  • operation_type: CharField - Type of operation (choices: clustering, ideas, content, images, reparse)
  • credits_used: IntegerField - Number of credits used
  • cost_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 metadata
  • created_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 instance
  • amount: Number of credits to deduct
  • operation_type: Type of operation
  • description: Description of the transaction
  • metadata: Optional metadata dict
  • cost_usd: Optional cost in USD
  • model_used: Optional AI model used
  • tokens_input: Optional input tokens
  • tokens_output: Optional output tokens
  • related_object_type: Optional related object type
  • related_object_id: Optional related object ID

Returns: New credit balance

Creates:

  • CreditTransaction record
  • CreditUsageLog record
add_credits(account, amount, transaction_type, description, ...)

Purpose: Add credits (purchase, subscription, etc.).

Parameters:

  • account: Account instance
  • amount: Number of credits to add
  • transaction_type: Type of transaction
  • description: Description of the transaction
  • metadata: 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 keywords
  • ideas: 1 credit per idea
  • content: 3 credits per content piece
  • images: 1 credit per image
  • reparse: 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:

  1. Model Level: All models inherit AccountBaseModel (has account ForeignKey)
  2. ViewSet Level: All ViewSets inherit AccountModelViewSet (filters by request.account)
  3. Middleware Level: AccountContextMiddleware sets request.account from 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 (has site and sector ForeignKeys)
  • 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

  1. Request Arrives: JWT token contains account ID
  2. Middleware: AccountContextMiddleware extracts account ID and sets request.account
  3. ViewSet: AccountModelViewSet.get_queryset() filters by request.account
  4. Database: All queries automatically filtered by account

Override: Admin/Developer users bypass account filtering


Summary

The IGNY8 multi-tenancy architecture provides:

  1. Complete Account Isolation: All data isolated by account with automatic filtering
  2. Site/Sector Hierarchy: Hierarchical organization with access control
  3. Role-Based Access Control: Granular permissions based on user roles
  4. Plan-Based Limits: Comprehensive plan limits (defined but not enforced)
  5. Credits System: Credit tracking and deduction system (implemented but not enforced)
  6. Flexible Site Access: Multiple sites can be active simultaneously
  7. 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.