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

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

@@ -0,0 +1,281 @@
# Django Admin Implementation Status
**Last Updated:** December 15, 2025
---
## Phase Completion Status
### ✅ Phase 0: Foundation - COMPLETED (Dec 14, 2025)
**What was completed:**
- [x] Installed django-unfold==0.73.1
- [x] Configured UNFOLD settings in settings.py
- [x] Updated all admin classes to inherit from Unfold ModelAdmin
- [x] Fixed admin site inheritance (Igny8AdminSite extends UnfoldAdminSite)
- [x] Fixed UserAdmin popup styling (multiple inheritance: BaseUserAdmin + ModelAdmin)
- [x] Updated Celery admin to use Unfold filters
- [x] Configured middleware (simple_history)
- [x] Rebuilt Docker images
- [x] All containers running healthy
**Result:** Single clean Unfold admin system with no conflicts. All styling handled by Unfold automatically.
---
### ✅ Phase 1: Configuration Cleanup - COMPLETED (Dec 14, 2025)
**Completed:**
- [x] UserAdmin popup styling fix
- [x] Verified no unused site_building models in site.py (already clean)
- [x] Verified duplicate registrations already commented out in business/billing/admin.py
- [x] Added PlanLimitUsage admin registration
- [x] PlanLimitUsage already in sidebar (Billing & Tenancy group)
- [x] All model links verified working
**Files modified:**
- `/data/app/igny8/backend/igny8_core/modules/billing/admin.py` - Added PlanLimitUsageAdmin
**Result:** Admin configuration is clean and organized. All models properly registered and grouped.
---
### ✅ Phase 2: Bulk Operations & Export - COMPLETED (Dec 14, 2025)
**Completed:**
- [x] Added ExportMixin to Keywords admin (already had export)
- [x] Created KeywordsResource class for export configuration
- [x] Added Unfold advanced filters to Tasks admin (ChoicesDropdownFilter, RelatedDropdownFilter, RangeDateFilter)
- [x] Added Unfold advanced filters to Content admin (RangeNumericFilter for word_count, enhanced search)
- [x] Added Unfold advanced filters to Keywords admin (RangeNumericFilter for volume/difficulty, full filter set)
- [x] Added Unfold advanced filters to Clusters admin (RangeNumericFilter, RangeDateFilter)
- [x] Added Unfold advanced filters to ContentIdeas admin (comprehensive filter set)
- [x] Verified existing bulk actions (Tasks: status changes, Content: status changes, Keywords: cluster assignment)
- [x] **NEW:** Added export to Account admin with AccountResource
- [x] **NEW:** Added export to Site admin with SiteResource
- [x] **NEW:** Added export to User admin with UserResource
- [x] **NEW:** Added bulk enable/disable actions to AutomationConfig admin
- [x] **NEW:** Added export to PublishingRecord admin with PublishingRecordResource
- [x] **NEW:** Added bulk retry_failed action to PublishingRecord admin
- [x] **NEW:** Added export to SyncEvent admin with SyncEventResource
- [x] **NEW:** Added bulk sync actions to SiteIntegration admin (enable/disable/trigger sync)
- [x] **CRITICAL FIX:** Fixed Issue #5 - Custom sidebar now appears on ALL admin pages (not just home/group pages)
- [x] Backend restarted successfully
**Files modified:**
- `/data/app/igny8/backend/igny8_core/modules/writer/admin.py` - Added Unfold filters to TasksAdmin and ContentAdmin
- `/data/app/igny8/backend/igny8_core/modules/planner/admin.py` - Added Unfold filters to KeywordsAdmin, ClustersAdmin, ContentIdeasAdmin
- `/data/app/igny8/backend/igny8_core/auth/admin.py` - Added export to Account, Site, User admins
- `/data/app/igny8/backend/igny8_core/business/automation/admin.py` - Added bulk enable/disable actions
- `/data/app/igny8/backend/igny8_core/business/publishing/admin.py` - Added export and bulk retry action
- `/data/app/igny8/backend/igny8_core/business/integration/admin.py` - Added export and bulk sync actions
- `/data/app/igny8/backend/igny8_core/admin/site.py` - **CRITICAL:** Fixed sidebar to appear on all pages
**Critical Bug Fixed:**
- **Issue #5:** Custom sidebar with organized groups now displays consistently on:
-`/admin/` (home page)
-`/admin/{group-level-page}/` (app index pages)
-`/admin/{app}/{model}/` (model list pages) - **NOW FIXED**
-`/admin/{app}/{model}/{id}/change/` (model detail pages) - **NOW FIXED**
**Result:**
- Admin interfaces now have modern date range pickers, numeric sliders, and searchable dropdowns
- Export functionality added to 10+ critical models (Account, Site, User, Payment, Keywords, Publishing, Sync Events)
- Bulk operations added to Automation, Publishing, and Integration modules
- **Custom sidebar navigation is now consistent across ALL admin pages**
- Better UX for filtering large datasets and managing operations at scale
---
### ✅ Phase 3: Monitoring & Dashboards - COMPLETED (Dec 14-15, 2025)
**Completed:**
- [x] Installed django-celery-results for task monitoring
- [x] Created CeleryTaskResultAdmin with colored status and execution time
- [x] Created CeleryGroupResultAdmin with result count display
- [x] Fixed celery import issue (added `from celery import current_app`)
- [x] Fixed execution_time format_html ValueError bug
- [x] Added retry_failed_tasks action to Celery admin
- [x] Added clear_old_tasks action to Celery admin
- [x] Created admin_dashboard view with 6 metric cards
- [x] Created dashboard.html template with Tailwind styling
- [x] Added AdminAlerts utility class for system alerts
- [x] Integrated alerts into dashboard
- [x] Added dashboard route to admin site URLs
- [x] Added index redirect to dashboard (auto-redirect from /admin/)
- [x] All Celery admin pages verified working (200 status)
**Files created:**
- `/data/app/igny8/backend/igny8_core/admin/dashboard.py` - Dashboard view with metrics
- `/data/app/igny8/backend/igny8_core/admin/alerts.py` - Alert system utility
- `/data/app/igny8/backend/igny8_core/admin/celery_admin.py` - Celery task monitoring
- `/data/app/igny8/backend/igny8_core/templates/admin/dashboard.html` - Dashboard template
**Files modified:**
- `/data/app/igny8/backend/igny8_core/admin/site.py` - Added dashboard route and index redirect
- `/data/app/igny8/backend/igny8_core/admin/apps.py` - Registered Celery admins
**Critical Bugs Fixed:**
- **ValueError in execution_time:** Fixed format_html usage with format specifiers
- **GroupResult 500 error:** Created and registered CeleryGroupResultAdmin
**Result:** Full operational monitoring dashboard with Celery task tracking, system alerts, and health metrics.
---
### 🔄 Phase 4: Analytics & Reporting - IN PROGRESS (Dec 15, 2025)
**Completed:**
- [x] Created reports.py module with 4 report views
- [x] Implemented revenue_report (6-month revenue, plan distribution, payment methods)
- [x] Implemented usage_report (credit usage by operation, top consumers, model usage)
- [x] Implemented content_report (30-day production timeline, content by type, word counts)
- [x] Implemented data_quality_report (orphaned content, missing relationships, negative credits)
- [x] Created all 4 report templates (revenue.html, usage.html, content.html, data_quality.html)
- [x] Integrated Chart.js 4.4.0 for data visualizations
- [x] Added 4 report routes to admin site URLs
- [x] Added Reports & Analytics section to sidebar with 4 report links
- [x] Permission checks added (@staff_member_required decorator on all reports)
- [x] Admin context merged in all reports for sidebar consistency
- [x] Backend restarted successfully
**Remaining Tasks:**
- [ ] Test all 4 reports with real production data
- [ ] Optimize report queries for performance (add select_related, prefetch_related)
- [ ] Add caching to dashboard metrics (optional)
**Note:** Reports are fully functional and accessible via sidebar. Testing with production data and query optimization can be done as needed during operations.
**Files created:**
- `/data/app/igny8/backend/igny8_core/admin/reports.py` - 4 report views with analytics
- `/data/app/igny8/backend/igny8_core/templates/admin/reports/revenue.html` - Revenue analytics with Chart.js
- `/data/app/igny8/backend/igny8_core/templates/admin/reports/usage.html` - Credit usage analytics
- `/data/app/igny8/backend/igny8_core/templates/admin/reports/content.html` - Content production metrics
- `/data/app/igny8/backend/igny8_core/templates/admin/reports/data_quality.html` - Data integrity checks
**Files modified:**
- `/data/app/igny8/backend/igny8_core/admin/site.py` - Added 4 report routes and sidebar links
**Result:** Full analytics and reporting suite with Chart.js visualizations, accessible via admin sidebar. Reports show revenue trends, credit usage patterns, content production metrics, and data quality issues.
---
### ✅ Phase 5: Advanced Features - COMPLETED (Dec 15, 2025)
**Completed:**
- [x] Enabled list_editable for Tasks admin (status field)
- [x] Enabled list_editable for Keywords admin (status field)
- [x] Added HistoricalRecords to Payment model
- [x] Added HistoricalRecords to Account model
- [x] Added HistoricalRecords to CreditCostConfig model
- [x] Created and ran migrations for history tables
- [x] Updated Payment, Account, CreditCostConfig admins to use SimpleHistoryAdmin
- [x] Created permission groups (Content Manager, Billing Admin, Support Agent)
- [x] Assigned appropriate permissions to each group
**Files created:**
- `/data/app/igny8/backend/igny8_core/auth/migrations/0017_add_history_tracking.py` - Account history migration
- `/data/app/igny8/backend/igny8_core/modules/billing/migrations/0017_add_history_tracking.py` - Payment & CreditCostConfig history migrations
- `/data/app/igny8/backend/igny8_core/management/commands/create_admin_groups.py` - Permission groups command
- `/data/app/igny8/backend/create_groups.py` - Standalone script for group creation
**Files modified:**
- `/data/app/igny8/backend/igny8_core/modules/writer/admin.py` - Added list_editable=['status']
- `/data/app/igny8/backend/igny8_core/modules/planner/admin.py` - Added list_editable=['status']
- `/data/app/igny8/backend/igny8_core/business/billing/models.py` - Added history to Payment, CreditCostConfig
- `/data/app/igny8/backend/igny8_core/auth/models.py` - Added history to Account
- `/data/app/igny8/backend/igny8_core/modules/billing/admin.py` - Updated to use SimpleHistoryAdmin
- `/data/app/igny8/backend/igny8_core/auth/admin.py` - Updated to use SimpleHistoryAdmin
**Permission Groups Created:**
1. **Content Manager** (18 permissions)
- Can add, change, view: Content, Tasks, Images, Keywords, Clusters, Content Ideas
- No delete permissions (safety)
2. **Billing Admin** (20 permissions)
- Full access: Payment, Invoice, Credit Transaction, Credit Usage Log
- Can view accounts for billing context
3. **Support Agent** (4 permissions)
- Read-only access: Content, Tasks, Accounts, Sites
- Perfect for customer support role
**Result:** Full audit trail for financial and account changes, quick inline editing for tasks/keywords, and role-based access control via permission groups.
---
## Key Achievements
1. **✅ NO Custom CSS/Styling Needed**
- Unfold handles all UI/UX automatically
- Modern Tailwind-based design out of the box
- Dark mode support built-in
- Responsive layout automatic
2. **✅ UserAdmin Popup Fix**
- Fixed popup forms to use Unfold templates
- Multiple inheritance: `class UserAdmin(BaseUserAdmin, ModelAdmin)`
- All popups now consistent with main admin styling
3. **✅ Clean Architecture**
- Single admin system (Unfold only)
- No conflicts between themes
- All containers healthy
4. **✅ CRITICAL: Sidebar Navigation Fix (Issue #5)**
- Custom sidebar now appears on ALL admin pages
- Consistent navigation throughout entire admin interface
- Fixed `get_app_list()` to ignore app_label parameter
- Set both `available_apps` and `app_list` in context
5. **✅ Export Functionality**
- Added to 10+ critical models
- CSV/Excel export with proper Resource classes
- Account, Site, User, Payment, Keywords, Publishing, Sync Events all exportable
6. **✅ Bulk Operations**
- Automation: Enable/disable configs
- Publishing: Retry failed publishes
- Integration: Enable/disable/trigger sync
- Content: Status changes, taxonomy assignment
- Tasks: Status changes, cluster assignment
- Keywords: Cluster assignment, status changes
---
## Next Steps
### Immediate (Current):
**Phase 5: Advanced Features** is the next phase to implement:
- Enable inline editing for Tasks and Keywords (list_editable)
- Add audit trail with django-simple-history to Payment, Account, CreditCostConfig
- Create admin permission groups for role-based access control
### Implementation Ready:
All foundation work is complete (Phases 0-4). Phase 5 focuses on advanced admin features:
- **Inline Editing:** Quick edits without opening detail page
- **History Tracking:** Full audit trail for financial and account changes
- **Permission Groups:** Content Manager, Billing Admin, Support Agent roles
### Operational Tasks (Ongoing):
1. Test reports with production data as system grows
2. Optimize slow report queries if needed
3. Review dashboard alerts weekly
4. Clean up old Celery task results monthly
---
## Technical Notes
- **No styling work required** - Unfold provides everything
- **Use Unfold classes** - badge, alert, card classes available
- **No emoji icons needed** - Unfold has Material Design icons
- **Focus on functionality** - UI is already handled
---
## Resources
- **Unfold Documentation:** https://unfoldadmin.com/
- **Current Admin:** https://api.igny8.com/admin/
- **Implementation Plan:** `/data/app/igny8/DJANGO-ADMIN-IMPROVEMENT-PLAN.md`

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