new docs
This commit is contained in:
739
docs/05-ACCOUNT-USER-PLAN.md
Normal file
739
docs/05-ACCOUNT-USER-PLAN.md
Normal file
@@ -0,0 +1,739 @@
|
||||
# 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](#overview)
|
||||
2. [Account Model](#account-model)
|
||||
3. [User Model](#user-model)
|
||||
4. [Plan Model](#plan-model)
|
||||
5. [Subscription Model](#subscription-model)
|
||||
6. [Site Model](#site-model)
|
||||
7. [Sector Model](#sector-model)
|
||||
8. [Industry & IndustrySector Models](#industry--industrysector-models)
|
||||
9. [Access Control](#access-control)
|
||||
10. [Credits System](#credits-system)
|
||||
11. [Plan Limits](#plan-limits)
|
||||
12. [Multi-Tenancy Architecture](#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.
|
||||
|
||||
Reference in New Issue
Block a user