content calendar fixed

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-16 21:34:20 +00:00
parent 0f5e02e451
commit cf755b23dc
3 changed files with 47 additions and 44 deletions

View File

@@ -0,0 +1,744 @@
# Free Account Options - Architecture Analysis
**Date:** January 14, 2026
**Status:** Planning Phase
**Purpose:** Compare two approaches for free user onboarding with limited AI operations
---
## Current System Architecture
### 1. **Account & Plan System**
```python
Account Model:
- plan (FK to Plan)
- credits (Integer, current balance)
- status (trial, active, suspended, pending_payment, cancelled)
- payment_method
- usage_ahrefs_queries (monthly counter)
- usage_period_start/end
Plan Model:
- name, slug, price, billing_cycle
- is_internal (hide from public listings)
- max_sites, max_keywords, max_users, max_author_profiles
- included_credits (monthly allocation)
- extra_credit_price
- allow_credit_topup
- max_ahrefs_queries (monthly limit)
```
### 2. **AI Configuration System**
```python
AIModelConfig (Global - Single Source of Truth):
- model_name (e.g., 'gpt-4o-mini', 'hidream-full')
- model_type (text/image)
- provider (openai, runware, etc.)
- is_default (one default per type)
- is_active
- cost_per_1k_input/output (text models)
- credits_per_image (image models)
- tokens_per_credit (text models)
AISettings (Per-Account Overrides):
- account (FK)
- integration_type (openai, runware)
- config (API keys, settings)
- model_preferences (per operation type)
- cost_limits (budgets)
```
### 3. **Credit Tracking System**
```python
CreditTransaction:
- transaction_type (purchase, subscription, deduction, adjustment)
- amount (positive/negative)
- balance_after
- description, metadata
CreditUsageLog (Per AI Operation):
- operation_type (clustering, idea_generation, content_generation, image_generation)
- credits_used
- cost_usd
- model_used
- tokens_input/output
- site (FK for filtering)
- related_object_type/id
```
### 4. **Current Registration Flow**
1. User registers → `RegisterSerializer.create()`
2. If `plan_slug` not provided or = 'free':
- Assigns Plan.slug='free' (must exist)
- Account.status = 'trial'
- Account.credits = plan.included_credits
- Creates CreditTransaction (initial allocation)
3. User can perform AI operations until credits exhausted
---
## 📊 Option 1: Individual Free Accounts (Isolated)
### **Concept**
Each user gets their own free account with:
- Fixed cheaper AI models (GPT-4o mini, Hidream-full)
- Low credit allocation (50-100 operations)
- Own isolated data/workspace
- Ability to upgrade to paid plan
### **Implementation Plan**
#### **Step 1: Create Free Plan**
```sql
-- Admin action via Django Admin
INSERT INTO igny8_plans (
name, slug, price, billing_cycle,
is_featured, is_internal, is_active,
max_sites, max_users, max_keywords,
included_credits, allow_credit_topup,
max_ahrefs_queries
) VALUES (
'Free Starter', 'free', 0.00, 'monthly',
false, true, true,
1, -- max_sites: 1 site only
1, -- max_users: owner only
100, -- max_keywords: 100
100, -- included_credits: 100 credits (~50 operations)
false, -- No credit topup for free
0 -- No Ahrefs access
);
```
#### **Step 2: Create AI Model Configs (If Not Exist)**
```sql
-- GPT-4o Mini (cheaper text model)
INSERT INTO igny8_ai_model_config (
model_name, model_type, provider, display_name,
is_default, is_active,
cost_per_1k_input, cost_per_1k_output,
tokens_per_credit, max_tokens, context_window
) VALUES (
'gpt-4o-mini', 'text', 'openai', 'GPT-4o Mini (Fast & Efficient)',
false, true,
0.00015, 0.0006, -- Cheaper than GPT-4
1000, 16384, 128000
);
-- Hidream Full (cheaper image model)
INSERT INTO igny8_ai_model_config (
model_name, model_type, provider, display_name,
is_default, is_active,
credits_per_image, quality_tier,
square_size, landscape_size
) VALUES (
'hidream-full', 'image', 'runware', 'Hidream Full (Standard Quality)',
false, true,
1, -- 1 credit per image (cheapest)
'basic',
'1024x1024', '1280x768'
);
```
#### **Step 3: Update Registration Logic**
```python
# In auth/serializers.py RegisterSerializer.create()
# No changes needed! Current logic already handles this:
if not plan_slug or plan_slug == 'free':
plan = Plan.objects.get(slug='free', is_active=True)
account_status = 'trial'
initial_credits = plan.included_credits # 100 credits
```
#### **Step 4: Force Free Plan AI Models**
**Option A: Global Default (Simplest)**
```sql
-- Set GPT-4o Mini and Hidream as defaults
UPDATE igny8_ai_model_config
SET is_default = false
WHERE model_type = 'text';
UPDATE igny8_ai_model_config
SET is_default = true
WHERE model_name = 'gpt-4o-mini';
UPDATE igny8_ai_model_config
SET is_default = false
WHERE model_type = 'image';
UPDATE igny8_ai_model_config
SET is_default = true
WHERE model_name = 'hidream-full';
```
**Pros:** Zero code changes, all free accounts inherit defaults
**Cons:** Affects ALL accounts (paid users too)
**Option B: Per-Account AI Settings (Recommended)**
```python
# In auth/serializers.py RegisterSerializer.create()
# After account creation:
if account_status == 'trial': # Free accounts only
from igny8_core.modules.system.settings_models import AISettings
# Create AI settings for OpenAI (text)
AISettings.objects.create(
account=account,
integration_type='openai',
model_preferences={
'clustering': 'gpt-4o-mini',
'idea_generation': 'gpt-4o-mini',
'content_generation': 'gpt-4o-mini',
'optimization': 'gpt-4o-mini',
},
is_active=True
)
# Create AI settings for Runware (images)
AISettings.objects.create(
account=account,
integration_type='runware',
model_preferences={
'image_generation': 'hidream-full',
},
is_active=True
)
```
**Pros:** Free accounts locked to cheap models, paid accounts unaffected
**Cons:** Requires code change in registration flow
**Option C: Plan-Level AI Model Configuration**
```python
# Add new field to Plan model (migration required)
class Plan(models.Model):
# ... existing fields ...
allowed_text_models = models.JSONField(
default=list,
help_text="Allowed text AI models (empty = all)"
)
allowed_image_models = models.JSONField(
default=list,
help_text="Allowed image AI models (empty = all)"
)
force_default_models = models.BooleanField(
default=False,
help_text="Force plan defaults, ignore user overrides"
)
# Update Free plan:
plan = Plan.objects.get(slug='free')
plan.allowed_text_models = ['gpt-4o-mini']
plan.allowed_image_models = ['hidream-full']
plan.force_default_models = True
plan.save()
# In AI operation logic (ai/services.py or similar):
def get_ai_model_for_account(account, operation_type):
plan = account.plan
if plan.force_default_models:
if operation_type in ['clustering', 'idea_generation', 'content_generation']:
return 'gpt-4o-mini'
elif operation_type == 'image_generation':
return 'hidream-full'
# ... existing logic for paid accounts
```
**Pros:** Centralized plan-based control, scalable
**Cons:** Requires migration + AI operation logic changes
#### **Step 5: Frontend Restrictions**
```typescript
// In frontend, check plan limits
if (user.account.plan.slug === 'free') {
// Hide model selector (force defaults)
// Show "Upgrade for more models" message
// Disable credit topup
// Disable Ahrefs research
}
```
### **✅ Pros: Individual Free Accounts**
1. **Full User Experience** - Users get their own workspace, test all features
2. **Data Isolation** - Private data, no cross-contamination
3. **Smooth Upgrade Path** - Existing account → upgrade plan → keep data
4. **Proper Multi-Tenancy** - Each account is isolated, secure
5. **Credit Tracking** - Accurate per-user usage analytics
6. **Marketing Value** - "100 Free Credits" sounds generous
### **❌ Cons: Individual Free Accounts**
1. **Database Growth** - Each user = new Account + User + potential Sites/Keywords
2. **Abuse Potential** - Users can create multiple emails for free credits
3. **Complex Enforcement** - Need to enforce model restrictions per account
4. **Storage Costs** - Each account stores independent data
5. **Migration Complexity** - If user upgrades, need to handle plan transition
### **Effort Estimate: Individual Free Accounts**
- **Minimal Approach** (Option A): **1 hour**
- Create free plan via admin
- Set default models globally
- Update frontend to hide topup for free users
- **Recommended Approach** (Option B): **4-6 hours**
- Create free plan via admin
- Update registration to create AISettings per free account
- Update AI operation logic to read account-specific models
- Frontend: Hide model selector for free users
- Testing across all AI operations
- **Enterprise Approach** (Option C): **1-2 days**
- Migration: Add allowed_models fields to Plan
- Update registration flow
- Refactor AI operation logic (all modules)
- Admin UI for plan model management
- Comprehensive testing
---
## 🎭 Option 2: Shared Demo Account (Multi-User)
### **Concept**
One demo account shared by multiple users:
- Users provide email → get "demo access" token
- Limited operations pool (50-100 per user, tracked separately)
- Shared data (users see what others created)
- Pre-configured cheaper AI models
- No upgrade path (must create new account)
### **Implementation Plan**
#### **Step 1: Create Demo Account**
```sql
-- Create demo plan (internal)
INSERT INTO igny8_plans (
name, slug, price, billing_cycle,
is_internal, is_active,
max_sites, max_users, max_keywords,
included_credits, allow_credit_topup
) VALUES (
'Demo Access', 'demo', 0.00, 'monthly',
true, true,
1, -- 1 demo site
999, -- Unlimited demo users
50, -- Limited keywords
10000, -- Large shared pool
false
);
-- Create demo account
INSERT INTO igny8_tenants (
name, slug, owner_id, plan_id,
credits, status
) VALUES (
'IGNY8 Demo Workspace', 'igny8-demo', 1, -- owner = admin
(SELECT id FROM igny8_plans WHERE slug='demo'),
10000, 'active'
);
-- Create demo site
INSERT INTO igny8_sites (
name, url, account_id, is_active
) VALUES (
'Demo Content Site', 'https://demo.example.com',
(SELECT id FROM igny8_tenants WHERE slug='igny8-demo'),
true
);
```
#### **Step 2: Create DemoUserAccess Model**
```python
# In auth/models.py
class DemoUserAccess(models.Model):
"""Track individual demo user access and limits"""
email = models.EmailField(unique=True, db_index=True)
demo_account = models.ForeignKey(
'Account',
on_delete=models.CASCADE,
related_name='demo_users'
)
access_token = models.CharField(max_length=255, unique=True)
operations_used = models.IntegerField(default=0)
operations_limit = models.IntegerField(default=50)
created_at = models.DateTimeField(auto_now_add=True)
last_accessed = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
class Meta:
db_table = 'igny8_demo_user_access'
indexes = [
models.Index(fields=['email', 'is_active']),
models.Index(fields=['access_token']),
]
def __str__(self):
return f"Demo: {self.email} ({self.operations_used}/{self.operations_limit})"
def has_operations_remaining(self):
return self.operations_used < self.operations_limit
```
#### **Step 3: Migration**
```python
# migrations/0014_demo_user_access.py
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('igny8_core_auth', '0013_add_plan_is_internal'),
]
operations = [
migrations.CreateModel(
name='DemoUserAccess',
fields=[
('id', models.AutoField(primary_key=True)),
('email', models.EmailField(unique=True, db_index=True)),
('demo_account', models.ForeignKey(
on_delete=models.CASCADE,
to='igny8_core_auth.Account',
related_name='demo_users'
)),
('access_token', models.CharField(max_length=255, unique=True)),
('operations_used', models.IntegerField(default=0)),
('operations_limit', models.IntegerField(default=50)),
('created_at', models.DateTimeField(auto_now_add=True)),
('last_accessed', models.DateTimeField(auto_now=True)),
('is_active', models.BooleanField(default=True)),
],
options={'db_table': 'igny8_demo_user_access'},
),
]
```
#### **Step 4: Demo Access Endpoint**
```python
# In auth/views.py AuthViewSet
@action(detail=False, methods=['post'], permission_classes=[])
def request_demo_access(self, request):
"""Request demo account access with email only"""
email = request.data.get('email')
if not email:
return error_response(
error='Email is required',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Validate email format
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
try:
validate_email(email)
except ValidationError:
return error_response(
error='Invalid email format',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Get demo account
try:
demo_account = Account.objects.get(slug='igny8-demo', status='active')
except Account.DoesNotExist:
return error_response(
error='Demo account not configured',
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
request=request
)
# Get or create demo user access
from .models import DemoUserAccess
import secrets
demo_user, created = DemoUserAccess.objects.get_or_create(
email=email,
demo_account=demo_account,
defaults={
'access_token': secrets.token_urlsafe(32),
'operations_limit': 50,
}
)
if not demo_user.is_active:
return error_response(
error='Demo access suspended. Please contact support.',
status_code=status.HTTP_403_FORBIDDEN,
request=request
)
if not demo_user.has_operations_remaining():
return error_response(
error='Demo operation limit reached. Please sign up for a full account.',
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
request=request
)
# Generate temporary JWT for demo account
# (Custom token that includes demo_user_id)
access_token = generate_demo_access_token(demo_account, demo_user)
return success_response(
data={
'access_token': access_token,
'demo_user': {
'email': demo_user.email,
'operations_remaining': demo_user.operations_limit - demo_user.operations_used,
'operations_limit': demo_user.operations_limit,
},
'account': {
'id': demo_account.id,
'name': demo_account.name,
'is_demo': True,
}
},
message='Demo access granted' if created else 'Welcome back to demo',
request=request
)
```
#### **Step 5: Custom JWT with Demo Context**
```python
# In auth/utils.py
def generate_demo_access_token(account, demo_user):
"""Generate JWT for demo access with demo_user context"""
import jwt
from datetime import datetime, timedelta
from django.conf import settings
expiry = datetime.utcnow() + timedelta(hours=24) # 24-hour demo session
payload = {
'account_id': account.id,
'account_slug': account.slug,
'demo_user_id': demo_user.id,
'demo_user_email': demo_user.email,
'is_demo': True,
'exp': expiry,
'iat': datetime.utcnow(),
}
return jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256')
```
#### **Step 6: Demo Operation Tracking Middleware**
```python
# In middleware/demo_tracking.py
class DemoOperationTrackingMiddleware:
"""Track demo user operations and enforce limits"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Check if demo user
if hasattr(request, 'demo_user_id'):
from igny8_core.auth.models import DemoUserAccess
demo_user = DemoUserAccess.objects.select_for_update().get(
id=request.demo_user_id
)
# Check limit before processing
if not demo_user.has_operations_remaining():
return JsonResponse({
'success': False,
'error': 'Demo operation limit reached. Please sign up for a full account.',
'upgrade_url': '/pricing'
}, status=429)
# Store demo_user in request for operation tracking
request.demo_user = demo_user
response = self.get_response(request)
return response
# Add to settings.py MIDDLEWARE
```
#### **Step 7: Update AI Operation Logic**
```python
# In ai/services.py or wherever AI operations are tracked
def log_ai_operation(account, operation_type, credits_used, **kwargs):
"""Log AI operation and increment demo counter if demo user"""
from igny8_core.business.billing.models import CreditUsageLog
from django.db import transaction
with transaction.atomic():
# Create credit usage log
CreditUsageLog.objects.create(
account=account,
operation_type=operation_type,
credits_used=credits_used,
**kwargs
)
# Deduct credits from account
account.credits -= credits_used
account.save()
# If demo user, increment their personal counter
from threading import local
_request = getattr(local(), 'request', None)
if _request and hasattr(_request, 'demo_user'):
demo_user = _request.demo_user
demo_user.operations_used += 1
demo_user.save()
```
#### **Step 8: Frontend Demo Flow**
```typescript
// New demo signup flow
async function requestDemoAccess(email: string) {
const response = await api.post('/v1/auth/request-demo-access/', { email });
if (response.success) {
// Store demo token
localStorage.setItem('demo_token', response.data.access_token);
localStorage.setItem('is_demo', 'true');
// Show demo banner
showDemoBanner({
operationsRemaining: response.data.demo_user.operations_remaining,
operationsLimit: response.data.demo_user.operations_limit,
});
// Redirect to demo workspace
router.push('/dashboard');
}
}
// Demo banner component
<DemoBanner>
<p>🎭 You're in Demo Mode - {operationsRemaining} operations remaining</p>
<Button onClick={() => router.push('/pricing')}>
Upgrade for Full Access
</Button>
</DemoBanner>
// Disable certain features in demo mode
if (isDemo) {
disableFeatures(['integrations', 'automation', 'wordpress_sync']);
showSharedDataWarning();
}
```
### **✅ Pros: Shared Demo Account**
1. **Zero Database Growth** - One account, minimal new records
2. **Instant Access** - No account creation, just email → token
3. **Showcase Content** - Users see real AI-generated examples from others
4. **Anti-Abuse** - Email-based tracking, hard limits per email
5. **Conversion Pressure** - "See others creating, sign up for your own workspace"
6. **Cost Efficient** - Shared credit pool, bulk tracking
### **❌ Cons: Shared Demo Account**
1. **No Data Privacy** - All users see shared workspace (could be feature or bug)
2. **Complex Access Control** - Need custom JWT + middleware + tracking
3. **No Upgrade Path** - Demo token ≠ real account, must register separately
4. **Shared Credit Pool** - If pool exhausted, demo is down for everyone
5. **Feature Limitations** - Can't show integrations, automation, publishing
6. **User Confusion** - "Why do I see others' content?" + "Lost my demo data!"
7. **Backend Complexity** - New model, middleware, JWT type, operation tracking
### **Effort Estimate: Shared Demo Account**
**Full Implementation**: **2-3 days**
- Create demo plan + account + site (1 hour)
- Create DemoUserAccess model + migration (2 hours)
- Build request_demo_access endpoint (2 hours)
- Custom JWT generation with demo context (2 hours)
- Middleware for demo tracking + limits (3 hours)
- Update AI operation logging (2 hours)
- Frontend: Demo flow + banner + restrictions (4 hours)
- Admin: Dashboard to manage demo users (2 hours)
- Testing: Edge cases, limits, shared data (4 hours)
---
## 🎯 Recommendation
### **🏆 Winner: Option 1 - Individual Free Accounts (Option B)**
**Rationale:**
1. **Simpler Architecture** - Leverages existing multi-tenancy, no custom JWT/middleware
2. **Better UX** - Private workspace, smooth upgrade path, feels like real product
3. **Faster Implementation** - 4-6 hours vs 2-3 days
4. **Lower Risk** - No shared data confusion, no new access control layer
5. **Marketing Win** - "100 Free Credits" > "Demo Access with Shared Data"
6. **Scalable** - If abuse becomes issue, add email verification or captcha
**Implementation Checklist:**
```markdown
- [ ] Create 'free' plan via Django Admin
- [ ] Set: included_credits=100, max_sites=1, max_keywords=100
- [ ] Set: is_internal=true, allow_credit_topup=false
- [ ] Verify AI Model Configs exist
- [ ] GPT-4o Mini (text, cheap)
- [ ] Hidream Full (image, cheap)
- [ ] Update RegisterSerializer (auth/serializers.py)
- [ ] After account creation for trial status:
- [ ] Create AISettings for openai (text) → gpt-4o-mini
- [ ] Create AISettings for runware (images) → hidream-full
- [ ] Update Frontend
- [ ] Hide model selector for free plan
- [ ] Disable credit topup for free plan
- [ ] Show "Upgrade for more models" CTA
- [ ] Testing
- [ ] Register new free account
- [ ] Run text AI operation → verify gpt-4o-mini used
- [ ] Run image AI operation → verify hidream-full used
- [ ] Verify 100 credits allocated
- [ ] Verify upgrade flow works
```
---
## 🔮 Future Enhancements (Optional)
### For Option 1 (Individual Free Accounts):
1. **Email Verification** - Require verified email to prevent abuse
2. **Captcha** - Add reCAPTCHA on free signups
3. **Usage Analytics** - Track free-to-paid conversion rates
4. **Referral Credits** - Give 50 bonus credits for referrals
5. **Time-Limited Trial** - 30-day access instead of credit-limited
### For Option 2 (Shared Demo - If Pursued):
1. **Demo Content Curation** - Pre-seed with high-quality examples
2. **Demo Reset** - Daily reset to clean state
3. **Anonymous Mode** - Show "User A, User B" instead of emails
4. **Live Activity Feed** - "User just generated an article about X"
5. **Demo Leaderboard** - Gamify the experience
---
## 📚 Reference Files
**Models:**
- `/backend/igny8_core/auth/models.py` - Account, Plan, User
- `/backend/igny8_core/business/billing/models.py` - AIModelConfig, CreditTransaction, CreditUsageLog
- `/backend/igny8_core/modules/system/settings_models.py` - AISettings
**Registration:**
- `/backend/igny8_core/auth/serializers.py` - RegisterSerializer
- `/backend/igny8_core/auth/views.py` - AuthViewSet.register()
**AI Operations:**
- Check modules: clustering, ideas, content, images for credit deduction logic
---
## ✅ Decision
**Recommended:** Proceed with **Option 1 - Individual Free Accounts (Option B)**
**Estimated Time:** 4-6 hours
**Risk Level:** Low
**User Experience:** Excellent
Consider **Option 2** only if:
- Need to showcase "collaborative" aspect
- Want zero database growth (high traffic expected)
- Marketing wants "see what others create" feature