This commit is contained in:
IGNY8 VPS (Salman)
2025-12-15 03:04:06 +00:00
parent 125489df0f
commit d9dbb1e4b8
8 changed files with 1173 additions and 4017 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,242 +0,0 @@
# Phase 2 Implementation Summary
**Date:** December 14, 2025
**Status:** ✅ COMPLETED + Critical Fix Applied
---
## What Was Accomplished
### 🔴 **CRITICAL FIX: Issue #5 - Sidebar Navigation**
**Problem:** Custom sidebar with organized groups only appeared on home and group-level pages, but showed default Django/Unfold sidebar on model list/detail pages.
**Solution Implemented:**
Modified `/data/app/igny8/backend/igny8_core/admin/site.py`:
```python
def each_context(self, request):
"""Force custom sidebar on ALL pages"""
context = super().each_context(request)
custom_apps = self.get_app_list(request, app_label=None)
context['available_apps'] = custom_apps
context['app_list'] = custom_apps # Added for full compatibility
return context
def get_app_list(self, request, app_label=None):
"""ALWAYS ignore app_label parameter"""
app_dict = self._build_app_dict(request, None) # Force None
# ... builds custom organized sidebar
```
**Result:**
- ✅ Custom sidebar now appears consistently on ALL admin pages
- ✅ Navigation is consistent throughout entire admin interface
- ✅ Users no longer lose context when drilling into models
- ✅ Overall admin grade improved from C+ to B
---
## Phase 2: Bulk Operations & Export - COMPLETED
### Export Functionality Added
**Auth Module:**
1. **Account Admin** - Added `AccountResource` and `ExportMixin`
- Exports: id, name, slug, owner email, plan name, status, credits, billing_country, timestamps
2. **Site Admin** - Added `SiteResource` and `ExportMixin`
- Exports: id, name, slug, account name, industry name, domain, status, is_active, site_type, hosting_type, created_at
3. **User Admin** - Added `UserResource` and `ExportMixin`
- Exports: id, email, username, account name, role, is_active, is_staff, created_at, last_login
**Publishing Module:**
4. **PublishingRecord Admin** - Added `PublishingRecordResource` and `ExportMixin`
- Exports: id, content title, site name, sector name, destination, status, destination_url, published_at, created_at
**Integration Module:**
5. **SyncEvent Admin** - Added `SyncEventResource` and `ExportMixin`
- Exports: id, integration site name, site name, event_type, action, success, external_id, description, created_at
### Bulk Actions Added
**Automation Module:**
- `AutomationConfig Admin`:
-`bulk_enable` - Enable selected automation configs
-`bulk_disable` - Disable selected automation configs
**Publishing Module:**
- `PublishingRecord Admin`:
-`bulk_retry_failed` - Retry failed publishing records
**Integration Module:**
- `SiteIntegration Admin`:
-`bulk_enable_sync` - Enable sync for selected integrations
-`bulk_disable_sync` - Disable sync for selected integrations
-`bulk_trigger_sync` - Trigger sync now for selected integrations
- `SyncEvent Admin`:
-`bulk_mark_reviewed` - Mark sync events as reviewed (placeholder for future 'reviewed' field)
### Previously Completed (Earlier in Phase 2)
- ✅ Advanced Unfold filters on Tasks, Content, Keywords, Clusters, ContentIdeas
- ✅ Export on Payment, CreditTransaction, Keywords
- ✅ Bulk actions on Tasks (status changes), Content (status changes), Keywords (cluster assignment)
---
## Files Modified
1. **`/data/app/igny8/backend/igny8_core/admin/site.py`**
- Fixed `each_context()` method
- Fixed `get_app_list()` method
- Added documentation comments
2. **`/data/app/igny8/backend/igny8_core/auth/admin.py`**
- Added import_export imports
- Created AccountResource, SiteResource, UserResource
- Updated AccountAdmin, SiteAdmin, UserAdmin with ExportMixin
3. **`/data/app/igny8/backend/igny8_core/business/automation/admin.py`**
- Added bulk_enable and bulk_disable actions
- Added messages import
4. **`/data/app/igny8/backend/igny8_core/business/publishing/admin.py`**
- Added import_export imports
- Created PublishingRecordResource
- Updated PublishingRecordAdmin with ExportMixin
- Added bulk_retry_failed action
5. **`/data/app/igny8/backend/igny8_core/business/integration/admin.py`**
- Added import_export imports
- Created SyncEventResource
- Updated SiteIntegrationAdmin with bulk sync actions
- Updated SyncEventAdmin with ExportMixin and bulk_mark_reviewed
6. **Documentation Updates:**
- `/data/app/igny8/ADMIN-IMPLEMENTATION-STATUS.md`
- `/data/app/igny8/DJANGO-ADMIN-AUDIT-REPORT.md`
---
## Impact & Benefits
### User Experience
- **Consistent Navigation:** Custom sidebar appears on every admin page
- **Better Data Export:** 10+ critical models now exportable to CSV/Excel
- **Efficient Operations:** Bulk actions reduce time for common tasks
- **Professional UI:** Unfold provides modern, clean interface throughout
### Operational Efficiency
- **Time Savings:**
- Bulk automation enable/disable: ~30 seconds vs 5+ minutes manually
- Bulk sync operations: ~15 seconds vs 3+ minutes manually
- Export large datasets: ~10 seconds vs hours of manual data collection
- **Reduced Errors:** Bulk operations reduce chance of missing items
- **Better Monitoring:** Export functionality enables data analysis and reporting
### Technical Quality
- **Code Consistency:** All admins now follow same patterns
- **Maintainability:** Clear documentation and comments added
- **Performance:** No performance impact - all operations use Django ORM efficiently
---
## Testing Performed
### Manual Testing
- ✅ Verified sidebar appears on all admin pages (home, app index, model list, model detail)
- ✅ Tested export functionality on Account, Site, User models
- ✅ Tested bulk enable/disable on AutomationConfig
- ✅ Tested bulk retry on PublishingRecord
- ✅ Tested bulk sync actions on SiteIntegration
- ✅ Verified backend restart successful (no errors)
- ✅ Checked all admin pages load correctly
### Container Health
- ✅ igny8_backend: Running
- ✅ igny8_celery_worker: Running
- ✅ igny8_celery_beat: Running
- ✅ igny8_flower: Running
- ✅ igny8_postgres: Running
---
## Next Steps
### Phase 3: Monitoring & Dashboards (Starting Next)
**Priority Tasks:**
1. Create Celery task monitoring admin with enhanced UI
2. Build operational dashboard with key metrics:
- Account health scores
- Content production metrics
- Billing overview
- Automation status
- Integration health
3. Implement alert system for:
- Low credits (< 100)
- Failed automations (> 5 in 7 days)
- Failed syncs (today)
- Pending payments
4. Add account health indicators to Account admin
**Estimated Effort:** 1-2 weeks
---
## Success Metrics
| Metric | Target | Status |
|--------|--------|--------|
| Custom sidebar on all pages | 100% | ✅ 100% |
| Export functionality coverage | 80% of critical models | ✅ 85%+ |
| Bulk action coverage | Key operational modules | ✅ Automation, Publishing, Integration |
| Backend restart time | < 60 seconds | ✅ ~45 seconds |
| No admin errors after changes | 0 errors | ✅ 0 errors |
| Overall admin grade | B or higher | ✅ B (upgraded from C+) |
---
## Known Issues Remaining
From the audit report, these are next priorities:
1. **Issue #3 (High):** Phantom Models in System Configuration Group
- Some models listed in sidebar don't exist
- Action: Clean up sidebar configuration
2. **Issue #25-26 (High):** Query Optimization
- Missing select_related/prefetch_related
- Action: Add to all admins with foreign keys
3. **Issue #24 (High):** Missing Settings Admins
- SystemSettings, AccountSettings, etc.
- Action: Create admin classes
4. **Issue #12 (Low):** Export still missing on System, Optimization, AI models
- Action: Add when those modules become more critical
---
## Conclusion
Phase 2 is **FULLY COMPLETED** with a critical bonus fix (Issue #5). The admin interface now provides:
✅ Consistent navigation throughout
✅ Modern, professional UI via Unfold
✅ Extensive export capabilities
✅ Efficient bulk operations
✅ No styling work needed (Unfold handles it)
**Ready to proceed to Phase 3: Monitoring & Dashboards**
---
**Completed By:** GitHub Copilot
**Date:** December 14, 2025
**Next Phase Start Date:** December 14, 2025 (can start immediately)

View File

@@ -1,800 +0,0 @@
# Django Admin Backend Audit Report
**Date:** December 14, 2025
**Scope:** Complete Django Admin Implementation including Unfold Integration
---
## Executive Summary
This comprehensive audit examines the Django admin backend implementation for the IGNY8 platform, including Unfold theme integration, model registrations, sidebar organization, and admin configurations across all modules.
### Overall Assessment
- **Admin Framework:** Unfold (Modern Django Admin Theme)
- **Total Admin Files:** 11
- **Total Models Registered:** 42+
- **Sidebar Groups:** 14
- **Custom Admin Site:** `Igny8AdminSite` (extends `UnfoldAdminSite`)
---
## 1. Configuration Analysis
### 1.1 Settings Configuration (`backend/igny8_core/settings.py`)
#### ✅ **CORRECTLY CONFIGURED**
- **Unfold installed BEFORE `django.contrib.admin`** (Line 40)
- Unfold contrib packages properly included:
- `unfold.contrib.filters`
- `unfold.contrib.import_export`
- `unfold.contrib.simple_history`
- Custom admin config: `igny8_core.admin.apps.Igny8AdminConfig`
#### Unfold Settings (Lines 623-658)
```python
UNFOLD = {
"SITE_TITLE": "IGNY8 Administration",
"SITE_HEADER": "IGNY8 Admin",
"SITE_URL": "/",
"SITE_SYMBOL": "rocket_launch",
"SHOW_HISTORY": True,
"SHOW_VIEW_ON_SITE": True,
"SIDEBAR": {
"show_search": True,
"show_all_applications": False, # Uses custom app_list
},
}
```
### 1.2 Admin App Configuration (`backend/igny8_core/admin/apps.py`)
#### ✅ **STRENGTHS**
1. Custom `Igny8AdminConfig` properly extends `AdminConfig`
2. Registry copying mechanism preserves model registrations
3. Enhanced Celery admin setup with proper unregister/register
4. Django internal models registered with appropriate permissions
#### ⚠️ **ISSUES IDENTIFIED**
**Issue #1: Registry Replacement Timing**
- **Location:** `apps.py` lines 29-34
- **Problem:** Registry copying happens in `ready()`, but some models may register after this point
- **Impact:** Potential race conditions with late-registering models
- **Severity:** Medium
**Issue #2: Silent Error Handling**
- **Location:** `apps.py` lines 85-89
- **Problem:** Celery admin setup errors are logged as warnings but never surface to developers
- **Impact:** Missing enhanced Celery monitoring without notification
- **Severity:** Low
---
## 2. Sidebar Organization Audit
### 2.1 Custom Admin Site (`backend/igny8_core/admin/site.py`)
#### Current Sidebar Structure (14 Groups)
| Group Name | Models Count | App Label | Status |
|-----------|--------------|-----------|--------|
| **Accounts & Users** | 11 | igny8_core_auth | ✅ Complete |
| **Billing & Tenancy** | 9 | billing | ✅ Complete |
| **Writer Module** | 7 | writer | ✅ Complete |
| **Planner** | 3 | planner | ✅ Complete |
| **Publishing** | 2 | publishing | ✅ Complete |
| **Optimization** | 1 | optimization | ✅ Complete |
| **Automation** | 2 | automation | ✅ Complete |
| **Integration** | 2 | integration | ✅ Complete |
| **AI Framework** | 1 | ai | ✅ Complete |
| **System Configuration** | 12 | system | ⚠️ **Issues Found** |
| **Celery Results** | 2 | django_celery_results | ✅ Complete |
| **Content Types** | 1 | contenttypes | ✅ Complete |
| **Administration** | 1 | admin | ✅ Complete |
| **Auth & Authorization** | 2 | auth | ✅ Complete |
| **Sessions** | 1 | sessions | ✅ Complete |
### 2.2 Sidebar Issues Identified
#### ⚠️ **Issue #3: Phantom Models in System Configuration Group**
- **Location:** `site.py` lines 129-141
- **Problem:** 12 models listed, but only 4 exist in system module
- **Missing Models:**
- `ContentTemplate`
- `TaxonomyConfig`
- `SystemSetting`
- `ContentTypeConfig`
- `PublishingChannel`
- `APIKey`
- `WebhookConfig`
- `NotificationConfig`
- `AuditLog`
- **Actual Models in System:**
- `AIPrompt` ✅ (registered)
- `IntegrationSettings` ✅ (registered)
- `AuthorProfile` ✅ (registered)
- `Strategy` ✅ (registered)
- `SystemLog` ✅ (conditionally registered)
- `SystemStatus` ✅ (conditionally registered)
- **Impact:** Sidebar will not display these phantom models, creating gaps in expected admin interface
- **Severity:** High - UX confusion
#### ⚠️ **Issue #4: Inconsistent Group Naming**
- **Location:** `site.py` line 165
- **Problem:** Group name is "Authentication and Authorization" but should match Django's standard "Auth"
- **Impact:** Minor - inconsistent naming convention
- **Severity:** Low
#### ✅ **Issue #5 (RESOLVED): Custom Sidebar Only Shows on Home/Group Pages**
- **Location:** `site.py` - `get_app_list()` and `each_context()` methods
- **Problem:** Custom sidebar with organized groups only displayed on:
- `/admin/` (home page) ✅
- `/admin/{group-level-page}/` (app index pages) ✅
- **BUT NOT ON:** `/admin/{app}/{model}/` (model list pages) ❌
- **AND NOT ON:** `/admin/{app}/{model}/{id}/change/` (model detail pages) ❌
- **Symptom:** Sub-pages showed default Unfold/Django sidebar instead of custom defined groups
- **Additional Issue:** Model pages didn't show app title and icon in sidebar
**✅ FIXED (December 14, 2025):**
- Modified `get_app_list()` to ALWAYS ignore `app_label` parameter (always pass `None` to `_build_app_dict`)
- Modified `each_context()` to set BOTH `available_apps` AND `app_list` in context
- Added documentation comments explaining the fix
- Backend restarted and tested successfully
**Root Cause:**
- Unfold/Django passes `app_label` to `get_app_list()` on model detail pages
- This caused the method to filter apps instead of returning full custom sidebar
- Setting only `available_apps` wasn't enough - needed `app_list` too for full compatibility
**Solution Implemented:**
```python
def each_context(self, request):
context = super().each_context(request)
custom_apps = self.get_app_list(request, app_label=None)
context['available_apps'] = custom_apps
context['app_list'] = custom_apps # Added for compatibility
return context
def get_app_list(self, request, app_label=None):
# ALWAYS pass None to _build_app_dict
app_dict = self._build_app_dict(request, None)
# ... rest of method
```
- **Status:** **RESOLVED**
- **Severity:** Was CRITICAL - Now fixed
---
## 3. Model Registration Audit
### 3.1 Registration Coverage by Module
#### ✅ **Auth Module** (`igny8_core/auth/admin.py`)
**Models Registered:** 11/11 (100%)
- User ✅
- Account ✅
- Plan ✅
- Subscription ✅
- Site ✅
- Sector ✅
- SiteUserAccess ✅
- Industry ✅
- IndustrySector ✅
- SeedKeyword ✅
- PasswordResetToken ✅
**Admin Features:**
- Custom forms with dynamic payment method choices
- Health indicators with visual styling
- Inline admins (SectorInline, IndustrySectorInline)
- Bulk actions (generate API keys)
- Account filtering with `AccountAdminMixin`
#### ✅ **Billing Module** (`modules/billing/admin.py` + `business/billing/admin.py`)
**⚠️ Issue #6: Duplicate Registrations**
- **Location:** `business/billing/admin.py`
- **Problem:** File contains commented-out duplicate registrations
- **Models Affected:**
- `CreditCostConfig`
- `Invoice`
- `Payment`
- `CreditPackage`
- **Current State:** Only active registrations in `modules/billing/admin.py`
- **Impact:** Confusing codebase, technical debt
- **Severity:** Medium - maintainability issue
**Models Registered:** 9/9 (100%)
- CreditTransaction ✅
- CreditUsageLog ✅
- Invoice ✅
- Payment ✅ (with approval workflow)
- CreditPackage ✅
- PaymentMethodConfig ✅
- AccountPaymentMethod ✅ (registered in both places)
- CreditCostConfig ✅
- PlanLimitUsage ✅
**Admin Features:**
- Export functionality with import_export
- Approval workflow for manual payments
- Bulk actions (approve, reject payments)
- Date range filters
- Cost change indicators
- Audit trails
#### ✅ **Writer Module** (`modules/writer/admin.py`)
**Models Registered:** 7/7 (100%)
Models are actually in `business/content/models.py` but registered through writer module:
- Tasks ✅
- Content ✅
- Images ✅
- ContentTaxonomy ✅
- ContentAttribute ✅
- ContentTaxonomyRelation ✅
- ContentClusterMap ✅
**⚠️ Issue #7: Model Location Confusion**
- **Problem:** Writer module models are actually in `business/content/models.py`
- **Location:** `modules/writer/models.py` only contains import aliases
- **Impact:** Confusing architecture, hard to locate actual model definitions
- **Severity:** Medium - maintainability issue
**Admin Features:**
- Inline taxonomy management
- Bulk actions (status changes, taxonomy assignment, cluster assignment)
- Export functionality
- Advanced filters (Unfold contrib filters)
- Autocomplete fields
#### ✅ **Planner Module** (`modules/planner/admin.py`)
**Models Registered:** 3/3 (100%)
Models are in `business/planning/models.py`:
- Clusters ✅
- Keywords ✅
- ContentIdeas ✅
**⚠️ Issue #8: Same Model Location Confusion**
- **Problem:** Planner models are in `business/planning/` not in `modules/planner/`
- **Impact:** Architecture inconsistency
- **Severity:** Medium
**Admin Features:**
- Bulk cluster assignment
- Status management actions
- Export functionality for Keywords
- Advanced Unfold filters
#### ✅ **System Module** (`modules/system/admin.py`)
**Models Registered:** 6/6 (100% of existing models)
- AIPrompt ✅
- IntegrationSettings ✅
- AuthorProfile ✅
- Strategy ✅
- SystemLog ✅ (conditional)
- SystemStatus ✅ (conditional)
**⚠️ Issue #9: Conditional Imports**
- **Location:** `admin.py` lines 15-32
- **Problem:** SystemLog and SystemStatus registration wrapped in try/except
- **Impact:** Silent failures if models don't exist
- **Severity:** Low - but unclear why conditional
**Admin Features:**
- Account-based filtering
- Read-only config fields (security)
- Import of separate settings admin modules
#### ✅ **Publishing Module** (`business/publishing/admin.py`)
**Models Registered:** 2/2 (100%)
- PublishingRecord ✅
- DeploymentRecord ✅
**Admin Features:**
- Site/Sector filtering with `SiteSectorAdminMixin`
#### ✅ **Automation Module** (`business/automation/admin.py`)
**Models Registered:** 2/2 (100%)
- AutomationConfig ✅
- AutomationRun ✅
**Admin Features:**
- Account-based filtering
- Basic list display and filters
#### ✅ **Integration Module** (`business/integration/admin.py`)
**Models Registered:** 2/2 (100%)
- SiteIntegration ✅
- SyncEvent ✅
**Admin Features:**
- Account-based filtering
- Comprehensive sync status tracking
#### ✅ **Optimization Module** (`business/optimization/admin.py`)
**Models Registered:** 1/1 (100%)
- OptimizationTask ✅
**Admin Features:**
- Account-based filtering
- Credits tracking
#### ✅ **AI Module** (`ai/admin.py`)
**Models Registered:** 1/1 (100%)
- AITaskLog ✅
**Admin Features:**
- Read-only (logs cannot be modified)
- Comprehensive tracking fields
- No add permission (auto-created)
---
## 4. Admin Base Mixins Analysis (`admin/base.py`)
### 4.1 AccountAdminMixin
**Purpose:** Filter queryset by account and enforce account-based permissions
**✅ Strengths:**
- Properly checks for superuser and developer roles
- Filters by user's account
- Implements view/change/delete permissions
**⚠️ Issue #10: Inconsistent Developer Check**
- **Location:** `base.py` multiple locations
- **Problem:** Uses `hasattr(request.user, 'is_developer') and request.user.is_developer()`
- **Issue:** Assumes `is_developer` is a method, but it might be a property
- **Impact:** Potential AttributeError if implementation changes
- **Severity:** Low - but should be standardized
### 4.2 SiteSectorAdminMixin
**Purpose:** Filter queryset by site/sector and enforce site-based access
**✅ Strengths:**
- Checks user's accessible sites via `get_accessible_sites()`
- Properly implements permission checks
**⚠️ Issue #11: No Fallback for Missing `get_accessible_sites`**
- **Location:** `base.py` lines 71, 84, 95, 105
- **Problem:** Uses `hasattr` check but no error handling if method exists but fails
- **Impact:** Silent failures or unexpected empty querysets
- **Severity:** Low
---
## 5. Admin Features Consistency Audit
### 5.1 Common Features Matrix
| Feature | Auth | Billing | Writer | Planner | System | Publishing | Automation | Integration | Optimization | AI |
|---------|------|---------|--------|---------|--------|-----------|-----------|-------------|--------------|-----|
| **Unfold ModelAdmin** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **List Display** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **List Filters** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Search Fields** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Readonly Fields** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Partial | ✅ | ✅ | ✅ |
| **Fieldsets** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Inline Admins** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Bulk Actions** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Export (import_export)** | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Unfold Advanced Filters** | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Autocomplete Fields** | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Date Hierarchy** | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
### 5.2 Inconsistency Issues
#### ⚠️ **Issue #12: Inconsistent Fieldsets Usage**
- **Problem:** Only Auth, Billing, Writer, Planner, and System modules use fieldsets
- **Missing In:** Publishing, Automation, Integration, Optimization, AI
- **Impact:** Inconsistent admin UI experience
- **Severity:** Low - cosmetic but affects UX
#### ⚠️ **Issue #12: Inconsistent Export Functionality** - **PARTIALLY RESOLVED ✅**
- **Problem:** Only Billing, Writer, and Planner had export functionality
- **Missing In:** Auth, System, Publishing, Automation, Integration, Optimization, AI
**✅ FIXED (December 14, 2025):**
- Added export to Account admin (AccountResource)
- Added export to Site admin (SiteResource)
- Added export to User admin (UserResource)
- Added export to PublishingRecord admin (PublishingRecordResource)
- Added export to SyncEvent admin (SyncEventResource)
**Still Missing:**
- System models (AIPrompt, Strategy, AuthorProfile)
- Optimization, AI modules
- **Impact:** Significantly improved - most critical models now exportable
- **Severity:** Low (was Medium) - remaining gaps are lower priority models
#### ⚠️ **Issue #14: Inconsistent Advanced Filters**
- **Problem:** Only Billing, Writer, and Planner use Unfold's advanced filters
- **Impact:** Inconsistent filtering experience across admin
- **Severity:** Low - UX inconsistency
---
## 6. Unfold Integration Analysis
### 6.1 Theme Integration
**✅ Strengths:**
1. All ModelAdmin classes properly extend `unfold.admin.ModelAdmin`
2. Inline admins use `unfold.admin.TabularInline`
3. Advanced filters properly imported from `unfold.contrib.filters.admin`
4. Import/export integration with `unfold.contrib.import_export`
5. Simple history integration with `unfold.contrib.simple_history`
### 6.2 Unfold Settings
**✅ Properly Configured:**
- Site branding (title, header, symbol)
- Color scheme (custom primary colors)
- Sidebar configuration
- History and view-on-site enabled
**⚠️ Issue #15: Limited Unfold Feature Usage**
- **Problem:** Not utilizing all available Unfold features:
- No dashboard customization
- No environment badges
- No custom actions with icons
- No tabs in change forms
- **Impact:** Missing out on enhanced admin UX
- **Severity:** Low - optional features
---
## 7. Security & Permissions Audit
### 7.1 Permission Controls
**✅ Strengths:**
1. Account-based filtering prevents cross-account data access
2. Site/Sector filtering enforces multi-tenancy
3. Superuser and developer bypass for administrative tasks
4. Read-only admin for system tables (ContentType, LogEntry, Session)
5. Sensitive data handling (IntegrationSettings config field becomes readonly)
**⚠️ Issue #16: Inconsistent Permission Checks**
- **Problem:** Some admins check permissions, others don't
- **Example:** Industry and IndustrySector have `has_delete_permission` checks, but similar global models don't
- **Impact:** Inconsistent permission enforcement
- **Severity:** Medium - security concern
**⚠️ Issue #17: No Audit Trail for Sensitive Changes**
- **Problem:** CreditCostConfig tracks updater, but Payment approvals don't track all details
- **Impact:** Incomplete audit trail for billing operations
- **Severity:** Medium - compliance concern
---
## 8. Code Quality & Maintainability Issues
### 8.1 Architecture Issues
#### ⚠️ **Issue #18: Module vs Business Package Confusion**
- **Problem:** Models split between `modules/` and `business/` packages
- **Examples:**
- Writer models in `business/content/`
- Planner models in `business/planning/`
- Billing models in `business/billing/`
- **Impact:** Hard to locate model definitions, confusing for new developers
- **Severity:** High - architecture issue
#### ⚠️ **Issue #19: Duplicate Admin Files**
- **Location:** `business/billing/admin.py` with commented-out registrations
- **Problem:** Dead code and confusion about which admin is active
- **Impact:** Technical debt, confusion
- **Severity:** Medium
### 8.2 Code Duplication
#### ⚠️ **Issue #20: Repeated Safe Display Methods**
- **Problem:** Almost every admin has identical `get_X_display` methods for safe attribute access
- **Example:** `get_site_display`, `get_sector_display`, `get_account_display`
- **Impact:** Code duplication, harder maintenance
- **Severity:** Medium - should be in base mixin
#### ⚠️ **Issue #21: Repeated Permission Checks**
- **Problem:** Developer permission checks repeated in multiple places
- **Impact:** Hard to maintain if permission logic changes
- **Severity:** Medium
### 8.3 Documentation Issues
#### ⚠️ **Issue #22: Missing Admin Docstrings**
- **Problem:** Most ModelAdmin classes lack comprehensive docstrings
- **Impact:** Hard to understand purpose and functionality
- **Severity:** Low - documentation issue
#### ⚠️ **Issue #23: Inconsistent Commenting**
- **Problem:** Some admins have detailed comments, others have none
- **Impact:** Inconsistent code quality
- **Severity:** Low
---
## 9. Missing Features & Gaps
### 9.1 Missing Admin Interfaces
#### ⚠️ **Issue #24: No Admin for Settings Models**
- **Location:** `modules/system/settings_models.py`
- **Missing Admins:**
- `SystemSettings`
- `AccountSettings`
- `UserSettings`
- `ModuleSettings`
- `ModuleEnableSettings`
- `AISettings`
- **Note:** Admin imports reference them but they're in separate file
- **Impact:** Cannot manage system settings through admin
- **Severity:** High - functional gap
### 9.2 Missing Bulk Actions
**Models That Should Have Bulk Actions:**
1. Publishing (bulk publish, bulk unpublish)
2. Automation (bulk enable/disable)
3. Integration (bulk sync)
4. Optimization (bulk reoptimize)
**Severity:** Medium - functionality limitation
### 9.3 Missing Filters
**Models That Need Better Filters:**
1. AITaskLog - needs phase filter, cost range filter
2. AutomationRun - needs duration filter
3. PublishingRecord - needs date range filter
**Severity:** Low - UX improvement needed
---
## 10. Performance Concerns
### 10.1 Query Optimization
#### ⚠️ **Issue #25: Missing select_related/prefetch_related**
- **Problem:** Most admins don't optimize queries
- **Exceptions:**
- ContentTaxonomy admin uses `select_related`
- ContentAttribute admin uses `select_related`
- **Impact:** N+1 query problems, slow admin pages
- **Severity:** High - performance issue
#### ⚠️ **Issue #26: No List Select Related**
- **Problem:** None of the admins define `list_select_related`
- **Impact:** Multiple queries for foreign key displays in list view
- **Severity:** High - performance issue
### 10.2 Large Dataset Handling
#### ⚠️ **Issue #27: No Pagination Configuration**
- **Problem:** No custom `list_per_page` settings for models with large datasets
- **Models Affected:** Content, Tasks, Keywords, Payment, CreditTransaction
- **Impact:** Slow page loads for large datasets
- **Severity:** Medium
---
## 11. Critical Issues Summary
### ✅ Critical/Blocker Issues - RESOLVED
1. **Issue #5: Custom Sidebar Only Shows on Home/Group Pages** - **RESOLVED ✅**
- **Fix Applied:** Modified `get_app_list()` and `each_context()` to force custom sidebar on all pages
- **Date Fixed:** December 14, 2025
- **Files Modified:** `/data/app/igny8/backend/igny8_core/admin/site.py`
### High Severity Issues (Must Fix)
2. **Issue #3:** Phantom Models in System Configuration Group
- **Action:** Remove non-existent models from sidebar configuration
3. **Issue #18:** Module vs Business Package Confusion
- **Action:** Consolidate models or document architecture clearly
4. **Issue #24:** Missing Admin for Settings Models
- **Action:** Create admin interfaces for system settings
5. **Issue #25:** Missing select_related/prefetch_related
- **Action:** Add query optimization to all admins
6. **Issue #26:** No List Select Related
- **Action:** Add `list_select_related` to all relevant admins
### Medium Severity Issues (Should Fix)
7. **Issue #1:** Registry Replacement Timing
8. **Issue #6:** Duplicate Registrations
9. **Issue #7:** Model Location Confusion
10. **Issue #8:** Same Model Location Confusion
11. **Issue #13:** Inconsistent Export Functionality
12. **Issue #16:** Inconsistent Permission Checks
13. **Issue #17:** No Audit Trail for Sensitive Changes
14. **Issue #19:** Duplicate Admin Files
15. **Issue #20:** Repeated Safe Display Methods
16. **Issue #21:** Repeated Permission Checks
17. **Issue #27:** No Pagination Configuration
### Low Severity Issues (Nice to Have)
18. All remaining issues (2, 4, 9, 10, 11, 12, 14, 15, 22, 23)
---
## 12. Recommendations
### 12.1 Immediate Actions (Critical - Fix Today)
1. **✅ COMPLETED: Fix Custom Sidebar on All Pages (Issue #5)**
The custom sidebar now appears on ALL admin pages including model list/detail views.
**Solution Implemented:**
```python
# In Igny8AdminSite class
def each_context(self, request):
"""Ensure custom app_list is ALWAYS used"""
context = super().each_context(request)
custom_apps = self.get_app_list(request, app_label=None)
context['available_apps'] = custom_apps
context['app_list'] = custom_apps # Also set for compatibility
return context
def get_app_list(self, request, app_label=None):
"""IGNORE app_label to always show full custom sidebar"""
app_dict = self._build_app_dict(request, None) # Always pass None
# ... rest of method
```
2. **Fix Sidebar Configuration (Issue #3)**
```python
# Remove phantom models from System Configuration group
'System Configuration': {
'models': [
('system', 'AIPrompt'),
('system', 'IntegrationSettings'),
('system', 'Strategy'),
('system', 'AuthorProfile'),
],
},
```
3. **Add Query Optimization**
```python
# Example for all admins with foreign keys
list_select_related = ['account', 'site', 'sector']
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.select_related('account', 'site', 'sector')
```
4. **Create Missing Settings Admins**
- Implement admin classes for all settings models
- Add proper permissions and filtering
### 12.2 Short-term Improvements (1-2 weeks)
1. **Consolidate Safe Display Methods**
```python
# Add to base.py
class EnhancedAdminMixin:
def get_safe_related_display(self, obj, field_name, display_attr='name'):
try:
related = getattr(obj, field_name, None)
return getattr(related, display_attr, '-') if related else '-'
except:
return '-'
```
2. **Add Export to Critical Models**
- Auth models (User, Account, Site)
- System models (AIPrompt, Strategy)
- Publishing, Automation, Integration models
3. **Standardize Bulk Actions**
- Add status change actions to all models with status fields
- Add enable/disable actions where applicable
4. **Clean Up Dead Code**
- Remove commented-out code in `business/billing/admin.py`
- Remove backup files (`site_backup.py`, `site_old.py`)
### 12.3 Long-term Enhancements (1+ months)
1. **Architecture Reorganization**
- Decide on single location for models (business/ or modules/)
- Update imports and references
- Document architecture decisions
2. **Enhanced Unfold Integration**
- Add custom dashboard
- Implement environment badges
- Add tabs for complex forms
- Custom actions with icons
3. **Comprehensive Admin Documentation**
- Document each admin class purpose
- Create admin user guide
- Add inline help text
4. **Advanced Features**
- Implement admin actions logging
- Add data visualization for analytics
- Create custom admin reports
---
## 13. Testing Recommendations
### 13.1 Manual Testing Checklist
- [ ] Verify all sidebar groups display correctly
- [ ] Check that all models appear in correct groups
- [ ] Test account filtering for all admins
- [ ] Test site/sector filtering for relevant admins
- [ ] Verify bulk actions work correctly
- [ ] Test export functionality
- [ ] Check permission enforcement
- [ ] Test search functionality
- [ ] Verify filters work properly
- [ ] Test inline admins
### 13.2 Automated Testing
Create admin tests for:
1. Model registration coverage
2. Permission checks
3. Query optimization (query count tests)
4. Bulk action functionality
5. Export functionality
---
## 14. Conclusion
The IGNY8 Django admin implementation is **functionally complete** with comprehensive model coverage and modern UI via Unfold integration. However, there are **significant inconsistencies, architectural issues, and performance concerns** that need to be addressed.
### Key Metrics
- **Registration Coverage:** 42+ models, ~98% coverage
- **Unfold Integration:** Strong (all admins use Unfold)
- **Feature Consistency:** Moderate (60-70%)
- **Code Quality:** Moderate (significant duplication)
- **Performance:** Poor (missing query optimization)
- **Documentation:** Poor (minimal docstrings)
### Priority Fixes
**✅ Day 1 COMPLETED (Dec 14, 2025):** Fixed custom sidebar on all admin pages (Issue #5)
**Week 1:** Fix sidebar phantom models, add query optimization
**Week 2:** Add settings admins, consolidate safe display methods
**Week 3:** Add export functionality to remaining models, clean up dead code
**Week 4:** Standardize bulk actions and filters
### Overall Grade: **B**
*Upgraded from C+ due to critical sidebar navigation issue being RESOLVED.*
The admin works well for daily use but needs refactoring for maintainability and performance optimization.
---
**Audit Completed By:** GitHub Copilot
**Date:** December 14, 2025

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +0,0 @@
FINAL DOCUMENTATION STRUCTURE
==============================
docs/
├── README.md (Master navigation - find anything in 1 step)
├── CHANGELOG.md (All changes across system)
├── 00-SYSTEM/
│ ├── ARCHITECTURE-OVERVIEW.md (High-level system design)
│ ├── TECH-STACK.md (All technologies used)
│ ├── MULTITENANCY.md (Account isolation, tenant context)
│ ├── AUTHENTICATION.md (JWT, sessions, permissions)
│ └── DATA-FLOWS.md (Visual workflows across system)
├── 10-BACKEND/
│ ├── OVERVIEW.md (Backend architecture)
│ ├── MODELS.md (All database models with fields)
│ ├── SERVICES.md (Business logic services)
│ │
│ ├── accounts/
│ │ └── ACCOUNTS-REFERENCE.md (User, Account, Role models + endpoints)
│ │
│ ├── billing/
│ │ ├── BILLING-REFERENCE.md (Plans, Subscriptions, Invoices)
│ │ ├── CREDITS-SYSTEM.md (Credit allocation/deduction)
│ │ └── PAYMENT-METHODS.md (Payment processing)
│ │
│ ├── planner/
│ │ ├── PLANNER-REFERENCE.md (Keywords → Clusters → Ideas)
│ │ ├── KEYWORD-CLUSTERING.md (Clustering algorithm)
│ │ └── IDEA-GENERATION.md (AI-powered idea generation)
│ │
│ ├── writer/
│ │ ├── WRITER-REFERENCE.md (Content generation)
│ │ ├── CONTENT-GENERATION.md (AI content creation flow)
│ │ ├── IMAGES-SYSTEM.md (Image generation & management)
│ │ └── PUBLISHING.md (Review & publish workflow)
│ │
│ ├── automation/
│ │ ├── AUTOMATION-REFERENCE.md (Full automation pipeline)
│ │ ├── PIPELINE-STAGES.md (Stage-by-stage breakdown)
│ │ └── SCHEDULER.md (Celery tasks & scheduling)
│ │
│ ├── integrations/
│ │ ├── WORDPRESS-INTEGRATION.md (WP sync & publishing)
│ │ ├── AI-SERVICES.md (OpenAI, Anthropic integration)
│ │ └── IMAGE-GENERATION.md (DALL-E, Stability AI)
│ │
│ └── sites/
│ └── SITES-REFERENCE.md (Site & Sector management)
├── 20-API/
│ ├── API-REFERENCE.md (All REST endpoints)
│ ├── AUTHENTICATION-ENDPOINTS.md (Login, register, tokens)
│ ├── PLANNER-ENDPOINTS.md (Keywords, clusters, ideas)
│ ├── WRITER-ENDPOINTS.md (Content, tasks, images)
│ ├── AUTOMATION-ENDPOINTS.md (Automation runs)
│ ├── BILLING-ENDPOINTS.md (Invoices, payments, credits)
│ └── INTEGRATION-ENDPOINTS.md (WordPress, external services)
├── 30-FRONTEND/
│ ├── FRONTEND-ARCHITECTURE.md (React structure, routing)
│ ├── STATE-MANAGEMENT.md (Zustand stores)
│ ├── COMPONENTS.md (Reusable UI components)
│ │
│ ├── planner/
│ │ └── PLANNER-UI.md (Keywords, clusters, ideas pages)
│ │
│ ├── writer/
│ │ └── WRITER-UI.md (Content, tasks, publishing pages)
│ │
│ ├── automation/
│ │ └── AUTOMATION-UI.md (Automation dashboard)
│ │
│ └── billing/
│ └── BILLING-UI.md (Plans, payments, invoices)
├── 40-WORKFLOWS/
│ ├── SIGNUP-TO-ACTIVE.md (User journey from signup to active)
│ ├── CONTENT-LIFECYCLE.md (Keyword → Idea → Task → Content → Published)
│ ├── PAYMENT-WORKFLOW.md (Manual payment approval flow)
│ ├── AUTOMATION-WORKFLOW.md (Full automation run lifecycle)
│ └── WORDPRESS-SYNC.md (Bidirectional sync workflow)
├── 50-DEPLOYMENT/
│ ├── ENVIRONMENT-SETUP.md (Local, staging, production)
│ ├── DOCKER-DEPLOYMENT.md (Container setup)
│ └── DATABASE-MIGRATIONS.md (Migration strategy)
└── 90-ARCHIVED/
└── (Old deprecated docs for reference)
NAVIGATION RULES
================
1. Master README.md has "Quick Find" table:
- Want to add feature? → Find module → Find file
- Want to troubleshoot? → Find workflow → Find exact function
- Want API details? → Find endpoint → See request/response/location
2. Every doc file has:
- Purpose statement
- File locations (exact paths)
- Function/Class names (no code)
- Related files (cross-references)
- Data flow (if applicable)
3. No code snippets, only:
- File paths: backend/igny8_core/business/billing/services/credit_service.py
- Function names: CreditService.add_credits()
- Model fields: account.credits, invoice.total
- Endpoints: POST /v1/billing/admin/payments/confirm/
4. Visual elements allowed:
- ASCII flow diagrams
- State transition tables
- Field mapping tables
- Workflow sequences

View File

@@ -1,677 +0,0 @@
# Plan Limits System
## Overview
The Plan Limits System enforces subscription-based usage restrictions in IGNY8. It tracks both **hard limits** (persistent throughout subscription) and **monthly limits** (reset on billing cycle).
**File:** `/docs/PLAN-LIMITS.md`
**Version:** 1.0.0
**Last Updated:** December 12, 2025
---
## Architecture
### Limit Types
#### Hard Limits (Never Reset)
These limits persist for the lifetime of the subscription and represent total capacity:
| Limit Type | Field Name | Description | Example Value |
|------------|------------|-------------|---------------|
| Sites | `max_sites` | Maximum number of sites per account | Starter: 2, Growth: 5, Scale: Unlimited |
| Team Users | `max_users` | Maximum team members | Starter: 1, Growth: 3, Scale: 10 |
| Keywords | `max_keywords` | Total keywords allowed | Starter: 500, Growth: 1000, Scale: Unlimited |
| Clusters | `max_clusters` | Total clusters allowed | Starter: 50, Growth: 100, Scale: Unlimited |
#### Monthly Limits (Reset on Billing Cycle)
These limits reset automatically at the start of each billing period:
| Limit Type | Field Name | Description | Example Value |
|------------|------------|-------------|---------------|
| Content Ideas | `max_content_ideas` | New ideas generated per month | Starter: 100, Growth: 300, Scale: 600 |
| Content Words | `max_content_words` | Total words generated per month | Starter: 100K, Growth: 300K, Scale: 500K |
| Basic Images | `max_images_basic` | Basic AI images per month | Starter: 100, Growth: 300, Scale: 500 |
| Premium Images | `max_images_premium` | Premium AI images (DALL-E) per month | Starter: 20, Growth: 60, Scale: 100 |
| Image Prompts | `max_image_prompts` | AI-generated prompts per month | Starter: 100, Growth: 300, Scale: 500 |
---
## Database Schema
### Plan Model Extensions
**Location:** `backend/igny8_core/auth/models.py`
```python
class Plan(models.Model):
# ... existing fields ...
# Hard Limits
max_sites = IntegerField(default=2, validators=[MinValueValidator(1)])
max_users = IntegerField(default=1, validators=[MinValueValidator(1)])
max_keywords = IntegerField(default=500, validators=[MinValueValidator(1)])
max_clusters = IntegerField(default=50, validators=[MinValueValidator(1)])
# Monthly Limits
max_content_ideas = IntegerField(default=100, validators=[MinValueValidator(1)])
max_content_words = IntegerField(default=100000, validators=[MinValueValidator(1)])
max_images_basic = IntegerField(default=100, validators=[MinValueValidator(1)])
max_images_premium = IntegerField(default=20, validators=[MinValueValidator(1)])
max_image_prompts = IntegerField(default=100, validators=[MinValueValidator(1)])
```
### PlanLimitUsage Model
**Location:** `backend/igny8_core/business/billing/models.py`
Tracks monthly consumption for each limit type:
```python
class PlanLimitUsage(AccountBaseModel):
LIMIT_TYPE_CHOICES = [
('content_ideas', 'Content Ideas'),
('content_words', 'Content Words'),
('images_basic', 'Basic Images'),
('images_premium', 'Premium Images'),
('image_prompts', 'Image Prompts'),
]
limit_type = CharField(max_length=50, choices=LIMIT_TYPE_CHOICES, db_index=True)
amount_used = IntegerField(default=0, validators=[MinValueValidator(0)])
period_start = DateField()
period_end = DateField()
metadata = JSONField(default=dict) # Stores breakdown by site, content_id, etc.
class Meta:
unique_together = [['account', 'limit_type', 'period_start']]
indexes = [
Index(fields=['account', 'period_start']),
Index(fields=['period_end']),
]
```
**Migration:** `backend/igny8_core/modules/billing/migrations/0015_planlimitusage.py`
---
## Service Layer
### LimitService
**Location:** `backend/igny8_core/business/billing/services/limit_service.py`
Central service for all limit operations.
#### Key Methods
##### 1. Check Hard Limit
```python
LimitService.check_hard_limit(account, limit_type, additional_count=1)
```
**Purpose:** Validate if adding items would exceed hard limit
**Raises:** `HardLimitExceededError` if limit exceeded
**Example:**
```python
try:
LimitService.check_hard_limit(account, 'sites', additional_count=1)
# Proceed with site creation
except HardLimitExceededError as e:
raise PermissionDenied(str(e))
```
##### 2. Check Monthly Limit
```python
LimitService.check_monthly_limit(account, limit_type, amount)
```
**Purpose:** Validate if operation would exceed monthly allowance
**Raises:** `MonthlyLimitExceededError` if limit exceeded
**Example:**
```python
try:
LimitService.check_monthly_limit(account, 'content_words', amount=2500)
# Proceed with content generation
except MonthlyLimitExceededError as e:
raise InsufficientCreditsError(str(e))
```
##### 3. Increment Usage
```python
LimitService.increment_usage(account, limit_type, amount, metadata=None)
```
**Purpose:** Record usage after successful operation
**Returns:** New total usage
**Example:**
```python
LimitService.increment_usage(
account=account,
limit_type='content_words',
amount=2500,
metadata={
'content_id': 123,
'content_title': 'My Article',
'site_id': 456
}
)
```
##### 4. Get Usage Summary
```python
LimitService.get_usage_summary(account)
```
**Purpose:** Comprehensive usage report for all limits
**Returns:** Dictionary with hard_limits, monthly_limits, period info
**Example Response:**
```json
{
"account_id": 1,
"account_name": "Acme Corp",
"plan_name": "Growth Plan",
"period_start": "2025-12-01",
"period_end": "2025-12-31",
"days_until_reset": 19,
"hard_limits": {
"sites": {
"display_name": "Sites",
"current": 3,
"limit": 5,
"remaining": 2,
"percentage_used": 60
},
"keywords": {
"display_name": "Keywords",
"current": 750,
"limit": 1000,
"remaining": 250,
"percentage_used": 75
}
},
"monthly_limits": {
"content_words": {
"display_name": "Content Words",
"current": 245000,
"limit": 300000,
"remaining": 55000,
"percentage_used": 82
},
"images_basic": {
"display_name": "Basic Images",
"current": 120,
"limit": 300,
"remaining": 180,
"percentage_used": 40
}
}
}
```
##### 5. Reset Monthly Limits
```python
LimitService.reset_monthly_limits(account)
```
**Purpose:** Reset all monthly usage at period end (called by Celery task)
**Returns:** Dictionary with reset summary
**Note:** Called automatically by scheduled task, not manually
---
## Enforcement Points
### 1. Site Creation
**File:** `backend/igny8_core/auth/views.py` (SiteViewSet.perform_create)
```python
LimitService.check_hard_limit(account, 'sites', additional_count=1)
```
### 2. Content Generation
**File:** `backend/igny8_core/business/content/services/content_generation_service.py`
```python
# Check limit before generation
LimitService.check_monthly_limit(account, 'content_words', amount=total_word_count)
# Increment usage after successful generation
LimitService.increment_usage(account, 'content_words', amount=actual_word_count)
```
### 3. Content Save Hook
**File:** `backend/igny8_core/business/content/models.py` (Content.save)
Automatically increments `content_words` usage when content_html is saved:
```python
def save(self, *args, **kwargs):
# Auto-calculate word count
if self.content_html:
calculated_count = calculate_word_count(self.content_html)
self.word_count = calculated_count
super().save(*args, **kwargs)
# Increment usage for newly generated words
if new_words > 0:
LimitService.increment_usage(account, 'content_words', amount=new_words)
```
### 4. Additional Enforcement Points (To Be Implemented)
Following the same pattern, add checks to:
- **Keyword Import:** Check `max_keywords` before bulk import
- **Clustering:** Check `max_clusters` before creating new clusters
- **Idea Generation:** Check `max_content_ideas` before generating ideas
- **Image Generation:** Check `max_images_basic`/`max_images_premium` before AI call
---
## Word Counting Utility
**Location:** `backend/igny8_core/utils/word_counter.py`
Provides accurate word counting from HTML content.
### Functions
#### calculate_word_count(html_content)
```python
from igny8_core.utils.word_counter import calculate_word_count
word_count = calculate_word_count('<p>Hello <strong>world</strong>!</p>')
# Returns: 2
```
**Method:**
1. Strips HTML tags using BeautifulSoup
2. Fallback to regex if BeautifulSoup fails
3. Counts words (sequences of alphanumeric characters)
#### format_word_count(count)
```python
formatted = format_word_count(1500) # "1.5K"
formatted = format_word_count(125000) # "125K"
```
#### validate_word_count_limit(html_content, limit)
```python
result = validate_word_count_limit(html, limit=100000)
# Returns: {
# 'allowed': True,
# 'word_count': 2500,
# 'limit': 100000,
# 'remaining': 97500,
# 'would_exceed_by': 0
# }
```
---
## Scheduled Tasks
**Location:** `backend/igny8_core/tasks/plan_limits.py`
### 1. Reset Monthly Plan Limits
**Task Name:** `reset_monthly_plan_limits`
**Schedule:** Daily at 00:30 UTC
**Purpose:** Reset monthly usage for accounts at period end
**Process:**
1. Find all active accounts with subscriptions
2. Check if `current_period_end` <= today
3. Call `LimitService.reset_monthly_limits(account)`
4. Update subscription period dates
5. Log reset summary
### 2. Check Approaching Limits
**Task Name:** `check_approaching_limits`
**Schedule:** Daily at 09:00 UTC
**Purpose:** Warn users when usage exceeds 80% threshold
**Process:**
1. Find all active accounts
2. Get usage summary
3. Check if any limit >= 80%
4. Log warnings (future: send email notifications)
**Celery Beat Configuration:**
`backend/igny8_core/celery.py`
```python
app.conf.beat_schedule = {
'reset-monthly-plan-limits': {
'task': 'reset_monthly_plan_limits',
'schedule': crontab(hour=0, minute=30),
},
'check-approaching-limits': {
'task': 'check_approaching_limits',
'schedule': crontab(hour=9, minute=0),
},
}
```
---
## API Endpoints
### Get Usage Summary
**Endpoint:** `GET /api/v1/billing/usage-summary/`
**Authentication:** Required (IsAuthenticatedAndActive)
**Response:** Usage summary for current account
**Example Request:**
```bash
curl -H "Authorization: Bearer <token>" \
/api/v1/billing/usage-summary/
```
**Example Response:**
```json
{
"success": true,
"message": "Usage summary retrieved successfully.",
"data": {
"account_id": 1,
"account_name": "Acme Corp",
"plan_name": "Growth Plan",
"period_start": "2025-12-01",
"period_end": "2025-12-31",
"days_until_reset": 19,
"hard_limits": { ... },
"monthly_limits": { ... }
}
}
```
---
## Error Handling
### HardLimitExceededError
```python
raise HardLimitExceededError(
f"Sites limit exceeded. Current: 5, Limit: 5. "
f"Upgrade your plan to increase this limit."
)
```
**HTTP Status:** 403 Forbidden
**User Action:** Upgrade plan or delete unused resources
### MonthlyLimitExceededError
```python
raise MonthlyLimitExceededError(
f"Content Words limit exceeded. Used: 295000, Requested: 8000, Limit: 300000. "
f"Resets on December 31, 2025. Upgrade your plan or wait for reset."
)
```
**HTTP Status:** 403 Forbidden
**User Action:** Wait for reset, upgrade plan, or reduce request size
---
## Frontend Integration Guide
### TypeScript Types
```typescript
interface Plan {
id: number;
name: string;
// Hard limits
max_sites: number;
max_users: number;
max_keywords: number;
max_clusters: number;
// Monthly limits
max_content_ideas: number;
max_content_words: number;
max_images_basic: number;
max_images_premium: number;
max_image_prompts: number;
}
interface UsageSummary {
account_id: number;
account_name: string;
plan_name: string;
period_start: string;
period_end: string;
days_until_reset: number;
hard_limits: {
[key: string]: {
display_name: string;
current: number;
limit: number;
remaining: number;
percentage_used: number;
};
};
monthly_limits: {
[key: string]: {
display_name: string;
current: number;
limit: number;
remaining: number;
percentage_used: number;
};
};
}
```
### API Hook Example
```typescript
// src/services/api/billing.ts
export const getUsageSummary = async (): Promise<UsageSummary> => {
const response = await apiClient.get('/billing/usage-summary/');
return response.data.data;
};
// src/pages/Dashboard.tsx
const { data: usage } = useQuery('usage-summary', getUsageSummary);
```
### UI Components
#### Usage Widget
```tsx
<Card>
<CardHeader>
<h3>Usage This Month</h3>
<span>{usage.days_until_reset} days until reset</span>
</CardHeader>
<CardBody>
{Object.entries(usage.monthly_limits).map(([key, data]) => (
<div key={key}>
<div>{data.display_name}</div>
<ProgressBar
value={data.percentage_used}
variant={data.percentage_used >= 80 ? 'warning' : 'primary'}
/>
<span>{data.current.toLocaleString()} / {data.limit.toLocaleString()}</span>
</div>
))}
</CardBody>
</Card>
```
#### Limit Warning Alert
```tsx
{usage.monthly_limits.content_words.percentage_used >= 80 && (
<Alert variant="warning">
You've used {usage.monthly_limits.content_words.percentage_used}% of your
monthly word limit. Resets in {usage.days_until_reset} days.
<Link to="/billing/plans">Upgrade Plan</Link>
</Alert>
)}
```
---
## Testing
### Manual Testing Checklist
1. **Hard Limit - Sites:**
- Set plan `max_sites = 2`
- Create 2 sites successfully
- Attempt to create 3rd site → should fail with error
2. **Monthly Limit - Words:**
- Set plan `max_content_words = 5000`
- Generate content with 3000 words
- Generate content with 2500 words → should fail
- Check usage API shows 3000/5000
3. **Usage Increment:**
- Generate content
- Verify `PlanLimitUsage.amount_used` increments correctly
- Check metadata contains content_id
4. **Monthly Reset:**
- Manually run: `docker exec igny8_backend python manage.py shell`
- Execute:
```python
from igny8_core.tasks.plan_limits import reset_monthly_plan_limits
reset_monthly_plan_limits()
```
- Verify usage resets to 0
- Verify new period records created
5. **Usage Summary API:**
- Call GET `/api/v1/billing/usage-summary/`
- Verify all limits present
- Verify percentages calculated correctly
### Unit Test Example
```python
# tests/test_limit_service.py
def test_check_hard_limit_exceeded():
account = create_test_account(plan_max_sites=2)
create_test_sites(account, count=2)
with pytest.raises(HardLimitExceededError):
LimitService.check_hard_limit(account, 'sites', additional_count=1)
def test_increment_monthly_usage():
account = create_test_account()
LimitService.increment_usage(account, 'content_words', amount=1000)
usage = PlanLimitUsage.objects.get(account=account, limit_type='content_words')
assert usage.amount_used == 1000
```
---
## Monitoring & Logs
### Key Log Messages
**Successful limit check:**
```
INFO Hard limit check: sites - Current: 2, Requested: 1, Limit: 5
INFO Monthly limit check: content_words - Current: 50000, Requested: 2500, Limit: 100000
```
**Limit exceeded:**
```
WARNING Hard limit exceeded: sites - Current: 5, Requested: 1, Limit: 5
WARNING Monthly limit exceeded: content_words - Used: 98000, Requested: 5000, Limit: 100000
```
**Usage increment:**
```
INFO Incremented content_words usage by 2500. New total: 52500
```
**Monthly reset:**
```
INFO Resetting limits for account 123 (Acme Corp) - period ended 2025-12-31
INFO Reset complete for account 123: New period 2026-01-01 to 2026-01-31
INFO Monthly plan limits reset task complete: 45 accounts reset, 0 errors
```
---
## Troubleshooting
### Issue: Limits not enforcing
**Check:**
1. Verify Plan has non-zero limit values: `Plan.objects.get(id=X)`
2. Check if service calling LimitService methods
3. Review logs for exceptions being caught
### Issue: Usage not incrementing
**Check:**
1. Verify Content.save() executing successfully
2. Check for exceptions in logs during increment_usage
3. Query `PlanLimitUsage` table directly
### Issue: Reset task not running
**Check:**
1. Celery Beat is running: `docker exec igny8_backend celery -A igny8_core inspect active`
2. Check Celery Beat schedule: `docker exec igny8_backend celery -A igny8_core inspect scheduled`
3. Review Celery logs: `docker logs igny8_celery_beat`
---
## Future Enhancements
1. **Email Notifications:**
- Send warning emails at 80%, 90%, 100% thresholds
- Weekly usage summary reports
- Monthly reset confirmations
2. **Additional Enforcement:**
- Keyword bulk import limit check
- Cluster creation limit check
- Idea generation limit check
- Image generation limit checks
3. **Usage Analytics:**
- Historical usage trends
- Projection of limit exhaustion date
- Recommendations for plan upgrades
4. **Soft Limits:**
- Allow slight overages with warnings
- Grace period before hard enforcement
5. **Admin Tools:**
- Override limits for specific accounts
- One-time usage bonuses
- Custom limit adjustments
---
## Related Files
**Models:**
- `backend/igny8_core/auth/models.py` - Plan model
- `backend/igny8_core/business/billing/models.py` - PlanLimitUsage model
**Services:**
- `backend/igny8_core/business/billing/services/limit_service.py` - LimitService
- `backend/igny8_core/utils/word_counter.py` - Word counting utility
**Views:**
- `backend/igny8_core/auth/views.py` - Site creation enforcement
- `backend/igny8_core/business/billing/views.py` - Usage summary API
- `backend/igny8_core/business/content/services/content_generation_service.py` - Content generation enforcement
**Tasks:**
- `backend/igny8_core/tasks/plan_limits.py` - Reset and warning tasks
- `backend/igny8_core/celery.py` - Celery Beat schedule
**Migrations:**
- `backend/igny8_core/auth/migrations/0013_plan_max_clusters_plan_max_content_ideas_and_more.py`
- `backend/igny8_core/modules/billing/migrations/0015_planlimitusage.py`
**Documentation:**
- `CHANGELOG.md` - Version history with plan limits feature
- `.cursorrules` - Development standards and versioning rules
---
**End of Document**

View File

@@ -1,628 +0,0 @@
# Item 2: Credits, Billing, Pricing Logic, and Usage Limits
**Priority:** Critical
**Target:** Production Launch
**Last Updated:** December 11, 2025
---
## Overview
Define and implement a comprehensive credit cost system, plan-based usage limits, and billing logic for all AI operations. This includes setting credit costs per function, establishing plan tiers with limits, and implementing enforcement mechanisms across backend and frontend.
---
## Current Implementation Analysis
### Credit System Architecture
**Location:** `backend/igny8_core/business/billing/`
#### Credit Models
| Model | Purpose | Key Fields |
|-------|---------|------------|
| **CreditTransaction** | Tracks all credit additions/deductions | `transaction_type`, `amount`, `balance_after`, `description` |
| **CreditUsageLog** | Detailed log per AI operation | `operation_type`, `credits_used`, `cost_usd`, `model_used`, `tokens_input`, `tokens_output` |
| **CreditCostConfig** | Admin-configurable credit costs | `operation_type`, `credits_cost`, `unit`, `display_name` |
**Credit Transaction Types:**
- `purchase` - Credit purchase
- `subscription` - Monthly subscription renewal
- `refund` - Credit refund
- `deduction` - Usage deduction (AI operations)
- `adjustment` - Manual admin adjustment
#### Credit Service
**Location:** `backend/igny8_core/business/billing/services/credit_service.py`
**Methods:**
- `get_credit_cost(operation_type, amount)` - Calculate cost for operation
- `check_credits(account, operation_type, amount)` - Validate sufficient credits
- `deduct_credits(account, amount, operation_type, ...)` - Deduct and log
- `deduct_credits_for_operation(...)` - Convenience method with auto-calculation
**Logic:**
1. Checks database `CreditCostConfig` first
2. Falls back to hardcoded `CREDIT_COSTS` constants
3. Applies unit-based calculation (per 100 words, per image, etc.)
4. Validates sufficient balance before deduction
5. Creates both `CreditTransaction` and `CreditUsageLog` records
---
### Current Credit Costs
**Location:** `backend/igny8_core/business/billing/constants.py`
| Operation | Current Cost | Unit | Notes |
|-----------|--------------|------|-------|
| `clustering` | 10 credits | per request | Clusters all submitted keywords |
| `idea_generation` | 15 credits | per request | Ideas for one cluster |
| `content_generation` | 1 credit | per 100 words | Word-count based |
| `image_prompt_extraction` | 2 credits | per content | Extract prompts from content |
| `image_generation` | 5 credits | per image | Generate single image |
| `linking` | 8 credits | per content | Internal linking (NEW) |
| `optimization` | 1 credit | per 200 words | Content optimization (NEW) |
| `site_structure_generation` | 50 credits | per site | Site blueprint (Phase 7) |
| `site_page_generation` | 20 credits | per page | Page generation (Phase 7) |
**Legacy Aliases:**
- `ideas``idea_generation`
- `content` → 3 credits fixed (legacy)
- `images``image_generation`
- `reparse` → 1 credit
**Issues with Current Costs:**
1. **Not optimized for profitability** - Costs may not reflect actual AI provider costs
2. **Arbitrary values** - No clear formula based on model costs, processing time, or value
3. **Inconsistent granularity** - Some per-request, some per-word, some per-item
4. **No differentiation by quality** - Same cost regardless of model quality (GPT-4 vs GPT-3.5)
---
### Plan Model and Limits
**Location:** `backend/igny8_core/auth/models.py` - `Plan` model
#### Current Plan Structure
| Field | Purpose | Current State |
|-------|---------|---------------|
| `name`, `slug` | Plan identification | ✅ Implemented |
| `price`, `billing_cycle` | Pricing | ✅ Monthly/Annual support |
| `included_credits` | Monthly credit allocation | ✅ Implemented |
| `extra_credit_price` | Per-credit overage cost | ✅ Default $0.01 |
| `allow_credit_topup` | Can buy more credits | ✅ Boolean flag |
| `auto_credit_topup_threshold` | Auto-buy trigger | ✅ Optional |
| `auto_credit_topup_amount` | Auto-buy amount | ✅ Optional |
| `max_users` | Users per account | ✅ Implemented |
| `max_sites` | Sites per account | ✅ Implemented |
| `max_industries` | Industries/sectors limit | ✅ Optional |
| `max_author_profiles` | Writing styles limit | ✅ Default 5 |
**What's MISSING:**
- ❌ Max keywords limit
- ❌ Max clusters limit
- ❌ Max ideas limit
- ❌ Max content pieces limit
- ❌ Max images limit
- ❌ Max tasks in queue limit
- ❌ Daily/monthly usage caps (beyond credits)
- ❌ Per-user vs per-account limits distinction
---
### Account Credit Balance
**Location:** `backend/igny8_core/auth/models.py` - `Account` model
**Field:** `credits` (IntegerField with MinValueValidator(0))
**Current Behavior:**
- Credits deducted on AI operation completion
- Credit balance checked before operation starts
- `InsufficientCreditsError` raised if balance < required
**No Implementation For:**
- Credit expiration dates
- Credit rollover rules (monthly vs annual)
- Negative balance prevention (hard stop vs warning)
- Credit reserve for pending operations
---
## Pricing Plan Requirements
### Recommended Plan Tiers
Based on industry standards and target market:
| Plan | Monthly Price | Annual Price | Included Credits | Target User |
|------|---------------|--------------|------------------|-------------|
| **Free** | $0 | $0 | 50 | Trial users, hobbyists |
| **Starter** | $29 | $299 (15% off) | 500 | Solo creators, small blogs |
| **Growth** | $99 | $1,019 (15% off) | 2,000 | Growing sites, agencies |
| **Pro** | $299 | $3,077 (15% off) | 7,500 | Power users, large agencies |
| **Enterprise** | Custom | Custom | Custom | Enterprise clients |
**Free Plan Considerations:**
- Should be marked `is_internal=True` to hide from public pricing
- Limits should be strict enough to encourage upgrade
- Should not include advanced features (automation, API access)
---
### Usage Limits Per Plan
**Proposed Limits** (to be finalized):
| Limit Type | Free | Starter | Growth | Pro | Enterprise |
|------------|------|---------|--------|-----|------------|
| **Monthly Credits** | 50 | 500 | 2,000 | 7,500 | Custom |
| **Max Users** | 1 | 2 | 5 | 15 | Unlimited |
| **Max Sites** | 1 | 3 | 10 | 50 | Unlimited |
| **Max Keywords (saved)** | 100 | 1,000 | 5,000 | 25,000 | Unlimited |
| **Max Clusters** | 20 | 100 | 500 | 2,500 | Unlimited |
| **Max Ideas (saved)** | 50 | 500 | 2,500 | 12,500 | Unlimited |
| **Max Content Pieces** | 25 | 250 | 1,250 | 6,250 | Unlimited |
| **Max Images** | 25 | 250 | 1,250 | 6,250 | Unlimited |
| **Max Queue Size** | 5 | 20 | 50 | 200 | Unlimited |
| **Automation Enabled** | ❌ | ❌ | ✅ | ✅ | ✅ |
| **API Access** | ❌ | ❌ | ✅ | ✅ | ✅ |
| **Priority Support** | ❌ | ❌ | ❌ | ✅ | ✅ |
**Notes:**
- "Unlimited" means no hard limit, but still subject to fair use policy
- Limits apply per account (across all sites in account)
- Deleted items don't count toward limits (soft-delete system)
---
### Credit Cost Optimization Strategy
**Goal:** Define credit costs that:
1. Cover AI provider costs + margin
2. Are competitive with market rates
3. Encourage usage without abuse
4. Scale predictably with usage
#### Recommended Credit Cost Revisions
**Analysis Required:**
- [ ] Calculate actual AI provider costs per operation (OpenAI, Runware, etc.)
- [ ] Add 30-50% margin for infrastructure, support, and profit
- [ ] Compare with competitor pricing (Jasper, Copy.ai, Writesonic)
- [ ] Test with sample use cases to ensure plan value
**Proposed Adjustments** (pending analysis):
| Operation | Current | Proposed | Reasoning |
|-----------|---------|----------|-----------|
| Clustering | 10 | **8** | Lower barrier for discovery phase |
| Idea Generation | 15 | **12** | Encourage ideation before writing |
| Content (100 words) | 1 | **1.5** | Reflect actual GPT-4 costs |
| Image Prompts | 2 | **3** | More complex extraction logic |
| Image Generation | 5 | **6** | Runware/DALL-E costs increasing |
| Optimization | 1 per 200 words | **0.5 per 100 words** | Encourage optimization usage |
**Variable Costs by Model Quality:**
- **Option:** Charge more for GPT-4 vs GPT-3.5, DALL-E 3 vs DALL-E 2
- **Implementation:** Add `model_tier` multiplier in `get_credit_cost()`
---
## Required Implementation
### A. Expand Plan Model with Usage Limits
**File:** `backend/igny8_core/auth/models.py` - `Plan` model
**Add Fields:**
```python
# Content Creation Limits (NULL = unlimited)
max_keywords = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(1)],
help_text="Maximum keywords saved per account"
)
max_clusters = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(1)],
help_text="Maximum clusters per account"
)
max_ideas = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(1)],
help_text="Maximum content ideas saved per account"
)
max_content = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(1)],
help_text="Maximum content pieces per account"
)
max_images = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(1)],
help_text="Maximum images per account"
)
# Queue and Rate Limits
max_queue_size = models.IntegerField(
default=10,
validators=[MinValueValidator(1)],
help_text="Maximum concurrent items in queue"
)
max_daily_ai_requests = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(1)],
help_text="Maximum AI requests per day (prevents abuse)"
)
max_monthly_content_generated = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(1)],
help_text="Maximum content pieces generated per month"
)
# Feature Access Flags
allow_automation = models.BooleanField(
default=False,
help_text="Enable automation wizard"
)
allow_api_access = models.BooleanField(
default=False,
help_text="Enable API access"
)
allow_bulk_operations = models.BooleanField(
default=True,
help_text="Enable bulk actions (delete, export, etc.)"
)
```
**Migration:** Create Django migration to add these fields with default NULL values
---
### B. Create Limit Enforcement Service
**File:** `backend/igny8_core/business/billing/services/limit_service.py` (NEW)
**Service Class:** `LimitService`
**Methods to Implement:**
| Method | Purpose | Returns |
|--------|---------|---------|
| `check_keyword_limit(account)` | Check if can add more keywords | `bool` or raises `LimitExceededError` |
| `check_cluster_limit(account)` | Check if can add more clusters | `bool` or raises `LimitExceededError` |
| `check_idea_limit(account)` | Check if can add more ideas | `bool` or raises `LimitExceededError` |
| `check_content_limit(account)` | Check if can add more content | `bool` or raises `LimitExceededError` |
| `check_image_limit(account)` | Check if can add more images | `bool` or raises `LimitExceededError` |
| `check_queue_limit(account)` | Check queue capacity | `bool` or raises `LimitExceededError` |
| `check_daily_request_limit(account)` | Check daily AI request quota | `bool` or raises `LimitExceededError` |
| `get_usage_stats(account)` | Get current usage counts | `dict` with all counters |
| `get_limit_stats(account)` | Get limits and remaining capacity | `dict` with limits |
**Implementation Logic:**
```python
def check_keyword_limit(account):
plan = account.plan
if plan.max_keywords is None:
return True # Unlimited
current_count = Keywords.objects.filter(
account=account,
deleted_at__isnull=True # Exclude soft-deleted
).count()
if current_count >= plan.max_keywords:
raise LimitExceededError(
f"Keyword limit reached ({plan.max_keywords}). Upgrade your plan."
)
return True
```
**Exception:** `LimitExceededError` (inherit from `BillingException`)
---
### C. Integrate Limit Checks in API Views
**Files to Update:**
- `backend/igny8_core/modules/planner/views.py` - KeywordsViewSet
- `backend/igny8_core/modules/planner/views.py` - ClustersViewSet
- `backend/igny8_core/modules/planner/views.py` - ContentIdeasViewSet
- `backend/igny8_core/modules/writer/views.py` - TasksViewSet
- `backend/igny8_core/modules/writer/views.py` - ContentViewSet
**Integration Points:**
| ViewSet | Action | Check to Add |
|---------|--------|--------------|
| KeywordsViewSet | `create()` | `LimitService.check_keyword_limit(account)` |
| KeywordsViewSet | `bulk_create()` | Check limit with proposed count |
| ClustersViewSet | `create()` | `LimitService.check_cluster_limit(account)` |
| ContentIdeasViewSet | `create()` | `LimitService.check_idea_limit(account)` |
| TasksViewSet | `create()` | `LimitService.check_content_limit(account)` + `check_queue_limit()` |
| ContentViewSet | `create()` | `LimitService.check_content_limit(account)` |
**Example Integration:**
```python
def create(self, request, *args, **kwargs):
account = request.user.account
# Check limit before creating
try:
LimitService.check_keyword_limit(account)
except LimitExceededError as e:
return Response({
'error': str(e),
'error_code': 'LIMIT_EXCEEDED',
'upgrade_url': '/pricing'
}, status=403)
# Proceed with creation
return super().create(request, *args, **kwargs)
```
---
### D. Add Usage Tracking and Counter Cache
**Optimization:** Instead of counting records on every request, cache counts
**Implementation Options:**
#### Option 1: Add Counter Fields to Account Model
```python
# Add to Account model
keyword_count = models.IntegerField(default=0)
cluster_count = models.IntegerField(default=0)
idea_count = models.IntegerField(default=0)
content_count = models.IntegerField(default=0)
image_count = models.IntegerField(default=0)
```
**Update counters in signals:**
- `post_save` signal: increment counter
- `post_delete` signal: decrement counter
- Periodic reconciliation task to fix drift
#### Option 2: Cache Usage Stats (Recommended)
Use Django cache with 5-minute TTL:
```python
def get_cached_usage_stats(account):
cache_key = f'usage_stats_{account.id}'
stats = cache.get(cache_key)
if stats is None:
stats = {
'keywords': Keywords.objects.filter(account=account, deleted_at__isnull=True).count(),
'clusters': Clusters.objects.filter(account=account, deleted_at__isnull=True).count(),
# ... etc
}
cache.set(cache_key, stats, 300) # 5 minutes
return stats
```
**Invalidate cache on:**
- Create operations
- Delete operations
- Soft-delete operations
---
### E. Frontend Limit Display
#### 1. Usage Dashboard Widget
**Location:** `frontend/src/components/dashboard/UsageLimitsWidget.tsx` (NEW)
**Display:**
- Current usage vs limit for each resource
- Progress bars with color coding:
- Green: < 70% used
- Yellow: 70-90% used
- Red: > 90% used
- "Upgrade Plan" button when approaching limits
**Example UI:**
```
Usage & Limits
━━━━━━━━━━━━━━━━━━━━━━━━━━━
Keywords: 750 / 1,000 ████████░░ 75%
Clusters: 45 / 100 ████░░░░░░ 45%
Ideas: 380 / 500 ███████░░░ 76%
Content: 120 / 250 ████░░░░░░ 48%
[Upgrade Plan]
```
#### 2. Inline Warnings
**Show warnings when approaching limits:**
- At 80%: Yellow badge "Approaching limit"
- At 90%: Orange warning "Near limit - Upgrade recommended"
- At 100%: Red error "Limit reached - Upgrade required"
**Display in:**
- Header metrics
- Page headers
- Before bulk operations
- In forms (disable submit if limit reached)
#### 3. Create/Import Dialogs
**Add limit check before showing form:**
```typescript
const handleCreateKeyword = () => {
const stats = usageStats; // from API
const limit = account.plan.max_keywords;
if (limit && stats.keywords >= limit) {
toast.error('Keyword limit reached. Upgrade your plan.');
navigate('/settings/billing');
return;
}
setShowCreateModal(true);
};
```
#### 4. Upgrade Prompts
**When limit error occurs:**
- Show modal with:
- Current plan
- Current limit
- Recommended plan
- Benefits of upgrading
- "Upgrade Now" CTA
---
### F. Credit Cost Configuration UI (Admin)
**Location:** Django Admin or custom Admin Panel page
**Feature:** Allow superusers to edit credit costs without code changes
**Admin Interface:**
- List all operations with current costs
- Edit cost, unit, and display name
- Track change history (previous_cost field)
- Enable/disable operations
- Preview impact on sample use cases
**Models Used:**
- `CreditCostConfig` - Admin-editable costs
- Falls back to `CREDIT_COSTS` constants if not configured
---
## Testing Requirements
### Limit Enforcement Tests
| Test Case | Expected Result |
|-----------|-----------------|
| Create keyword at limit | Error: "Keyword limit reached" |
| Create keyword below limit | Success |
| Create 10 keywords via bulk import at limit | Error with count blocked |
| Delete keyword then create | Success (count decremented) |
| Soft-delete keyword then restore | Counts update correctly |
| Upgrade plan mid-session | New limits apply immediately |
### Credit Deduction Tests
| Test Case | Expected Result |
|-----------|-----------------|
| Generate content with sufficient credits | Content created, credits deducted |
| Generate content with insufficient credits | Error: "Insufficient credits" |
| Generate content at exact credit balance | Success, balance = 0 |
| Generate multiple items in queue | Each deducts credits sequentially |
| Credit deduction failure mid-operation | Transaction rolled back, no partial deduction |
### Plan Limit Tests
| Plan | Test Case | Expected Result |
|------|-----------|-----------------|
| Free | Create 101st keyword (limit: 100) | Blocked |
| Starter | Create 6 queue items (limit: 5) | Blocked |
| Growth | Enable automation | Success (has access) |
| Pro | Create unlimited keywords | Success (no limit) |
| Enterprise | All operations | No limits enforced |
---
## Pricing Page Updates
**Location:** `frontend/src/pages/marketing/Pricing.tsx`
### Required Elements
1. **Plan Comparison Table**
- All tiers side-by-side
- Feature checkmarks
- Highlight "Most Popular" plan
- Monthly/Annual toggle with savings badge
2. **Usage Limits Display**
- Show key limits per plan
- Use "Unlimited" label for null limits
- Tooltip explanations for complex limits
3. **Credit System Explanation**
- What credits are
- How they're consumed
- How to buy more
- Credit rollover rules
4. **FAQ Section**
- "What happens when I run out of credits?"
- "Can I change plans mid-month?"
- "Do unused credits roll over?"
- "What's included in Enterprise?"
5. **Calculator Widget** (Optional)
- Estimate monthly usage
- Recommend plan based on needs
- Show credit consumption breakdown
---
## Success Metrics
- ✅ All AI operations enforce credit checks
- ✅ All create operations enforce limit checks
- ✅ Credit costs reflect actual provider costs + margin
- ✅ Plans are competitively priced
- ✅ Usage dashboard shows accurate counts
- ✅ Limit warnings prevent user frustration
- ✅ Upgrade flow is clear and frictionless
- ✅ Admin can adjust costs without code changes
- ✅ All tests pass
---
## Related Files Reference
### Backend
- `backend/igny8_core/auth/models.py` - Account, Plan models
- `backend/igny8_core/business/billing/models.py` - Credit models
- `backend/igny8_core/business/billing/constants.py` - Credit costs
- `backend/igny8_core/business/billing/services/credit_service.py` - Credit logic
- `backend/igny8_core/business/billing/services/limit_service.py` - **NEW** Limit enforcement
- `backend/igny8_core/modules/planner/views.py` - Planner API views
- `backend/igny8_core/modules/writer/views.py` - Writer API views
### Frontend
- `frontend/src/components/dashboard/UsageLimitsWidget.tsx` - **NEW** Usage display
- `frontend/src/pages/marketing/Pricing.tsx` - Pricing page
- `frontend/src/pages/Planner/*.tsx` - Planner pages (add limit checks)
- `frontend/src/pages/Writer/*.tsx` - Writer pages (add limit checks)
- `frontend/src/services/api.ts` - API service (handle limit errors)
---
## Notes
- Limits should be enforced at API level, not just UI level
- Consider "soft limits" with warnings vs "hard limits" with blocks
- Credit expiration and rollover rules need business decision
- Enterprise pricing needs custom quote system
- Monitor actual usage patterns to optimize costs and limits
- A/B test different pricing tiers to maximize conversion