temproary docs uplaoded
This commit is contained in:
452
v2/Live Docs on Server/igny8-app-docs/10-MODULES/AUTOMATION.md
Normal file
452
v2/Live Docs on Server/igny8-app-docs/10-MODULES/AUTOMATION.md
Normal file
@@ -0,0 +1,452 @@
|
||||
# Automation Module
|
||||
|
||||
**Last Verified:** January 18, 2026
|
||||
**Version:** 1.8.1
|
||||
**Status:** ✅ Active
|
||||
**Backend Path:** `backend/igny8_core/business/automation/`
|
||||
**Frontend Path:** `frontend/src/pages/Automation/`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Models | `business/automation/models.py` | `AutomationConfig`, `AutomationRun`, `DefaultAutomationConfig` |
|
||||
| Service | `business/automation/services/automation_service.py` | `AutomationService` |
|
||||
| Logger | `business/automation/services/automation_logger.py` | `AutomationLogger` |
|
||||
| Celery Tasks | `business/automation/tasks.py` | `run_automation_task`, `check_scheduled_automations`, `check_test_triggers` |
|
||||
| Publishing Tasks | `igny8_core/tasks/publishing_scheduler.py` | Scheduled publishing |
|
||||
| **Unified Settings** | `api/unified_settings.py` | **v1.8.0** Consolidated settings API |
|
||||
| **Default Settings API** | `api/unified_settings.py` | **v1.8.1** `DefaultSettingsAPIView` for reset |
|
||||
| Frontend Overview | `pages/Automation/AutomationOverview.tsx` | **v1.8.0** Run history dashboard |
|
||||
| Frontend Run Detail | `pages/Automation/AutomationRunDetail.tsx` | **v1.8.0** Detailed run view |
|
||||
| Frontend Manual Run | `pages/Automation/AutomationPage.tsx` | Manual run UI |
|
||||
| **Site Settings** | `pages/Sites/AIAutomationSettings.tsx` | **v1.8.0** Unified settings UI |
|
||||
| Progress Bar | `components/Automation/GlobalProgressBar.tsx` | Full pipeline progress |
|
||||
| Processing Card | `components/Automation/CurrentProcessingCard.tsx` | Real-time progress |
|
||||
| **Scheduling Doc** | `docs/40-WORKFLOWS/AUTOMATION-AND-PUBLISHING-SCHEDULING.md` | **v1.8.1** Complete scheduling guide |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Automation module runs the complete 7-stage content pipeline automatically:
|
||||
|
||||
```
|
||||
Keywords → Clusters → Ideas → Tasks → Content → Image Prompts → Images → Published
|
||||
```
|
||||
|
||||
**Settings Location (v1.8.0+):** Site Settings → Automation tab
|
||||
|
||||
---
|
||||
|
||||
## Scheduling (v1.8.1)
|
||||
|
||||
| Task | Schedule | Purpose |
|
||||
|------|----------|---------|
|
||||
| `check_scheduled_automations` | Every hour at `:05` | Check if automations should run |
|
||||
| `check_test_triggers` | Every minute | Check for admin test triggers |
|
||||
|
||||
**How it works:**
|
||||
- Users select hour (12-hour AM/PM format), stored as `HH:00` (24-hour)
|
||||
- Celery compares `scheduled_hour == current_hour`
|
||||
- 23-hour block prevents re-runs within same day
|
||||
|
||||
**Test Mode (Admin):**
|
||||
- `test_mode_enabled` + `test_trigger_at` fields on `AutomationConfig`
|
||||
- Allows immediate triggering without waiting for schedule
|
||||
- Bypasses 23-hour blocking
|
||||
|
||||
---
|
||||
|
||||
## 7-Stage Pipeline
|
||||
|
||||
| Stage | Name | AI Function | Credit Cost | Can Skip |
|
||||
|-------|------|-------------|-------------|----------|
|
||||
| 1 | Keywords → Clusters | `AutoClusterFunction` | Per batch | ✅ |
|
||||
| 2 | Clusters → Ideas | `GenerateIdeasFunction` | Per idea | ✅ |
|
||||
| 3 | Ideas → Tasks | None (local) | None | ✅ |
|
||||
| 4 | Tasks → Content | `GenerateContentFunction` | Per 100 words | ✅ |
|
||||
| 5 | Content → Image Prompts | `GenerateImagePromptsFunction` | Per prompt | ✅ |
|
||||
| 6 | Image Prompts → Images | `process_image_generation_queue` | Per image | ✅ |
|
||||
| 7 | Review → Published | Publishing Scheduler | None | ✅ |
|
||||
|
||||
**Note:** Stage 7 uses the Publishing Scheduler with `PublishingSettings` for auto-approval and scheduling.
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### AutomationConfig
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Target site |
|
||||
| enabled | Boolean | Enable/disable automation |
|
||||
| frequency | CharField | hourly/daily/weekly |
|
||||
| scheduled_time | TimeField | Time to run |
|
||||
| stage_1_batch_size | Integer | Keywords per batch |
|
||||
| stage_2_batch_size | Integer | Clusters per batch |
|
||||
| stage_3_batch_size | Integer | Ideas per batch |
|
||||
| stage_4_batch_size | Integer | Tasks per batch |
|
||||
| stage_5_batch_size | Integer | Content per batch |
|
||||
| stage_6_batch_size | Integer | Images per batch |
|
||||
| **stage_1_enabled** | Boolean | **v1.8.0** Enable stage 1 |
|
||||
| **stage_2_enabled** | Boolean | **v1.8.0** Enable stage 2 |
|
||||
| **stage_3_enabled** | Boolean | **v1.8.0** Enable stage 3 |
|
||||
| **stage_4_enabled** | Boolean | **v1.8.0** Enable stage 4 |
|
||||
| **stage_5_enabled** | Boolean | **v1.8.0** Enable stage 5 |
|
||||
| **stage_6_enabled** | Boolean | **v1.8.0** Enable stage 6 |
|
||||
| **stage_7_enabled** | Boolean | **v1.8.0** Enable stage 7 |
|
||||
| **stage_X_use_testing** | Boolean | **v1.8.1** Use testing model (AI stages 1,2,4,5,6) |
|
||||
| **stage_X_budget_pct** | Integer | **v1.8.1** Credit budget % (AI stages) |
|
||||
| **max_keywords_per_run** | Integer | **v1.8.0** Per-run limit stage 1 |
|
||||
| **max_clusters_per_run** | Integer | **v1.8.0** Per-run limit stage 2 |
|
||||
| **max_ideas_per_run** | Integer | **v1.8.0** Per-run limit stage 3 |
|
||||
| **max_tasks_per_run** | Integer | **v1.8.0** Per-run limit stage 4 |
|
||||
| **max_content_per_run** | Integer | **v1.8.0** Per-run limit stage 5 |
|
||||
| **max_images_per_run** | Integer | **v1.8.0** Per-run limit stage 6 |
|
||||
| within_stage_delay | Integer | Seconds between batches |
|
||||
| between_stage_delay | Integer | Seconds between stages |
|
||||
| last_run_at | DateTime | Last execution |
|
||||
| next_run_at | DateTime | Next scheduled run |
|
||||
| **test_mode_enabled** | Boolean | **v1.8.1** Enable test mode for admin |
|
||||
| **test_trigger_at** | DateTime | **v1.8.1** When to trigger test run |
|
||||
|
||||
### DefaultAutomationConfig (v1.8.1)
|
||||
|
||||
Singleton model for centralized default settings. See [AUTOMATION-AND-PUBLISHING-SCHEDULING.md](../40-WORKFLOWS/AUTOMATION-AND-PUBLISHING-SCHEDULING.md) for full schema.
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| is_enabled | Boolean | Default: Enable scheduled automation |
|
||||
| frequency | CharField | Default frequency (daily/weekly/monthly) |
|
||||
| next_scheduled_hour | Integer | Next hour to assign (auto-increments) |
|
||||
| stage_X_enabled | Boolean | Default: Enable each stage |
|
||||
| stage_X_batch_size | Integer | Default: Batch size per stage |
|
||||
| stage_X_use_testing | Boolean | Default: Use testing model |
|
||||
| stage_X_budget_pct | Integer | Default: Credit budget % |
|
||||
| max_X_per_run | Integer | Default: Per-run limits |
|
||||
| auto_approval_enabled | Boolean | Default: Auto-approve content |
|
||||
| auto_publish_enabled | Boolean | Default: Auto-publish content |
|
||||
| publish_days | JSONField | Default: Days to publish |
|
||||
| publish_time_slots | JSONField | Default: Time slots to publish |
|
||||
|
||||
### AutomationRun
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| config | FK | Parent config |
|
||||
| trigger_type | CharField | manual/scheduled |
|
||||
| status | CharField | running/paused/cancelled/completed/failed |
|
||||
| current_stage | Integer | Current stage (1-7) |
|
||||
| started_at | DateTime | Start time |
|
||||
| paused_at | DateTime | Pause time (nullable) |
|
||||
| resumed_at | DateTime | Resume time (nullable) |
|
||||
| cancelled_at | DateTime | Cancel time (nullable) |
|
||||
| completed_at | DateTime | Completion time (nullable) |
|
||||
| total_credits_used | Decimal | Total credits consumed |
|
||||
| **initial_snapshot** | JSON | **v1.3.0** Queue sizes at run start |
|
||||
| stage_1_result | JSON | Stage 1 results |
|
||||
| stage_2_result | JSON | Stage 2 results |
|
||||
| stage_3_result | JSON | Stage 3 results |
|
||||
| stage_4_result | JSON | Stage 4 results |
|
||||
| stage_5_result | JSON | Stage 5 results |
|
||||
| stage_6_result | JSON | Stage 6 results |
|
||||
| stage_7_result | JSON | Stage 7 results |
|
||||
| error_message | TextField | Error details (nullable) |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/automation/config/` | Get/create config | Get automation config |
|
||||
| PUT | `/api/v1/automation/update_config/` | Update config | Update settings |
|
||||
| POST | `/api/v1/automation/run_now/` | Start manual run | Start automation |
|
||||
| GET | `/api/v1/automation/current_run/` | Get current run | Run status/progress |
|
||||
| GET | `/api/v1/automation/pipeline_overview/` | Get pipeline | Stage status counts |
|
||||
| GET | `/api/v1/automation/current_processing/` | Get processing | Live processing status |
|
||||
| **GET** | `/api/v1/automation/run_progress/` | **v1.3.0** | Unified progress data |
|
||||
| POST | `/api/v1/automation/pause/` | Pause run | Pause after current item |
|
||||
| POST | `/api/v1/automation/resume/` | Resume run | Resume from saved stage |
|
||||
| POST | `/api/v1/automation/cancel/` | Cancel run | Cancel after current item |
|
||||
| GET | `/api/v1/automation/history/` | Get history | Last 20 runs |
|
||||
| GET | `/api/v1/automation/logs/` | Get logs | Activity log for run |
|
||||
| GET | `/api/v1/automation/estimate/` | Get estimate | Credit estimate |
|
||||
| **GET** | `/api/v1/integration/settings/defaults/` | **v1.8.1** | Get default settings for reset |
|
||||
|
||||
**Query Parameters:** All require `?site_id=`, run-specific require `?run_id=`
|
||||
|
||||
### Default Settings Endpoint (v1.8.1)
|
||||
|
||||
Returns centralized defaults from `DefaultAutomationConfig`:
|
||||
```json
|
||||
{
|
||||
"automation": { "enabled": false, "frequency": "daily", "time": "02:00" },
|
||||
"stages": [
|
||||
{ "number": 1, "enabled": true, "batch_size": 50, "per_run_limit": 0, "use_testing": false, "budget_pct": 15 },
|
||||
...
|
||||
],
|
||||
"delays": { "within_stage": 3, "between_stage": 5 },
|
||||
"publishing": {
|
||||
"auto_approval_enabled": false,
|
||||
"auto_publish_enabled": false,
|
||||
"daily_publish_limit": 3,
|
||||
"publish_days": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"time_slots": ["09:00", "14:00", "18:00"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### run_progress Endpoint (v1.3.0)
|
||||
|
||||
Returns unified progress data for frontend:
|
||||
```json
|
||||
{
|
||||
"run": { "run_id": "...", "status": "running", "current_stage": 3 },
|
||||
"global_progress": { "total_items": 100, "completed_items": 45, "percentage": 45 },
|
||||
"stages": [
|
||||
{ "number": 1, "status": "completed", "input_count": 50, "processed_count": 50 },
|
||||
...
|
||||
],
|
||||
"metrics": { "credits_used": 120, "duration_seconds": 3600 },
|
||||
"initial_snapshot": { "stage_1_initial": 50, ... }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Flow
|
||||
|
||||
### Manual Run
|
||||
|
||||
1. User clicks "Run Now" on frontend
|
||||
2. Frontend calls `POST /automation/run_now/?site_id=X`
|
||||
3. Backend acquires cache lock `automation_lock_{site_id}`
|
||||
4. **v1.3.0:** Captures initial snapshot with `_capture_initial_snapshot()`
|
||||
5. Estimates credits required (1.2x buffer)
|
||||
6. Validates balance >= estimate
|
||||
7. Creates `AutomationRun` record
|
||||
8. Enqueues `run_automation_task` Celery task
|
||||
8. Returns run ID immediately
|
||||
|
||||
### Stage Execution
|
||||
|
||||
For each stage (1-7):
|
||||
|
||||
1. Check `_check_should_stop()` (paused/cancelled?)
|
||||
2. Load items for processing
|
||||
3. Process in batches (respecting batch_size)
|
||||
4. For AI stages: Call AIEngine function
|
||||
5. Wait `within_stage_delay` between batches
|
||||
6. Save stage result JSON
|
||||
7. Wait `between_stage_delay` before next stage
|
||||
|
||||
### Stage Result Fields
|
||||
|
||||
**Stage 1 (Clustering):**
|
||||
```json
|
||||
{
|
||||
"keywords_processed": 150,
|
||||
"clusters_created": 12,
|
||||
"batches_run": 3,
|
||||
"credits_used": 45,
|
||||
"time_elapsed": 120,
|
||||
"skipped": false,
|
||||
"partial": false
|
||||
}
|
||||
```
|
||||
|
||||
**Stage 2 (Ideas):**
|
||||
```json
|
||||
{
|
||||
"clusters_processed": 12,
|
||||
"ideas_created": 36,
|
||||
"batches_run": 2,
|
||||
"credits_used": 72
|
||||
}
|
||||
```
|
||||
|
||||
**Stage 3 (Tasks):**
|
||||
```json
|
||||
{
|
||||
"ideas_processed": 36,
|
||||
"tasks_created": 36,
|
||||
"batches_run": 4
|
||||
}
|
||||
```
|
||||
|
||||
**Stage 4 (Content):**
|
||||
```json
|
||||
{
|
||||
"tasks_processed": 36,
|
||||
"content_created": 36,
|
||||
"total_words": 54000,
|
||||
"batches_run": 6,
|
||||
"credits_used": 540
|
||||
}
|
||||
```
|
||||
|
||||
**Stage 5 (Image Prompts):**
|
||||
```json
|
||||
{
|
||||
"content_processed": 36,
|
||||
"prompts_created": 180,
|
||||
"batches_run": 4,
|
||||
"credits_used": 36
|
||||
}
|
||||
```
|
||||
|
||||
**Stage 6 (Images):**
|
||||
```json
|
||||
{
|
||||
"images_processed": 180,
|
||||
"images_generated": 180,
|
||||
"batches_run": 18
|
||||
}
|
||||
```
|
||||
|
||||
**Stage 7 (Review):**
|
||||
```json
|
||||
{
|
||||
"ready_for_review": 36
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Settings Configuration (v1.8.0)
|
||||
|
||||
**Location:** Site Settings → Automation tab
|
||||
**API:** `GET/PATCH /api/v1/integration/sites/{site_id}/unified-settings/`
|
||||
|
||||
> ⚠️ **v1.8.0 Change:** Settings are now consolidated in Site Settings. The previous standalone `/automation/settings` page has been removed.
|
||||
|
||||
### Settings UI Sections
|
||||
|
||||
1. **Schedule & Frequency Card**
|
||||
- Enable/disable toggle
|
||||
- Frequency (hourly/daily/weekly)
|
||||
- Days of week selection
|
||||
- Time slot selection
|
||||
- Next run display
|
||||
|
||||
2. **Capacity Card**
|
||||
- Total items per run (calculated)
|
||||
- Stage-by-stage breakdown
|
||||
|
||||
3. **AI Configuration Card**
|
||||
- Testing model selection (is_testing=true)
|
||||
- Live model selection (is_testing=false)
|
||||
- Image model selection
|
||||
|
||||
4. **Stage Configuration (Matrix)**
|
||||
- Per-stage Enable/Disable toggle
|
||||
- Per-stage batch size
|
||||
- Per-stage max items per run (**new in v1.8.0**)
|
||||
|
||||
5. **Help Cards**
|
||||
- Pipeline flow visualization
|
||||
- Stage descriptions
|
||||
|
||||
---
|
||||
|
||||
## Scheduling
|
||||
|
||||
**Celery Beat Task:** `check_scheduled_automations`
|
||||
**Frequency:** Hourly
|
||||
|
||||
**Logic:**
|
||||
1. Find configs where `enabled=True`
|
||||
2. Check if `next_run_at <= now`
|
||||
3. Check if no active run exists
|
||||
4. Start `run_automation_task` for eligible configs
|
||||
5. Update `next_run_at` based on frequency
|
||||
|
||||
---
|
||||
|
||||
## Lock Mechanism
|
||||
|
||||
**Purpose:** Prevent concurrent runs for same site
|
||||
|
||||
**Key:** `automation_lock_{site_id}`
|
||||
**Storage:** Redis cache
|
||||
**Acquired:** On run start
|
||||
**Released:** On completion/failure/cancel
|
||||
|
||||
---
|
||||
|
||||
## Credit Validation
|
||||
|
||||
Before starting:
|
||||
1. Calculate estimated credits for all stages
|
||||
2. Apply 1.2x safety buffer
|
||||
3. Compare with account balance
|
||||
4. Reject if balance < estimate
|
||||
|
||||
During execution:
|
||||
- Each AI stage checks credits before processing
|
||||
- Deductions happen after successful AI calls
|
||||
- `total_credits_used` accumulates across stages
|
||||
|
||||
---
|
||||
|
||||
## Frontend Integration
|
||||
|
||||
### AutomationOverview (v1.8.0)
|
||||
|
||||
**Path:** `/automation/overview`
|
||||
**Purpose:** Run history dashboard with:
|
||||
- All automation runs with status
|
||||
- Filter by status, date range
|
||||
- Click to view detailed run information
|
||||
|
||||
### AutomationRunDetail (v1.8.0)
|
||||
|
||||
**Path:** `/automation/runs/:runId`
|
||||
**Purpose:** Detailed view of individual run with:
|
||||
- Stage-by-stage progress
|
||||
- Items processed per stage
|
||||
- Errors and logs
|
||||
|
||||
### AutomationPage
|
||||
|
||||
**Path:** `/automation`
|
||||
**Purpose:** Manual run control with:
|
||||
- **Pipeline Cards:** Stage-by-stage status with pending counts
|
||||
- **Processing Card:** Live processing status during run
|
||||
- **Control Buttons:** Run Now, Pause, Resume, Cancel
|
||||
- **Activity Log:** Real-time log streaming
|
||||
|
||||
### Polling
|
||||
|
||||
- Every ~5s while run is running/paused
|
||||
- Fetches: `current_run`, `pipeline_overview`, `current_processing`
|
||||
- Lighter polling when idle
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| "Already running" error | Lock exists from previous run | Wait or check if stuck |
|
||||
| Insufficient credits | Balance < 1.2x estimate | Add credits |
|
||||
| Stage skipped | No items to process | Check previous stages |
|
||||
| Run stuck | Worker crashed | Clear lock, restart |
|
||||
| Images not generating | Stage 5 didn't create prompts | Check stage 5 result |
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
| Version | Changes |
|
||||
|---------|---------|
|
||||
| v1.8.0 | Unified settings in Site Settings, per-run limits, skip stage functionality |
|
||||
| v1.7.0 | AutomationOverview dashboard, AutomationRunDetail page |
|
||||
| v1.3.2 | Stage 7 publishing scheduler integration |
|
||||
| v1.3.0 | Progress tracking with initial snapshots |
|
||||
@@ -0,0 +1,756 @@
|
||||
# IGNY8 Billing & Payments - Complete Reference
|
||||
|
||||
> **Last Updated:** January 20, 2026
|
||||
> **Version:** 2.0 (Two-Pool Credit System)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [System Overview](#1-system-overview)
|
||||
2. [Credit System](#2-credit-system)
|
||||
3. [Payment Methods](#3-payment-methods)
|
||||
4. [Subscription Lifecycle](#4-subscription-lifecycle)
|
||||
5. [Credit Packages](#5-credit-packages)
|
||||
6. [Invoice System](#6-invoice-system)
|
||||
7. [Renewal Workflow](#7-renewal-workflow)
|
||||
8. [Admin Operations](#8-admin-operations)
|
||||
9. [API Reference](#9-api-reference)
|
||||
10. [Database Schema](#10-database-schema)
|
||||
|
||||
---
|
||||
|
||||
## 1. System Overview
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ IGNY8 BILLING SYSTEM │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ STRIPE │ │ PAYPAL │ │ BANK TRANSFER│ │
|
||||
│ │ (Card/Intl) │ │ (Intl) │ │ (PK Only) │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ PAYMENT GATEWAY LAYER │ │
|
||||
│ │ • Webhook Processing • Payment Verification • Logging │ │
|
||||
│ └─────────────────────────────┬───────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ INVOICE SERVICE │ │
|
||||
│ │ • Create Invoice • Update Status • PDF Generation │ │
|
||||
│ └─────────────────────────────┬───────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ CREDIT SERVICE │ │
|
||||
│ │ • Plan Credits • Bonus Credits • Deduction • Reset │ │
|
||||
│ └─────────────────────────────┬───────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ ACCOUNT │ │
|
||||
│ │ credits (plan) │ bonus_credits (purchased) │ status │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Files
|
||||
|
||||
| Component | File Path |
|
||||
|-----------|-----------|
|
||||
| Credit Service | `business/billing/services/credit_service.py` |
|
||||
| Invoice Service | `business/billing/services/invoice_service.py` |
|
||||
| Payment Service | `business/billing/services/payment_service.py` |
|
||||
| Email Service | `business/billing/services/email_service.py` |
|
||||
| Stripe Webhooks | `business/billing/views/stripe_views.py` |
|
||||
| PayPal Webhooks | `business/billing/views/paypal_views.py` |
|
||||
| Subscription Renewal | `business/billing/tasks/subscription_renewal.py` |
|
||||
| Invoice Lifecycle | `business/billing/tasks/invoice_lifecycle.py` |
|
||||
| Billing Admin | `modules/billing/admin.py` |
|
||||
| Billing Models | `business/billing/models.py` |
|
||||
|
||||
---
|
||||
|
||||
## 2. Credit System
|
||||
|
||||
### Two-Pool Credit Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ACCOUNT CREDITS │
|
||||
├────────────────────────────┬────────────────────────────────┤
|
||||
│ PLAN CREDITS │ BONUS CREDITS │
|
||||
│ (account.credits) │ (account.bonus_credits) │
|
||||
├────────────────────────────┼────────────────────────────────┤
|
||||
│ • From subscription plan │ • From credit packages │
|
||||
│ • Resets on renewal │ • NEVER expire │
|
||||
│ • Used FIRST │ • Used SECOND (after plan=0) │
|
||||
│ • Max = plan.included_ │ • No maximum limit │
|
||||
│ credits │ │
|
||||
└────────────────────────────┴────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Credit Deduction Flow
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ CREDIT REQUEST │
|
||||
│ (e.g., 50) │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ Check Total │
|
||||
│ credits + bonus │
|
||||
│ >= requested? │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
┌──────────────┴──────────────┐
|
||||
│ NO │ YES
|
||||
▼ ▼
|
||||
┌────────────────┐ ┌────────────────────┐
|
||||
│ INSUFFICIENT │ │ Plan Credits >= 50?│
|
||||
│ Return False │ └─────────┬──────────┘
|
||||
└────────────────┘ │
|
||||
┌──────────┴──────────┐
|
||||
│ YES │ NO
|
||||
▼ ▼
|
||||
┌───────────────┐ ┌────────────────────┐
|
||||
│ Deduct from │ │ Deduct plan credits│
|
||||
│ plan credits │ │ to 0, remainder │
|
||||
│ only │ │ from bonus_credits │
|
||||
└───────────────┘ └────────────────────┘
|
||||
```
|
||||
|
||||
> **Note:** The two-pool logic is internal to `CreditService.deduct_credits()`. All AI functions and other credit consumers are unaffected - they call the same credit check/deduct methods as before.
|
||||
|
||||
### Credit Operations
|
||||
|
||||
| Operation | Method | Affects | Description |
|
||||
|-----------|--------|---------|-------------|
|
||||
| Add Plan Credits | `add_credits()` | `credits` | Initial subscription, manual adjustment |
|
||||
| Add Bonus Credits | `add_bonus_credits()` | `bonus_credits` | Credit package purchases |
|
||||
| Deduct Credits | `deduct_credits()` | Both pools | AI operations, uses plan first |
|
||||
| Reset on Renewal | `reset_credits_for_renewal()` | `credits` only | Sets plan credits to new amount |
|
||||
| Check Balance | `check_credits()` | Read-only | Returns total available |
|
||||
|
||||
### Credit Transaction Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `subscription` | Plan credits from subscription |
|
||||
| `purchase` | Bonus credits from credit package |
|
||||
| `usage` | Credit consumption for AI operations |
|
||||
| `refund` | Credits returned due to failed operation |
|
||||
| `manual` | Admin adjustment |
|
||||
| `renewal` | Reset during subscription renewal |
|
||||
| `bonus` | Promotional bonus credits |
|
||||
|
||||
---
|
||||
|
||||
## 3. Payment Methods
|
||||
|
||||
### Payment Method by Country
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PAYMENT METHOD MATRIX │
|
||||
├─────────────────┬───────────────────────────────────────────┤
|
||||
│ COUNTRY │ AVAILABLE METHODS │
|
||||
├─────────────────┼───────────────────────────────────────────┤
|
||||
│ Pakistan (PK) │ ✅ Bank Transfer ✅ Stripe (Card) │
|
||||
│ │ ❌ PayPal (not available) │
|
||||
├─────────────────┼───────────────────────────────────────────┤
|
||||
│ Other (Intl) │ ✅ Stripe (Card) ✅ PayPal │
|
||||
│ │ ❌ Bank Transfer (not available) │
|
||||
└─────────────────┴───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Payment Flow by Method
|
||||
|
||||
#### Stripe (Card) Flow
|
||||
|
||||
```
|
||||
┌─────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐
|
||||
│ User │────▶│ Frontend │────▶│ Create │────▶│ Stripe │
|
||||
│ Clicks │ │ Checkout │ │ Checkout │ │ Hosted │
|
||||
│ Pay │ │ │ │ Session │ │ Page │
|
||||
└─────────┘ └──────────┘ └─────────────┘ └────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌─────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐
|
||||
│ Account │◀────│ Credit │◀────│ Invoice │◀────│ Webhook │
|
||||
│ Active │ │ Added │ │ Paid │ │ Received │
|
||||
└─────────┘ └──────────┘ └─────────────┘ └──────────┘
|
||||
```
|
||||
|
||||
#### PayPal Flow
|
||||
|
||||
```
|
||||
┌─────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐
|
||||
│ User │────▶│ Frontend │────▶│ Create │────▶│ PayPal │
|
||||
│ Clicks │ │ Checkout │ │ Order/Sub │ │ Hosted │
|
||||
│ Pay │ │ │ │ │ │ Page │
|
||||
└─────────┘ └──────────┘ └─────────────┘ └────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌─────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐
|
||||
│ Account │◀────│ Credit │◀────│ Invoice │◀────│ Webhook │
|
||||
│ Active │ │ Added │ │ Paid │ │ CAPTURE │
|
||||
└─────────┘ └──────────┘ └─────────────┘ └──────────┘
|
||||
```
|
||||
|
||||
#### Bank Transfer Flow (PK Only)
|
||||
|
||||
```
|
||||
┌─────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐
|
||||
│ User │────▶│ View │────▶│ Manual │────▶│ Upload │
|
||||
│ Selects │ │ Bank │ │ Transfer │ │ Proof │
|
||||
│ Bank │ │ Details │ │ to Bank │ │ │
|
||||
└─────────┘ └──────────┘ └─────────────┘ └────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌─────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐
|
||||
│ Account │◀────│ Credit │◀────│ Admin │◀────│ Payment │
|
||||
│ Active │ │ Added │ │ Approves │ │ Created │
|
||||
└─────────┘ └──────────┘ └─────────────┘ └──────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Subscription Lifecycle
|
||||
|
||||
### Subscription States
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ SUBSCRIPTION STATE MACHINE │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────┐
|
||||
│ NEW │
|
||||
│ SIGNUP │
|
||||
└────┬────┘
|
||||
│ Create subscription + invoice
|
||||
▼
|
||||
┌─────────────┐ Payment Failed ┌─────────────┐
|
||||
│ PENDING │─────────────────────────▶│ FAILED │
|
||||
│ (awaiting │ │ │
|
||||
│ payment) │ └─────────────┘
|
||||
└──────┬──────┘
|
||||
│ Payment Success
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ ACTIVE │◀─────────────────────────────────────┐
|
||||
│ │ Renewal Payment Success │
|
||||
└──────┬──────┘ │
|
||||
│ Renewal Date │
|
||||
▼ │
|
||||
┌─────────────┐ Payment Within ┌───────────┴─┐
|
||||
│ PENDING │ Grace Period │ │
|
||||
│ RENEWAL │────────────────────────▶│ ACTIVE │
|
||||
│ │ │ (renewed) │
|
||||
└──────┬──────┘ └─────────────┘
|
||||
│ Grace Period Expired (7 days)
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ EXPIRED │ Manual Reactivation
|
||||
│ │────────────────────────▶ Back to PENDING
|
||||
└─────────────┘
|
||||
|
||||
┌─────────────┐
|
||||
│ CANCELLED │ User-initiated cancellation
|
||||
│ │ (end of current period)
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
### Subscription Status Reference
|
||||
|
||||
| Status | Credits Access | Can Use Features | Next Action |
|
||||
|--------|----------------|------------------|-------------|
|
||||
| `pending` | ❌ No | ❌ No | Complete payment |
|
||||
| `active` | ✅ Yes | ✅ Yes | None (auto-renews) |
|
||||
| `pending_renewal` | ✅ Yes (24h) | ✅ Yes | Pay invoice |
|
||||
| `expired` | ❌ No | ❌ Limited | Resubscribe |
|
||||
| `cancelled` | ✅ Until end | ✅ Until end | None |
|
||||
| `failed` | ❌ No | ❌ No | Retry payment |
|
||||
|
||||
---
|
||||
|
||||
## 5. Credit Packages
|
||||
|
||||
### Available Packages
|
||||
|
||||
| Package | Credits | USD Price | PKR Price | Per Credit |
|
||||
|---------|---------|-----------|-----------|------------|
|
||||
| Starter | 500 | $50.00 | ≈ PKR 14,000 | $0.10 |
|
||||
| Growth | 2,000 | $200.00 | ≈ PKR 56,000 | $0.10 |
|
||||
| Scale | 5,000 | $300.00 | ≈ PKR 83,000 | $0.06 |
|
||||
| Enterprise | 20,000 | $1,200.00 | ≈ PKR 334,000 | $0.06 |
|
||||
|
||||
### Credit Package Purchase Flow
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────────────┐
|
||||
│ CREDIT PACKAGE PURCHASE FLOW │
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
User selects package
|
||||
│
|
||||
▼
|
||||
┌───────────────────┐
|
||||
│ Create Invoice │
|
||||
│ type='credit_ │
|
||||
│ package' │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
|
||||
│ Stripe/PayPal? │────▶│ Auto-process via │────▶│ Webhook confirms │
|
||||
│ │ │ payment gateway │ │ payment success │
|
||||
└─────────┬─────────┘ └───────────────────┘ └─────────┬─────────┘
|
||||
│ │
|
||||
│ Bank Transfer? │
|
||||
▼ │
|
||||
┌───────────────────┐ ┌───────────────────┐ │
|
||||
│ Payment created │────▶│ Admin reviews & │ │
|
||||
│ status='pending_ │ │ approves payment │ │
|
||||
│ approval' │ └─────────┬─────────┘ │
|
||||
└───────────────────┘ │ │
|
||||
▼ ▼
|
||||
┌───────────────────────────────────────┐
|
||||
│ PAYMENT APPROVED │
|
||||
└─────────────────┬─────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────┐
|
||||
│ CreditService.add_bonus_credits() │
|
||||
│ • Adds to account.bonus_credits │
|
||||
│ • Creates CreditTransaction │
|
||||
│ • NEVER expires │
|
||||
└───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Credit Package Invoice Lifecycle
|
||||
|
||||
```
|
||||
Invoice Created ──▶ 48 hours ──▶ Reminder Sent ──▶ 48 hours ──▶ Invoice Voided
|
||||
│ │ │
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
status='pending' status='pending' status='void'
|
||||
(reminder sent) (auto-cancelled)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Invoice System
|
||||
|
||||
### Invoice Types
|
||||
|
||||
| Type | Description | Auto-Pay | Manual Pay |
|
||||
|------|-------------|----------|------------|
|
||||
| `subscription` | Monthly plan payment | Stripe/PayPal | Bank Transfer |
|
||||
| `credit_package` | One-time credit purchase | Stripe/PayPal | Bank Transfer |
|
||||
| `addon` | Additional features | Stripe/PayPal | Bank Transfer |
|
||||
| `custom` | Manual/admin-created | ❌ | ✅ |
|
||||
|
||||
### Invoice Status Flow
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ DRAFT │────▶│ SENT │────▶│ PENDING │────▶│ PAID │
|
||||
└─────────┘ └─────────┘ └────┬────┘ └─────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ OVERDUE │ │ FAILED │
|
||||
└────┬─────┘ └──────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ VOID │ │CANCELLED │
|
||||
└──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
### Invoice Status Reference
|
||||
|
||||
| Status | Description | User Action |
|
||||
|--------|-------------|-------------|
|
||||
| `draft` | Being created | - |
|
||||
| `sent` | Delivered to user | Pay Now |
|
||||
| `pending` | Awaiting payment | Pay Now |
|
||||
| `overdue` | Past due date | Pay Now (urgent) |
|
||||
| `paid` | Payment received | Download PDF |
|
||||
| `failed` | Payment failed | Retry/Pay Now |
|
||||
| `void` | Cancelled by system | - |
|
||||
| `cancelled` | Cancelled by admin | - |
|
||||
|
||||
---
|
||||
|
||||
## 7. Renewal Workflow
|
||||
|
||||
### Renewal Timeline by Payment Method
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ RENEWAL TIMELINE - STRIPE/PAYPAL (AUTO-PAY) │
|
||||
│ Industry Standard: No Advance Notice │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Day 0 (Renewal) Day +1 Day +7 │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────┐ ┌────────────┐ ┌──────────┐ │
|
||||
│ │Auto-Pay │ │ If Failed: │ │ Expired │ │
|
||||
│ │ Attempt │ │ Retry + │ │ (after │ │
|
||||
│ └────┬─────┘ │ Email Sent │ │ retries) │ │
|
||||
│ │ └────────────┘ └──────────┘ │
|
||||
│ ┌────┴────┐ │
|
||||
│ │ SUCCESS │──▶ Receipt email + Credits reset to plan amount │
|
||||
│ └────┬────┘ │
|
||||
│ │ │
|
||||
│ ┌────┴────┐ │
|
||||
│ │ FAILURE │──▶ Payment failed email, Stripe retries 4x over 7 days │
|
||||
│ └─────────┘ │
|
||||
│ │
|
||||
│ EMAIL SCHEDULE (Industry Standard - Netflix, Spotify, Adobe): │
|
||||
│ • Payment Success: Receipt immediately │
|
||||
│ • Payment Failed: Notification after each retry attempt │
|
||||
│ • Final Warning: 1 day before account suspension │
|
||||
│ • Account Suspended: When grace period ends │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ RENEWAL TIMELINE - BANK TRANSFER │
|
||||
│ Simplified 3-Email Flow │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Day -3 Day 0 Day +1 Day +7 │
|
||||
│ │ │ │ │ │
|
||||
│ ▼ ▼ ▼ ▼ │
|
||||
│ ┌────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │Invoice │ │Reminder │ │ Urgent │ │ Expired │ │
|
||||
│ │Created │ │ Email │ │Reminder │ │ │ │
|
||||
│ │+Email │ │(if not │ │+Credits │ │ │ │
|
||||
│ │ │ │ paid) │ │ Reset │ │ │ │
|
||||
│ └────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||
│ │
|
||||
│ Timeline: │
|
||||
│ • Day -3: Invoice created + Email sent with payment instructions │
|
||||
│ • Day 0: Reminder email (renewal day, if still unpaid) │
|
||||
│ • Day +1: Urgent reminder + credits reset to 0 (if unpaid) │
|
||||
│ • Day +7: Subscription expired │
|
||||
│ │
|
||||
│ EMAILS: │
|
||||
│ 1. Invoice Email (Day -3): Invoice attached, bank details, Pay Now link │
|
||||
│ 2. Renewal Reminder (Day 0): "Your subscription renews today" │
|
||||
│ 3. Urgent Reminder (Day +1): "Payment overdue - action required" │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Renewal Credit Behavior
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ CREDIT RESET ON RENEWAL │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ SCENARIO 1: Payment Before Renewal │
|
||||
│ ───────────────────────────────────── │
|
||||
│ • Credits NOT reset early │
|
||||
│ • On payment confirmation: │
|
||||
│ - plan credits → reset to plan.included_credits │
|
||||
│ - bonus_credits → UNCHANGED (never affected) │
|
||||
│ │
|
||||
│ SCENARIO 2: Payment After Renewal (within 24h) │
|
||||
│ ────────────────────────────────────────────── │
|
||||
│ • Credits stay unchanged for 24 hours │
|
||||
│ • On payment confirmation: │
|
||||
│ - plan credits → reset to plan.included_credits │
|
||||
│ - bonus_credits → UNCHANGED │
|
||||
│ │
|
||||
│ SCENARIO 3: No Payment After 24 Hours │
|
||||
│ ───────────────────────────────────── │
|
||||
│ • plan credits → reset to 0 (task: send_day_after_reminders) │
|
||||
│ • bonus_credits → UNCHANGED (user can still use these) │
|
||||
│ • Warning email sent │
|
||||
│ │
|
||||
│ SCENARIO 4: Payment After Credit Reset │
|
||||
│ ────────────────────────────────────── │
|
||||
│ • On payment confirmation: │
|
||||
│ - plan credits → reset to plan.included_credits (restored) │
|
||||
│ - bonus_credits → UNCHANGED │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Celery Tasks Schedule
|
||||
|
||||
| Task | Schedule | Purpose |
|
||||
|------|----------|---------|
|
||||
| `create_bank_transfer_invoices` | Daily 09:00 | Create invoices 3 days before renewal (bank transfer only) |
|
||||
| `process_subscription_renewals` | Daily 00:05 | Process auto-pay renewals (Stripe/PayPal) |
|
||||
| `send_renewal_day_reminders` | Daily 10:00 | Send Day 0 reminder for bank transfer (if unpaid) |
|
||||
| `send_day_after_reminders` | Daily 09:15 | Send Day +1 urgent reminders + reset credits |
|
||||
| `check_expired_renewals` | Daily 00:15 | Mark subscriptions expired after 7-day grace period |
|
||||
| `send_credit_invoice_expiry_reminders` | Daily 09:30 | Remind about expiring credit package invoices |
|
||||
| `void_expired_credit_invoices` | Daily 00:45 | Auto-void credit invoices after 48h |
|
||||
|
||||
### Email Schedule Summary
|
||||
|
||||
**Stripe/PayPal (Auto-Pay) - Industry Standard:**
|
||||
| Event | Email |
|
||||
|-------|-------|
|
||||
| Payment Success | ✅ Receipt/Confirmation |
|
||||
| Payment Failed | ⚠️ Retry notification (per attempt) |
|
||||
| Final Warning | 🚨 1 day before suspension |
|
||||
| Account Suspended | ❌ Subscription expired |
|
||||
|
||||
**Bank Transfer (Manual Pay):**
|
||||
| Day | Email |
|
||||
|-----|-------|
|
||||
| Day -3 | 📧 Invoice created + payment instructions |
|
||||
| Day 0 | ⏰ Renewal day reminder (if unpaid) |
|
||||
| Day +1 | 🚨 Urgent reminder + credits reset warning |
|
||||
|
||||
---
|
||||
|
||||
## 8. Admin Operations
|
||||
|
||||
### Webhook Events (Payment Logs)
|
||||
|
||||
**Location:** Admin → Billing → Webhook Events
|
||||
|
||||
| Column | Description |
|
||||
|--------|-------------|
|
||||
| Event ID | Unique ID from Stripe/PayPal |
|
||||
| Provider | STRIPE or PAYPAL badge |
|
||||
| Event Type | e.g., `checkout.session.completed`, `PAYMENT.CAPTURE.COMPLETED` |
|
||||
| Status | Processed ✓, Failed ✗, Pending ⏳ |
|
||||
| Process Time | Actual processing duration |
|
||||
| Created | When webhook received |
|
||||
|
||||
**Actions:**
|
||||
- Mark as processed
|
||||
- Retry processing
|
||||
- View full payload (JSON)
|
||||
|
||||
### Payment Approval (Bank Transfer)
|
||||
|
||||
**Location:** Admin → Billing → Payments
|
||||
|
||||
**Approval Flow:**
|
||||
1. Filter by `status = pending_approval`
|
||||
2. Review `manual_reference` and `manual_notes`
|
||||
3. Check proof of payment upload
|
||||
4. Change status to `succeeded`
|
||||
5. System automatically:
|
||||
- Updates invoice to `paid`
|
||||
- Activates account (if subscription)
|
||||
- Adds credits (plan or bonus based on invoice type)
|
||||
|
||||
### Manual Credit Adjustment
|
||||
|
||||
**Location:** Admin → Billing → Credit Transactions
|
||||
|
||||
**To add credits manually:**
|
||||
1. Go to Account admin
|
||||
2. Edit the account
|
||||
3. Modify `credits` (plan) or `bonus_credits` (purchased)
|
||||
4. Save with note in admin_notes
|
||||
|
||||
**OR use shell:**
|
||||
```python
|
||||
from igny8_core.business.billing.services.credit_service import CreditService
|
||||
|
||||
# Add plan credits
|
||||
CreditService.add_credits(
|
||||
account=account,
|
||||
amount=500,
|
||||
transaction_type='manual',
|
||||
description='Manual adjustment - support ticket #123'
|
||||
)
|
||||
|
||||
# Add bonus credits
|
||||
CreditService.add_bonus_credits(
|
||||
account=account,
|
||||
amount=500,
|
||||
description='Promotional bonus - January 2026'
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. API Reference
|
||||
|
||||
### Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/api/v1/billing/credits/` | GET | Get credit balance |
|
||||
| `/api/v1/billing/credits/usage/` | GET | Get usage statistics |
|
||||
| `/api/v1/billing/invoices/` | GET | List invoices |
|
||||
| `/api/v1/billing/invoices/{id}/` | GET | Invoice detail |
|
||||
| `/api/v1/billing/invoices/{id}/pdf/` | GET | Download invoice PDF |
|
||||
| `/api/v1/billing/payments/` | GET | List payments |
|
||||
| `/api/v1/billing/plans/` | GET | List available plans |
|
||||
| `/api/v1/billing/subscriptions/` | GET | List subscriptions |
|
||||
| `/api/v1/billing/credit-packages/` | GET | List credit packages |
|
||||
| `/api/v1/billing/purchase/credits/` | POST | Purchase credit package |
|
||||
| `/api/v1/billing/subscribe/` | POST | Subscribe to plan |
|
||||
| `/api/v1/webhooks/stripe/` | POST | Stripe webhook endpoint |
|
||||
| `/api/v1/webhooks/paypal/` | POST | PayPal webhook endpoint |
|
||||
|
||||
### Credit Balance Response
|
||||
|
||||
```json
|
||||
{
|
||||
"credits": 3500,
|
||||
"bonus_credits": 2000,
|
||||
"total_credits": 5500,
|
||||
"credits_used_this_month": 1500,
|
||||
"plan_credits_per_month": 5000,
|
||||
"subscription_plan": "Scale",
|
||||
"period_end": "2026-02-12T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Database Schema
|
||||
|
||||
### Core Tables
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ accounts │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ id │ PK │
|
||||
│ name │ Account name │
|
||||
│ status │ pending, active, expired, etc. │
|
||||
│ credits │ Plan credits (resets on renewal) │
|
||||
│ bonus_credits │ Purchased credits (never expire) │
|
||||
│ plan_id │ FK → plans │
|
||||
│ billing_email │ Email for invoices │
|
||||
│ billing_country │ Country code (PK, US, etc.) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ subscriptions │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ id │ PK │
|
||||
│ account_id │ FK → accounts │
|
||||
│ plan_id │ FK → plans │
|
||||
│ status │ pending, active, pending_renewal, expired, etc. │
|
||||
│ current_period_start │ Start of current billing period │
|
||||
│ current_period_end │ End of current billing period (renewal date) │
|
||||
│ metadata │ JSON (stripe_subscription_id, paypal_sub_id) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ invoices │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ id │ PK │
|
||||
│ invoice_number │ Unique invoice number (INV-2026-00001) │
|
||||
│ account_id │ FK → accounts │
|
||||
│ subscription_id │ FK → subscriptions (nullable) │
|
||||
│ invoice_type │ subscription, credit_package, addon, custom │
|
||||
│ status │ draft, sent, pending, paid, overdue, void, etc. │
|
||||
│ total_amount │ Total amount │
|
||||
│ currency │ USD, PKR │
|
||||
│ due_date │ Payment due date │
|
||||
│ paid_at │ When payment received │
|
||||
│ metadata │ JSON (credit_package_id, etc.) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ payments │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ id │ PK │
|
||||
│ invoice_id │ FK → invoices │
|
||||
│ account_id │ FK → accounts │
|
||||
│ amount │ Payment amount │
|
||||
│ currency │ USD, PKR │
|
||||
│ payment_method │ stripe, paypal, bank_transfer │
|
||||
│ status │ pending_approval, processing, succeeded, failed│
|
||||
│ stripe_payment_intent_id│ Stripe reference │
|
||||
│ paypal_order_id │ PayPal reference │
|
||||
│ manual_reference │ Bank transfer reference │
|
||||
│ approved_by_id │ FK → users (admin who approved) │
|
||||
│ approved_at │ When approved │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ credit_transactions │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ id │ PK │
|
||||
│ account_id │ FK → accounts │
|
||||
│ transaction_type │ subscription, purchase, usage, refund, manual, etc. │
|
||||
│ amount │ Credits added (+) or deducted (-) │
|
||||
│ balance_after │ Balance after this transaction │
|
||||
│ description │ Human-readable description │
|
||||
│ metadata │ JSON (invoice_id, payment_id, etc.) │
|
||||
│ created_at │ Timestamp │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ webhook_events │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ id │ PK │
|
||||
│ event_id │ Unique event ID from provider │
|
||||
│ provider │ stripe, paypal │
|
||||
│ event_type │ checkout.session.completed, PAYMENT.CAPTURE.COMPLETED │
|
||||
│ payload │ JSON - full webhook payload │
|
||||
│ processed │ Boolean - successfully processed? │
|
||||
│ processed_at │ When processed │
|
||||
│ error_message│ Error if processing failed │
|
||||
│ retry_count │ Number of retry attempts │
|
||||
│ created_at │ When received │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### Credit Consumption Priority
|
||||
1. **Plan credits** used first
|
||||
2. **Bonus credits** used only when plan credits = 0
|
||||
|
||||
### What Happens on Renewal
|
||||
|
||||
| Event | Plan Credits | Bonus Credits |
|
||||
|-------|--------------|---------------|
|
||||
| Payment success | Reset to plan amount | No change |
|
||||
| No payment (24h) | Reset to 0 | No change |
|
||||
| Late payment | Reset to plan amount | No change |
|
||||
|
||||
### Payment Method Availability
|
||||
|
||||
| Country | Stripe | PayPal | Bank Transfer |
|
||||
|---------|--------|--------|---------------|
|
||||
| Pakistan (PK) | ✅ | ❌ | ✅ |
|
||||
| Others | ✅ | ✅ | ❌ |
|
||||
|
||||
### Invoice Expiry
|
||||
|
||||
| Invoice Type | Expiry |
|
||||
|--------------|--------|
|
||||
| Subscription | 7 days (grace period) |
|
||||
| Credit Package | 48 hours |
|
||||
|
||||
---
|
||||
|
||||
*Document generated from current codebase implementation as of January 20, 2026*
|
||||
384
v2/Live Docs on Server/igny8-app-docs/10-MODULES/BILLING.md
Normal file
384
v2/Live Docs on Server/igny8-app-docs/10-MODULES/BILLING.md
Normal file
@@ -0,0 +1,384 @@
|
||||
# Billing Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Status:** ✅ Active (Two-Pool Credit System v2.0 January 2026)
|
||||
**Backend Path:** `backend/igny8_core/modules/billing/` + `backend/igny8_core/business/billing/`
|
||||
**Frontend Path:** `frontend/src/pages/Billing/` + `frontend/src/pages/Account/`
|
||||
|
||||
> **Complete Billing Reference:** For comprehensive billing & payment documentation, see [BILLING-PAYMENTS-COMPLETE.md](BILLING-PAYMENTS-COMPLETE.md)
|
||||
> **Payment System Reference:** For payment gateway documentation (Stripe, PayPal, Bank Transfer), see [PAYMENT-SYSTEM.md](../90-REFERENCE/PAYMENT-SYSTEM.md)
|
||||
|
||||
---
|
||||
|
||||
## Two-Pool Credit System (v2.0)
|
||||
|
||||
| Pool | Field | Source | Behavior |
|
||||
|------|-------|--------|----------|
|
||||
| **Plan Credits** | `account.credits` | Subscription plan | Reset on renewal, reset to 0 if unpaid after 24h |
|
||||
| **Bonus Credits** | `account.bonus_credits` | Credit packages | **NEVER expire**, **NEVER reset** |
|
||||
|
||||
### Credit Usage Priority
|
||||
1. **Plan credits** used FIRST
|
||||
2. **Bonus credits** only used when plan credits = 0
|
||||
|
||||
### What Happens on Renewal
|
||||
| Event | Plan Credits | Bonus Credits |
|
||||
|-------|--------------|---------------|
|
||||
| Payment success | Reset to plan amount | No change |
|
||||
| No payment (24h) | Reset to 0 | No change |
|
||||
| Late payment | Reset to plan amount | No change |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Models | `business/billing/models.py` | `CreditTransaction`, `CreditUsageLog`, `CreditCostConfig`, `AIModelConfig` |
|
||||
| Service | `business/billing/services/credit_service.py` | `CreditService` |
|
||||
| Limit Service | `business/billing/services/limit_service.py` | `LimitService` (4 limits only) |
|
||||
| Views | `modules/billing/views.py` | `CreditBalanceViewSet`, `CreditUsageViewSet` |
|
||||
| Frontend | `pages/Account/PlansAndBillingPage.tsx` | Plans, credits, billing history |
|
||||
| Store | `store/billingStore.ts` | `useBillingStore` |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Billing module manages:
|
||||
- Credit balance and transactions
|
||||
- AI model pricing and credit configuration (v1.4.0)
|
||||
- Usage tracking with 4 simplified limits (v1.5.0)
|
||||
- Plan enforcement
|
||||
- Payment processing
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### AIModelConfig (NEW v1.4.0)
|
||||
|
||||
Single Source of Truth for all AI models with pricing.
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| model_name | CharField(100) | Model identifier (gpt-4o-mini, dall-e-3) |
|
||||
| model_type | CharField(20) | text / image |
|
||||
| provider | CharField(50) | openai, anthropic, runware |
|
||||
| display_name | CharField(200) | Human-readable name |
|
||||
| is_default | BooleanField | One default per type |
|
||||
| is_active | BooleanField | Enable/disable |
|
||||
| cost_per_1k_input | DecimalField | USD cost per 1K input tokens (text) |
|
||||
| cost_per_1k_output | DecimalField | USD cost per 1K output tokens (text) |
|
||||
| tokens_per_credit | IntegerField | Text: tokens per 1 credit (e.g., 1000) |
|
||||
| credits_per_image | IntegerField | Image: credits per image (1, 5, 15) |
|
||||
| quality_tier | CharField(20) | basic / quality / premium |
|
||||
| max_tokens | IntegerField | Model token limit |
|
||||
| context_window | IntegerField | Model context size |
|
||||
| capabilities | JSONField | vision, function_calling, etc. |
|
||||
|
||||
**Credit Examples:**
|
||||
|
||||
| Model | tokens_per_credit | credits_per_image | quality_tier |
|
||||
|-------|-------------------|-------------------|--------------|
|
||||
| gpt-4o | 1000 | - | - |
|
||||
| gpt-4o-mini | 10000 | - | - |
|
||||
| runware:97@1 | - | 1 | basic |
|
||||
| dall-e-3 | - | 5 | quality |
|
||||
| google:4@2 | - | 15 | premium |
|
||||
|
||||
### CreditTransaction (Ledger)
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| transaction_type | CharField | purchase/subscription/refund/deduction/adjustment |
|
||||
| amount | Decimal | Positive (add) or negative (deduct) |
|
||||
| balance_after | Decimal | Balance after transaction |
|
||||
| description | CharField | Transaction description |
|
||||
| metadata | JSON | Additional data |
|
||||
| payment | FK(Payment) | Payment that triggered this (v1.4.0) |
|
||||
| reference_id | CharField | DEPRECATED: Use payment FK |
|
||||
| created_at | DateTime | Transaction time |
|
||||
|
||||
### CreditUsageLog (Analytics)
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| operation_type | CharField | clustering/idea_generation/content_generation/image_generation |
|
||||
| credits_used | Decimal | Credits consumed |
|
||||
| cost_usd | Decimal | USD cost |
|
||||
| model_used | CharField | AI model used |
|
||||
| tokens_in | Integer | Input tokens |
|
||||
| tokens_out | Integer | Output tokens |
|
||||
| content_type | FK | Related content type |
|
||||
| object_id | Integer | Related object ID |
|
||||
| metadata | JSON | Additional data |
|
||||
| created_at | DateTime | Usage time |
|
||||
|
||||
### CreditCostConfig (Updated v1.4.0)
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| operation_type | CharField(50) PK | Unique operation ID |
|
||||
| display_name | CharField(100) | Human-readable name |
|
||||
| base_credits | IntegerField | Fixed credits per operation |
|
||||
| is_active | BooleanField | Enable/disable |
|
||||
| description | TextField | Admin notes |
|
||||
|
||||
**Note:** `tokens_per_credit` moved to AIModelConfig in v1.4.0.
|
||||
|
||||
---
|
||||
|
||||
## Credit Calculation Methods
|
||||
|
||||
### Method 1: Token-Based (Text Models) - Updated v1.4.0
|
||||
|
||||
Used for: Clustering, Ideas, Content Generation, Optimization
|
||||
|
||||
**Flow:**
|
||||
1. AI call completes
|
||||
2. OpenAI returns actual token usage
|
||||
3. Calculate: `total_tokens = input_tokens + output_tokens`
|
||||
4. Look up `AIModelConfig.tokens_per_credit` for the model used
|
||||
5. Calculate: `credits = ceil(total_tokens / tokens_per_credit)`
|
||||
6. Deduct from balance
|
||||
|
||||
**Example:**
|
||||
- Model: `gpt-4o-mini` (tokens_per_credit = 10000)
|
||||
- Tokens used: 15000
|
||||
- Credits: `ceil(15000 / 10000)` = 2 credits
|
||||
|
||||
### Method 2: Fixed Cost (Image Models) - Updated v1.4.0
|
||||
|
||||
Used for: Image Generation
|
||||
|
||||
**Flow:**
|
||||
1. User selects quality tier (basic/quality/premium)
|
||||
2. Look up `AIModelConfig.credits_per_image` for selected tier
|
||||
3. Check balance sufficient: `balance >= num_images * credits_per_image`
|
||||
4. Deduct credits
|
||||
5. Make AI call
|
||||
|
||||
**Example:**
|
||||
- Quality Tier: "quality" (dall-e-3, credits_per_image = 5)
|
||||
- Images: 3
|
||||
- Credits: 3 × 5 = 15 credits
|
||||
2. Calculate: `credits = num_images * cost_per_image`
|
||||
3. Check balance sufficient
|
||||
4. Deduct credits
|
||||
5. Then make AI call
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/billing/balance/` | `CreditBalanceViewSet.list` | Current balance + monthly usage |
|
||||
| GET | `/api/v1/billing/usage/` | `CreditUsageViewSet.list` | Usage log (paginated) |
|
||||
| GET | `/api/v1/billing/usage/summary/` | `CreditUsageViewSet.summary` | Aggregated by operation |
|
||||
| GET | `/api/v1/billing/usage/limits/` | `CreditUsageViewSet.limits` | Plan limits + current usage |
|
||||
| GET | `/api/v1/billing/transactions/` | `TransactionViewSet.list` | Transaction history |
|
||||
|
||||
---
|
||||
|
||||
## Credit Service API
|
||||
|
||||
### Check Credits
|
||||
|
||||
```python
|
||||
CreditService.check_credits(account, required_credits)
|
||||
# Raises InsufficientCreditsError if balance < required
|
||||
```
|
||||
|
||||
### Deduct Credits
|
||||
|
||||
```python
|
||||
CreditService.deduct_credits_for_operation(
|
||||
account=account,
|
||||
operation_type='content_generation',
|
||||
amount=word_count, # For per_word operations
|
||||
model='gpt-4o-mini',
|
||||
tokens_in=2500,
|
||||
tokens_out=1500,
|
||||
metadata={'content_id': 123}
|
||||
)
|
||||
```
|
||||
|
||||
### Add Credits
|
||||
|
||||
```python
|
||||
CreditService.add_credits(
|
||||
account=account,
|
||||
amount=1000,
|
||||
transaction_type='purchase',
|
||||
description='Credit package purchase'
|
||||
)
|
||||
```
|
||||
|
||||
### Calculate Credits for Images (NEW v1.4.0)
|
||||
|
||||
```python
|
||||
# Calculate credits needed for image generation based on model
|
||||
credits = CreditService.calculate_credits_for_image(
|
||||
model_name='dall-e-3', # Model with credits_per_image = 5
|
||||
num_images=3
|
||||
)
|
||||
# Returns: 15 (3 images × 5 credits)
|
||||
```
|
||||
|
||||
### Check Credits for Image Generation (NEW v1.7.1)
|
||||
|
||||
```python
|
||||
# Pre-check credits before image generation starts
|
||||
# Raises InsufficientCreditsError if not enough credits
|
||||
required = CreditService.check_credits_for_image(
|
||||
account=account,
|
||||
model_name='dall-e-3', # Model with credits_per_image = 5
|
||||
num_images=3
|
||||
)
|
||||
# Returns: 15 (required credits) if sufficient, raises exception if not
|
||||
```
|
||||
|
||||
### Deduct Credits for Image Generation (v1.7.1 Verified)
|
||||
|
||||
```python
|
||||
# Called after each successful image generation
|
||||
credits_deducted = CreditService.deduct_credits_for_image(
|
||||
account=account,
|
||||
model_name='dall-e-3',
|
||||
num_images=1,
|
||||
description='Image generation: Article Title',
|
||||
metadata={'image_id': 123, 'content_id': 456},
|
||||
cost_usd=0.04,
|
||||
related_object_type='image',
|
||||
related_object_id=123
|
||||
)
|
||||
# Logs to CreditTransaction and CreditUsageLog
|
||||
```
|
||||
|
||||
### Calculate Credits from Tokens by Model (NEW v1.4.0)
|
||||
|
||||
```python
|
||||
# Calculate credits from token usage based on model's tokens_per_credit
|
||||
credits = CreditService.calculate_credits_from_tokens_by_model(
|
||||
model_name='gpt-4o-mini', # Model with tokens_per_credit = 10000
|
||||
total_tokens=15000
|
||||
)
|
||||
# Returns: 2 (ceil(15000 / 10000))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Plan Limits
|
||||
|
||||
### Hard Limits (Never Reset)
|
||||
|
||||
| Limit | Field | Description |
|
||||
|-------|-------|-------------|
|
||||
| Sites | `max_sites` | Maximum sites per account |
|
||||
| Users | `max_users` | Maximum team members |
|
||||
| Keywords | `max_keywords` | Total keywords allowed |
|
||||
|
||||
### Monthly Limits (Reset on Billing Cycle)
|
||||
|
||||
| Limit | Field | Description |
|
||||
|-------|-------|-------------|
|
||||
| Ahrefs Queries | `max_ahrefs_queries` | Live Ahrefs API queries per month |
|
||||
|
||||
**Note:** As of January 2026, the limit system was simplified from 10+ limits to just 4. Credits handle all AI operation costs (content generation, image generation, clustering, etc.) instead of separate per-operation limits.
|
||||
|
||||
---
|
||||
|
||||
## Usage Limits Panel
|
||||
|
||||
**Component:** `UsageLimitsPanel.tsx`
|
||||
|
||||
Displays:
|
||||
- Progress bars for 4 limits only (Sites, Users, Keywords, Ahrefs Queries)
|
||||
- Color coding: blue (safe), yellow (warning), red (critical)
|
||||
- Days until reset for monthly limits (Ahrefs Queries)
|
||||
- Upgrade CTA when approaching limits
|
||||
|
||||
---
|
||||
|
||||
## Credit Costs Reference (Updated v1.4.0)
|
||||
|
||||
**Text Model Credits** (from `AIModelConfig.tokens_per_credit`):
|
||||
|
||||
| Model | tokens_per_credit | Cost/Credit | Notes |
|
||||
|-------|-------------------|-------------|-------|
|
||||
| gpt-4o | 1000 | ~$0.015 | High quality, lower throughput |
|
||||
| gpt-4o-mini | 10000 | ~$0.001 | Fast, cost-effective |
|
||||
| gpt-4.5-preview | 500 | ~$0.05 | Highest quality |
|
||||
|
||||
**Image Model Credits** (from `AIModelConfig.credits_per_image`):
|
||||
|
||||
| Quality Tier | credits_per_image | Model Example | Notes |
|
||||
|--------------|-------------------|---------------|-------|
|
||||
| Basic | 1 | runware:97@1 | Fast generation |
|
||||
| Quality | 5 | dall-e-3 | Balanced |
|
||||
| Premium | 15 | google:4@2 | Best quality |
|
||||
|
||||
**Operation Base Costs** (from `CreditCostConfig.base_credits`):
|
||||
|
||||
| Operation | Base Credits | Notes |
|
||||
|-----------|--------------|-------|
|
||||
| Clustering | 10 | Per clustering request |
|
||||
| Idea Generation | 2 | Per idea generated |
|
||||
| Content Optimization | 5 | Per optimization run |
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages
|
||||
|
||||
### Plans & Billing (`/account/plans`)
|
||||
|
||||
**Tabs:**
|
||||
1. **Current Plan** - Active plan details, renewal date, "View Usage" link
|
||||
2. **Upgrade Plan** - Pricing table with plan comparison
|
||||
3. **Billing History** - Invoices and payment history
|
||||
|
||||
### Usage Analytics (`/account/usage`)
|
||||
|
||||
**Tabs:**
|
||||
1. **Limits & Usage** - Plan limits with progress bars (4 limits only)
|
||||
2. **Credit History** - Credit transaction history
|
||||
3. **Credit Insights** - Charts: credits by type, daily timeline, operations breakdown
|
||||
4. **Activity Log** - API call statistics and operation details
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
| From | To | Trigger |
|
||||
|------|----|---------|
|
||||
| AIEngine | CreditService | Pre-check and post-deduct |
|
||||
| Automation | CreditService | Per-stage credit tracking |
|
||||
| Subscription | Account | Credit allocation |
|
||||
| Admin | CreditCostConfig | Price adjustments |
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| 402 error | Insufficient credits | Add credits or reduce operation |
|
||||
| Usage not showing | Log not created | Check CreditService called |
|
||||
| Limits wrong | Cache stale | Clear cache, reload |
|
||||
| Monthly usage high | Automation running | Pause automation |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| ~~AI Model Config database~~ | ✅ v1.4.0 | Model pricing moved to AIModelConfig |
|
||||
| ~~Image model quality tiers~~ | ✅ v1.4.0 | credits_per_image per quality tier |
|
||||
| ~~Credit service enhancements~~ | ✅ v1.7.1 | Model-specific calculation methods |
|
||||
| ~~Image generation credit check~~ | ✅ v1.7.1 | Pre-generation credit verification |
|
||||
| ~~Image generation logging~~ | ✅ v1.7.1 | AITaskLog + notifications for images |
|
||||
368
v2/Live Docs on Server/igny8-app-docs/10-MODULES/INTEGRATIONS.md
Normal file
368
v2/Live Docs on Server/igny8-app-docs/10-MODULES/INTEGRATIONS.md
Normal file
@@ -0,0 +1,368 @@
|
||||
# Integrations Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Version:** 1.8.4
|
||||
**Status:** ✅ Active
|
||||
**Backend Path:** `backend/igny8_core/modules/integration/` + `backend/igny8_core/business/integration/`
|
||||
**Frontend Path:** `frontend/src/pages/Settings/IntegrationSettings.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Models | `business/integration/models.py` | `SiteIntegration`, `SyncEvent` |
|
||||
| Views | `modules/integration/views.py` | `SiteIntegrationViewSet` |
|
||||
| Webhooks | `modules/integration/webhooks.py` | `wordpress_webhook` |
|
||||
| Services | `business/integration/services/*.py` | Sync services |
|
||||
| AI Core | `ai/ai_core.py` | OpenAI, Anthropic, Runware, Bria clients |
|
||||
| Model Registry | `ai/model_registry.py` | Centralized model configs |
|
||||
| Frontend | `pages/Settings/IntegrationSettings.tsx` | Integration UI |
|
||||
|
||||
---
|
||||
|
||||
## AI Provider Integrations (v1.3.0)
|
||||
|
||||
### Supported Providers
|
||||
|
||||
| Provider | Type | Models | API Key Field |
|
||||
|----------|------|--------|---------------|
|
||||
| OpenAI | Text/Image | gpt-4o, gpt-4o-mini, dall-e-3 | `openai_api_key` |
|
||||
| Anthropic | Text | claude-3-5-sonnet, claude-3-opus, claude-3-haiku | `anthropic_api_key` |
|
||||
| Runware | Image | runware-v2 | `runware_api_key` |
|
||||
| Bria AI | Image | bria-2.3, bria-2.3-fast, bria-2.2 | `bria_api_key` |
|
||||
|
||||
### GlobalIntegrationSettings
|
||||
|
||||
Located in `modules/system/global_settings_models.py`:
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| openai_api_key | CharField | OpenAI API key |
|
||||
| anthropic_api_key | CharField | Anthropic API key (v1.3.0) |
|
||||
| runware_api_key | CharField | Runware API key |
|
||||
| bria_api_key | CharField | Bria AI API key (v1.3.0) |
|
||||
| default_text_model | CharField | Default text model |
|
||||
| default_image_model | CharField | Default image model |
|
||||
| default_image_provider | CharField | openai/runware/bria |
|
||||
|
||||
### Model Registry
|
||||
|
||||
Centralized model configuration with caching:
|
||||
|
||||
```python
|
||||
from igny8_core.ai.model_registry import ModelRegistry
|
||||
|
||||
# Get model config
|
||||
model = ModelRegistry.get_model('gpt-4o-mini')
|
||||
|
||||
# Calculate cost
|
||||
cost = ModelRegistry.calculate_cost('gpt-4o-mini', input_tokens=1000, output_tokens=500)
|
||||
|
||||
# List all models
|
||||
models = ModelRegistry.list_models(model_type='text', provider='openai')
|
||||
```
|
||||
|
||||
### Migrations
|
||||
|
||||
- `0012_add_bria_integration.py` - Adds Bria AI integration settings
|
||||
- `0013_add_anthropic_integration.py` - Adds Anthropic integration settings
|
||||
- `0009_seed_ai_model_configs.py` - Seeds model configurations
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Integrations module manages:
|
||||
- AI provider connections (OpenAI, Anthropic, Runware, Bria)
|
||||
- WordPress site connections
|
||||
- Two-way content synchronization
|
||||
- Webhook handling
|
||||
- External platform integrations
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### Authentication Note
|
||||
|
||||
**⚠️ Important:** For WordPress integrations, `Site.wp_api_key` is the **SINGLE source of truth** for API authentication, NOT SiteIntegration fields. The SiteIntegration model is used for sync tracking and multi-platform support (future: Shopify).
|
||||
|
||||
### SiteIntegration
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | IGNY8 site |
|
||||
| platform | CharField | wordpress/shopify |
|
||||
| external_site_url | URLField | External site URL |
|
||||
| is_active | Boolean | Enable/disable |
|
||||
| sync_enabled | Boolean | Enable auto-sync |
|
||||
| last_sync_at | DateTime | Last sync time |
|
||||
| sync_status | CharField | pending/syncing/completed/error |
|
||||
| sync_error | TextField | Sync error message |
|
||||
| connection_status | CharField | connected/error |
|
||||
| config_json | JSON | Platform-specific configuration |
|
||||
| credentials_json | JSON | (Reserved for future platforms, NOT used for WordPress) |
|
||||
| created_at | DateTime | Creation date |
|
||||
|
||||
### SyncEvent
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| integration | FK | Parent integration |
|
||||
| event_type | CharField | content_push/content_pull/webhook |
|
||||
| direction | CharField | igny8_to_wp/wp_to_igny8 |
|
||||
| content_type | CharField | post/page/product |
|
||||
| content_id | Integer | IGNY8 content ID |
|
||||
| external_id | Integer | WordPress post ID |
|
||||
| status | CharField | pending/success/failed |
|
||||
| error_message | TextField | Error details |
|
||||
| metadata | JSON | Additional data |
|
||||
| created_at | DateTime | Event time |
|
||||
|
||||
### PublishingSettings (v1.3.2)
|
||||
|
||||
Site-level publishing configuration. Used by the publishing scheduler.
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| site | OneToOneField | Parent site (unique per site) |
|
||||
| auto_approval_enabled | BooleanField | Auto-approve content (default: False) |
|
||||
| auto_publish_enabled | BooleanField | Auto-publish approved content (default: False) |
|
||||
| daily_publish_limit | IntegerField | Max publications per day (default: 5) |
|
||||
| weekly_publish_limit | IntegerField | Max per week (default: 20) |
|
||||
| monthly_publish_limit | IntegerField | Max per month (default: 60) |
|
||||
| publish_days | JSONField | Days for publishing ["mon","wed","fri"] |
|
||||
| publish_time_slots | JSONField | Time slots [{"start":"09:00","end":"17:00"}] |
|
||||
|
||||
**Helper Method:**
|
||||
```python
|
||||
# Get or create settings for a site
|
||||
settings, created = PublishingSettings.get_or_create_for_site(site)
|
||||
```
|
||||
|
||||
**Related:** See [PUBLISHER.md](PUBLISHER.md) for publishing scheduler details.
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/integration/` | `SiteIntegrationViewSet.list` | List integrations |
|
||||
| POST | `/api/v1/integration/` | `SiteIntegrationViewSet.create` | Create integration |
|
||||
| PUT | `/api/v1/integration/{id}/` | `SiteIntegrationViewSet.update` | Update integration |
|
||||
| DELETE | `/api/v1/integration/{id}/` | `SiteIntegrationViewSet.destroy` | Remove integration |
|
||||
| POST | `/api/v1/integration/{id}/test_connection/` | Test connection | Verify credentials |
|
||||
| POST | `/api/v1/integration/{id}/test_collection_connection/` | Test per-collection | Test specific content type |
|
||||
| POST | `/api/v1/integration/{id}/sync/` | Trigger sync | Start synchronization |
|
||||
| GET | `/api/v1/integration/{id}/sync_status/` | Get sync status | Current sync progress |
|
||||
| POST | `/api/v1/integration/{id}/update_structure/` | Update structure | Refresh site structure |
|
||||
| GET | `/api/v1/integration/{id}/content_types/` | Get content types | Available types + counts |
|
||||
| GET | `/api/v1/integration/{id}/sync_health/` | Get sync health | Sync statistics |
|
||||
| POST | `/api/v1/integration/site_sync/` | Site-level sync | Sync by site ID |
|
||||
|
||||
### Webhooks
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| POST | `/api/v1/integration/webhook/wordpress/` | `wordpress_webhook` | Receive WP updates |
|
||||
|
||||
---
|
||||
|
||||
## WordPress Integration
|
||||
|
||||
### Setup Flow
|
||||
|
||||
1. User enters WordPress site URL
|
||||
2. User creates Application Password in WordPress
|
||||
3. User enters credentials in IGNY8
|
||||
4. IGNY8 tests connection via WordPress REST API
|
||||
5. If successful, saves `SiteIntegration` record
|
||||
|
||||
### Required WordPress Setup
|
||||
|
||||
- WordPress 5.6+ (REST API enabled)
|
||||
- Application Passwords enabled
|
||||
- Pretty permalinks enabled
|
||||
- REST API accessible (no blocking plugins)
|
||||
|
||||
### Test Connection
|
||||
|
||||
**Endpoint:** `POST /integration/{id}/test_connection/`
|
||||
|
||||
**Flow:**
|
||||
1. Call WordPress `/wp-json/wp/v2/users/me`
|
||||
2. Verify authentication works
|
||||
3. Return user info and site capabilities
|
||||
|
||||
---
|
||||
|
||||
## Content Synchronization
|
||||
|
||||
### IGNY8 → WordPress (Push)
|
||||
|
||||
**Trigger:** User clicks "Publish to WordPress"
|
||||
|
||||
**Flow:**
|
||||
1. Content is in `approved` status
|
||||
2. Get `SiteIntegration` for content's site
|
||||
3. Build WordPress post payload:
|
||||
- title, content, excerpt, slug
|
||||
- status: `publish` or `draft`
|
||||
- categories, tags (mapped)
|
||||
- featured media (if image exists)
|
||||
4. Call WordPress REST API:
|
||||
- `POST /wp-json/wp/v2/posts` (new)
|
||||
- `PUT /wp-json/wp/v2/posts/{id}` (update)
|
||||
5. Store `wordpress_id` on Content
|
||||
6. Create `SyncEvent` record
|
||||
7. Update Content status to `published`
|
||||
|
||||
### WordPress → IGNY8 (Pull)
|
||||
|
||||
**Trigger:** Manual sync or webhook
|
||||
|
||||
**Flow:**
|
||||
1. Call WordPress `/wp-json/wp/v2/posts`
|
||||
2. For each post:
|
||||
- Check if exists in IGNY8 by `wordpress_id`
|
||||
- If exists: Update if modified
|
||||
- If new: Create Content record
|
||||
3. Sync taxonomies (categories, tags)
|
||||
4. Create `SyncEvent` records
|
||||
|
||||
### Webhook Handling
|
||||
|
||||
**WordPress Plugin** sends webhooks to IGNY8 when:
|
||||
- Post created/updated/deleted
|
||||
- Post status changed (draft → published)
|
||||
|
||||
**Webhook Payload:**
|
||||
```json
|
||||
{
|
||||
"action": "post_updated",
|
||||
"post_id": 123,
|
||||
"post_type": "post",
|
||||
"site_url": "https://example.com"
|
||||
}
|
||||
```
|
||||
|
||||
**IGNY8 Processing:**
|
||||
1. Validate webhook signature (optional)
|
||||
2. Find matching `SiteIntegration`
|
||||
3. Fetch full post from WordPress
|
||||
4. Update/create Content in IGNY8
|
||||
5. Create `SyncEvent` record
|
||||
|
||||
---
|
||||
|
||||
## Image Sync
|
||||
|
||||
### Push (IGNY8 → WordPress)
|
||||
|
||||
1. Image record has `image_url`
|
||||
2. Download image from URL
|
||||
3. Upload to WordPress Media Library
|
||||
4. Get WordPress attachment ID
|
||||
5. Set as featured image on post
|
||||
|
||||
### Pull (WordPress → IGNY8)
|
||||
|
||||
1. Get featured media ID from post
|
||||
2. Fetch media URL from WordPress
|
||||
3. Store URL in IGNY8 Images record
|
||||
|
||||
---
|
||||
|
||||
## Taxonomy Sync
|
||||
|
||||
### Categories
|
||||
|
||||
1. Get WordPress categories via `/wp-json/wp/v2/categories`
|
||||
2. Create `ContentTaxonomy` records (type=category)
|
||||
3. Store `wordpress_id` for mapping
|
||||
|
||||
### Tags
|
||||
|
||||
1. Get WordPress tags via `/wp-json/wp/v2/tags`
|
||||
2. Create `ContentTaxonomy` records (type=tag)
|
||||
3. Store `wordpress_id` for mapping
|
||||
|
||||
---
|
||||
|
||||
## Sync Status Tracking
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| idle | No sync in progress |
|
||||
| syncing | Sync currently running |
|
||||
| error | Last sync failed |
|
||||
|
||||
**Sync Health Metrics:**
|
||||
- Total synced posts
|
||||
- Last successful sync
|
||||
- Failed sync count
|
||||
- Pending items
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages
|
||||
|
||||
### Integration Settings (`/settings/integration`)
|
||||
|
||||
- Add new integration button
|
||||
- List of configured integrations
|
||||
- Status indicators (connected/error)
|
||||
- Test connection button
|
||||
- Sync now button
|
||||
- Sync status and history
|
||||
|
||||
### WordPress Sync Dashboard (`/sites/{id}/sync`)
|
||||
|
||||
- Detailed sync status
|
||||
- Content sync queue
|
||||
- Error log
|
||||
- Manual sync triggers
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Connection failed | Invalid credentials | Verify Application Password |
|
||||
| 401 Unauthorized | Password expired | Regenerate Application Password |
|
||||
| 403 Forbidden | REST API blocked | Check security plugins |
|
||||
| Images not syncing | CORS/URL issues | Verify image URLs accessible |
|
||||
| Categories missing | Not synced | Run sync_structure first |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Credential Storage
|
||||
|
||||
- API keys encrypted at rest
|
||||
- Never exposed in API responses
|
||||
- Application Passwords rotatable
|
||||
|
||||
### Webhook Security
|
||||
|
||||
- Optional signature verification
|
||||
- Site URL validation
|
||||
- Rate limiting on webhook endpoint
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Shopify integration | 🔜 Planned | E-commerce platform support |
|
||||
| Ghost integration | 🔜 Planned | Ghost CMS support |
|
||||
| Webflow integration | 🔜 Planned | Webflow CMS support |
|
||||
| Scheduled sync | 🔜 Planned | Automatic periodic sync |
|
||||
| Conflict resolution | 🔜 Planned | Handle edit conflicts |
|
||||
184
v2/Live Docs on Server/igny8-app-docs/10-MODULES/LINKER.md
Normal file
184
v2/Live Docs on Server/igny8-app-docs/10-MODULES/LINKER.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Linker Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Version:** 1.8.4
|
||||
**Status:** ⏸️ Inactive (Disabled by Default)
|
||||
**Backend Path:** `backend/igny8_core/modules/linker/`
|
||||
**Frontend Path:** `frontend/src/pages/Linker/`
|
||||
|
||||
---
|
||||
|
||||
## Module Status
|
||||
|
||||
| Aspect | Current State | Notes |
|
||||
|--------|---------------|-------|
|
||||
| Backend API | ✅ Implemented | Endpoints functional |
|
||||
| Frontend Pages | ✅ Implemented | UI exists |
|
||||
| Sidebar Nav | ⚠️ Conditional | Hidden when disabled |
|
||||
| Route Protection | ❌ Not Protected | Direct URL access still works |
|
||||
| Dashboard References | ❌ Not Hidden | May show in dashboard |
|
||||
| Default State | Disabled | `linker_enabled = True` in model, but typically disabled |
|
||||
|
||||
**⚠️ Pending Implementation:** Extend module disable to route-level protection and all page references.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Views | `modules/linker/views.py` | `LinkerViewSet` |
|
||||
| API | `modules/linker/urls.py` | Linker endpoints |
|
||||
| Frontend Pages | `pages/Linker/index.tsx` | Linker overview |
|
||||
| Frontend Pages | `pages/Linker/LinkerContent.tsx` | Content for linking |
|
||||
| API Client | `api/linker.api.ts` | `linkerApi` |
|
||||
| Module Control | `modules/system/settings_models.py` | `ModuleEnableSettings.linker_enabled` |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Linker module provides internal linking automation:
|
||||
- Identify link opportunities in content
|
||||
- Suggest relevant internal links
|
||||
- Auto-inject links into content body
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| POST | `/api/v1/linker/process/` | `LinkerViewSet.process` | Process single content for linking |
|
||||
| POST | `/api/v1/linker/batch_process/` | `LinkerViewSet.batch_process` | Process multiple content items |
|
||||
|
||||
### Process Request
|
||||
|
||||
```json
|
||||
{
|
||||
"content_id": 123,
|
||||
"max_links": 5,
|
||||
"anchor_strategy": "exact_match"
|
||||
}
|
||||
```
|
||||
|
||||
### Process Response
|
||||
|
||||
```json
|
||||
{
|
||||
"content_id": 123,
|
||||
"links_added": 3,
|
||||
"link_opportunities": [
|
||||
{
|
||||
"anchor_text": "SEO optimization",
|
||||
"target_url": "/blog/seo-guide/",
|
||||
"target_content_id": 456,
|
||||
"position": 234,
|
||||
"confidence": 0.85
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Business Logic
|
||||
|
||||
### Link Candidate Discovery
|
||||
|
||||
**Trigger:** User initiates linking process
|
||||
**Flow:**
|
||||
1. Load source content body
|
||||
2. Extract potential anchor phrases
|
||||
3. Find matching content by:
|
||||
- Keyword overlap
|
||||
- Title similarity
|
||||
- Same sector/industry
|
||||
4. Score candidates by relevance
|
||||
5. Filter to avoid over-linking
|
||||
|
||||
### Link Injection
|
||||
|
||||
**Trigger:** User approves link suggestions
|
||||
**Flow:**
|
||||
1. Locate anchor text in content
|
||||
2. Wrap in `<a>` tag with target URL
|
||||
3. Ensure no nested links
|
||||
4. Save updated content body
|
||||
|
||||
---
|
||||
|
||||
## Module Enable Control
|
||||
|
||||
### Backend Model
|
||||
|
||||
```python
|
||||
# modules/system/settings_models.py
|
||||
class ModuleEnableSettings(AccountBaseModel):
|
||||
linker_enabled = models.BooleanField(default=True)
|
||||
```
|
||||
|
||||
### Frontend Check
|
||||
|
||||
```typescript
|
||||
// layout/AppSidebar.tsx
|
||||
if (isModuleEnabled('linker')) {
|
||||
workflowItems.push({
|
||||
name: "Linker",
|
||||
path: "/linker/content",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Current Limitation
|
||||
|
||||
Direct URL access to `/linker/*` routes still works even when module is disabled.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages
|
||||
|
||||
### Linker Overview (`/linker`)
|
||||
|
||||
- Module overview
|
||||
- Quick stats
|
||||
- Start linking action
|
||||
|
||||
### Content for Linking (`/linker/content`)
|
||||
|
||||
- List of content available for linking
|
||||
- Link status indicators
|
||||
- Process button per content
|
||||
- Batch processing action
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
| From | To | Trigger |
|
||||
|------|----|---------|
|
||||
| Content | Linker | Manual process |
|
||||
| Linker | Content | Link injection |
|
||||
| Automation | Linker | Automated linking (future) |
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Module visible when disabled | Only sidebar hidden | Pending: route protection |
|
||||
| No link candidates | No matching content | Create more related content |
|
||||
| Links broken | Target content deleted | Validate target exists |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Route-level protection | 🔜 Pending | Block access when module disabled |
|
||||
| Dashboard card hiding | 🔜 Pending | Hide from dashboard when disabled |
|
||||
| Automation integration | 🔜 Planned | Add to automation pipeline |
|
||||
| Link density control | 🔜 Planned | Prevent over-linking |
|
||||
| External link support | 🔜 Planned | Add outbound links |
|
||||
@@ -0,0 +1,594 @@
|
||||
# Notifications Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Version:** 1.8.4
|
||||
**Status:** ✅ Active
|
||||
**Backend Path:** `backend/igny8_core/business/notifications/`
|
||||
**Frontend Path:** `frontend/src/store/notificationStore.ts`, `frontend/src/components/header/NotificationDropdown.tsx`, `frontend/src/pages/account/NotificationsPage.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Models | `business/notifications/models.py` | `Notification`, `NotificationPreference` |
|
||||
| Views | `business/notifications/views.py` | `NotificationViewSet` |
|
||||
| Serializers | `business/notifications/serializers.py` | `NotificationSerializer` |
|
||||
| Services | `business/notifications/services.py` | `NotificationService` |
|
||||
| Frontend Store | `store/notificationStore.ts` | Zustand notification store |
|
||||
| Frontend Dropdown | `components/header/NotificationDropdown.tsx` | Notification UI (header bell) |
|
||||
| Frontend Page | `pages/account/NotificationsPage.tsx` | Full notifications history page |
|
||||
| API Client | `services/notifications.api.ts` | API methods |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Notifications module provides real-time user notifications for AI operations, system events, and workflow milestones. Notifications appear in the header dropdown and persist in the database.
|
||||
|
||||
**Key Features:**
|
||||
- Real-time notifications for AI task completion/failure
|
||||
- Account-wide or user-specific notifications
|
||||
- Read/unread state tracking
|
||||
- Action buttons with navigation
|
||||
- Auto-fetch and auto-sync with API
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### Notification
|
||||
|
||||
| Field | Type | Currently Used? | Purpose |
|
||||
|-------|------|----------------|---------|
|
||||
| account | FK | ✅ **Yes** | Owner account (required for multi-tenancy) |
|
||||
| user | FK | ❌ **No** | User-specific notifications (nullable - if null, visible to all account users). **Currently unused** - all notifications are account-wide |
|
||||
| notification_type | CharField | ✅ **Yes** | Type from NotificationType choices (for filtering, icons) |
|
||||
| title | CharField(200) | ✅ **Yes** | Notification title shown in dropdown |
|
||||
| message | TextField | ✅ **Yes** | Notification body text shown in dropdown |
|
||||
| severity | CharField | ✅ **Yes** | info/success/warning/error (affects icon color) |
|
||||
| site | FK | ✅ **Partial** | Related site (nullable). Created but **not displayed** in current dropdown |
|
||||
| content_type | FK | ❌ **No** | Generic relation to any object. **Future:** Link notification to specific Content/Task/etc. for direct access |
|
||||
| object_id | PositiveInteger | ❌ **No** | Generic relation ID. **Future:** Used with content_type for object linking |
|
||||
| action_url | CharField(500) | ✅ **Yes** | Frontend route for action button (e.g., `/planner/clusters`) |
|
||||
| action_label | CharField(50) | ✅ **Yes** | Action button text (e.g., "View Clusters") |
|
||||
| is_read | Boolean | ✅ **Yes** | Read status (default: False). Used for unread badge count |
|
||||
| read_at | DateTime | ✅ **Partial** | When marked read (nullable). Created but **not displayed** in dropdown |
|
||||
| metadata | JSON | ✅ **Partial** | Additional data (counts, details). Stored but **not displayed** in dropdown |
|
||||
| created_at | DateTime | ✅ **Yes** | Creation timestamp. Shown as relative time ("2 minutes ago") |
|
||||
| updated_at | DateTime | ❌ **No** | Last update timestamp. **Currently unused** |
|
||||
|
||||
**Indexes:**
|
||||
- `(account, -created_at)` - List notifications by account ✅ **Used**
|
||||
- `(account, is_read, -created_at)` - Filter unread ✅ **Used**
|
||||
- `(user, -created_at)` - User-specific notifications ❌ **Unused** (all notifications are account-wide)
|
||||
|
||||
---
|
||||
|
||||
### Why So Many Unused Fields?
|
||||
|
||||
The model was designed with **future expansion** in mind, but the current "dropdown-only" implementation uses less than half of the fields. Here's why they exist:
|
||||
|
||||
**Over-Engineered for Current Use:**
|
||||
- `content_type` + `object_id` → For direct object linking (planned: click notification, go to that exact content item)
|
||||
- `user` → For user-specific notifications (planned: "@John, your content is ready")
|
||||
- `metadata` → For rich data (planned: show counts, progress, details in dedicated page)
|
||||
- `read_at` → For analytics (planned: "You read this 2 days ago")
|
||||
- `updated_at` → For editing notifications (planned: update notification instead of creating duplicate)
|
||||
|
||||
**Currently Essential:**
|
||||
- `account`, `title`, `message`, `severity`, `notification_type` → Core notification data
|
||||
- `action_url`, `action_label` → Navigation buttons
|
||||
- `is_read`, `created_at` → Dropdown functionality
|
||||
|
||||
**The Design Mismatch:**
|
||||
You're right - this is a **database schema designed for a full-featured notification system** (with dedicated page, filtering, search, history) but the frontend only implements a **simple dropdown**. The backend is over-built for the current use case.
|
||||
|
||||
---
|
||||
|
||||
## Notification Types
|
||||
|
||||
### AI Operations
|
||||
|
||||
| Type | Severity | Trigger | Message Format |
|
||||
|------|----------|---------|----------------|
|
||||
| `ai_cluster_complete` | success | Clustering finishes | "Created X clusters from Y keywords" |
|
||||
| `ai_cluster_failed` | error | Clustering fails | "Failed to cluster keywords: [error]" |
|
||||
| `ai_ideas_complete` | success | Idea generation finishes | "Generated X content ideas from Y clusters" |
|
||||
| `ai_ideas_failed` | error | Idea generation fails | "Failed to generate ideas: [error]" |
|
||||
| `ai_content_complete` | success | Content generation finishes | "Generated X articles (Y words)" |
|
||||
| `ai_content_failed` | error | Content generation fails | "Failed to generate content: [error]" |
|
||||
| `ai_images_complete` | success | Image generation finishes | "Generated X images" |
|
||||
| `ai_images_failed` | error | Image generation fails | "Failed to generate X images: [error]" |
|
||||
| `ai_prompts_complete` | success | Image prompts created | "X image prompts ready (1 featured + Y in-article)" |
|
||||
| `ai_prompts_failed` | error | Image prompt creation fails | "Failed to create image prompts: [error]" |
|
||||
|
||||
### Workflow Events
|
||||
|
||||
| Type | Severity | Trigger | Message Format |
|
||||
|------|----------|---------|----------------|
|
||||
| `content_ready_review` | info | Content moved to review | "[Title] ready for review" |
|
||||
| `content_published` | success | Content published | '"[Title]" published to [site]' |
|
||||
| `content_publish_failed` | error | Publishing fails | 'Failed to publish "[Title]": [error]' |
|
||||
|
||||
### WordPress Sync
|
||||
|
||||
| Type | Severity | Trigger | Message Format |
|
||||
|------|----------|---------|----------------|
|
||||
| `wordpress_sync_success` | success | Sync completes | "Synced X items with [site]" |
|
||||
| `wordpress_sync_failed` | error | Sync fails | "WordPress sync failed for [site]: [error]" |
|
||||
|
||||
### Credits & Billing
|
||||
|
||||
| Type | Severity | Trigger | Message Format |
|
||||
|------|----------|---------|----------------|
|
||||
| `credits_low` | warning | Credits < 20% | "You've used X% of your credits. Y remaining." |
|
||||
| `credits_depleted` | error | Credits exhausted | "Your credits are exhausted. Upgrade to continue." |
|
||||
|
||||
### Setup & System
|
||||
|
||||
| Type | Severity | Trigger | Message Format |
|
||||
|------|----------|---------|----------------|
|
||||
| `site_setup_complete` | success | All setup steps done | "[Site] is fully configured and ready!" |
|
||||
| `keywords_imported` | info | Keywords added | "Added X keywords to [site]" |
|
||||
| `system_info` | info | System messages | Custom message |
|
||||
|
||||
---
|
||||
|
||||
## Notification Triggers
|
||||
|
||||
### AI Task Completion (AIEngine)
|
||||
|
||||
**Location:** `backend/igny8_core/ai/engine.py`
|
||||
**Methods:** `_create_success_notification()`, `_create_failure_notification()`
|
||||
|
||||
**Flow:**
|
||||
1. AIEngine executes AI function (`auto_cluster`, `generate_ideas`, etc.)
|
||||
2. On **success** (after DONE phase):
|
||||
- Calls `_create_success_notification(function_name, save_result, payload)`
|
||||
- Maps function to NotificationService method
|
||||
- Creates notification with counts/metrics
|
||||
3. On **failure** (in `_handle_error()`):
|
||||
- Calls `_create_failure_notification(function_name, error)`
|
||||
- Creates error notification with error message
|
||||
|
||||
**Mapping:**
|
||||
```python
|
||||
auto_cluster → NotificationService.notify_clustering_complete/failed
|
||||
generate_ideas → NotificationService.notify_ideas_complete/failed
|
||||
generate_content → NotificationService.notify_content_complete/failed
|
||||
generate_image_prompts → NotificationService.notify_prompts_complete/failed
|
||||
generate_images → NotificationService.notify_images_complete/failed
|
||||
```
|
||||
|
||||
### WordPress Publishing
|
||||
|
||||
**Location:** `backend/igny8_core/tasks/wordpress_publishing.py`
|
||||
**Trigger:** After publishing content to WordPress
|
||||
|
||||
```python
|
||||
NotificationService.notify_content_published(
|
||||
account=account,
|
||||
site=site,
|
||||
title=content.title,
|
||||
content_object=content
|
||||
)
|
||||
```
|
||||
|
||||
### WordPress Sync
|
||||
|
||||
**Location:** `backend/igny8_core/tasks/wordpress_publishing.py`
|
||||
**Trigger:** After syncing posts from WordPress
|
||||
|
||||
```python
|
||||
NotificationService.notify_wordpress_sync_success(
|
||||
account=account,
|
||||
site=site,
|
||||
count=synced_count
|
||||
)
|
||||
```
|
||||
|
||||
### Credit Alerts
|
||||
|
||||
**Location:** `backend/igny8_core/business/billing/services/credit_service.py`
|
||||
**Trigger:** After deducting credits
|
||||
|
||||
```python
|
||||
# When credits drop below threshold
|
||||
NotificationService.notify_credits_low(
|
||||
account=account,
|
||||
percentage_used=80,
|
||||
credits_remaining=remaining
|
||||
)
|
||||
|
||||
# When credits exhausted
|
||||
NotificationService.notify_credits_depleted(account=account)
|
||||
```
|
||||
|
||||
### Manual Triggers (Optional)
|
||||
|
||||
Notifications can also be created manually from anywhere:
|
||||
|
||||
```python
|
||||
from igny8_core.business.notifications.services import NotificationService
|
||||
|
||||
NotificationService.notify_keywords_imported(
|
||||
account=account,
|
||||
site=site,
|
||||
count=keyword_count
|
||||
)
|
||||
```
|
||||
|
||||
**Note:** As of v1.2.1, the following actions **DO create notifications**:
|
||||
- ✅ AI task completion/failure (clustering, ideas, content, images, prompts)
|
||||
- ✅ Keyword import via "Add to Workflow" - **Fixed in v1.2.1**
|
||||
|
||||
**Actions that DON'T yet create notifications** (planned):
|
||||
- ❌ WordPress publishing (needs integration in wordpress_publishing.py)
|
||||
- ❌ WordPress sync (needs integration in wordpress_publishing.py)
|
||||
- ❌ Credit alerts (needs integration in credit_service.py)
|
||||
- ❌ Automation completion (planned for future)
|
||||
|
||||
---
|
||||
|
||||
## User Interface
|
||||
|
||||
### Notification Dropdown (Header)
|
||||
|
||||
**Location:** Header bell icon (top right)
|
||||
**File:** `frontend/src/components/header/NotificationDropdown.tsx`
|
||||
|
||||
**Features:**
|
||||
- Shows last 50 notifications
|
||||
- Animated badge with unread count
|
||||
- Click notification to mark read + navigate
|
||||
- "Mark all read" button
|
||||
- Auto-refreshes every 30 seconds
|
||||
- Refreshes when opened (if stale > 1 minute)
|
||||
- "View All Notifications" link to full page
|
||||
|
||||
### Notifications Page (Full History)
|
||||
|
||||
**Location:** `/account/notifications`
|
||||
**File:** `frontend/src/pages/account/NotificationsPage.tsx`
|
||||
**Access:** Sidebar → ACCOUNT → Notifications OR NotificationDropdown → "View All Notifications"
|
||||
|
||||
**Features:**
|
||||
- **Filters:**
|
||||
- Severity (info/success/warning/error)
|
||||
- Notification type (AI operations, WordPress sync, credits, etc.)
|
||||
- Read status (all/unread/read)
|
||||
- Site (filter by specific site)
|
||||
- Date range (from/to dates)
|
||||
- **Actions:**
|
||||
- Mark individual notifications as read
|
||||
- Mark all as read (bulk action)
|
||||
- Delete individual notifications
|
||||
- Click notification to navigate to action URL
|
||||
- **Display:**
|
||||
- Full notification history with pagination
|
||||
- Severity icons with color coding
|
||||
- Relative timestamps ("2 hours ago")
|
||||
- Site badge when applicable
|
||||
- Action buttons for related pages
|
||||
- Unread badge in sidebar menu
|
||||
|
||||
**v1.2.2 Implementation:**
|
||||
- ✅ Full-page notifications view created
|
||||
- ✅ Advanced filtering by severity, type, read status, site, date range
|
||||
- ✅ Bulk actions (mark all read)
|
||||
- ✅ Individual actions (mark read, delete)
|
||||
- ✅ Added to sidebar under ACCOUNT section
|
||||
- ✅ Unread count badge in sidebar
|
||||
- ✅ Fixed broken link in NotificationDropdown (was `/notifications`, now `/account/notifications`)
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### List Notifications
|
||||
|
||||
```http
|
||||
GET /api/v1/notifications/
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
- `page` - Page number (default: 1)
|
||||
- `page_size` - Results per page (default: 20)
|
||||
- `is_read` - Filter by read status (`true`/`false`)
|
||||
- `notification_type` - Filter by type (e.g., `ai_cluster_complete`)
|
||||
- `severity` - Filter by severity (`info`/`success`/`warning`/`error`)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"count": 42,
|
||||
"next": "/api/v1/notifications/?page=2",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 123,
|
||||
"notification_type": "ai_cluster_complete",
|
||||
"severity": "success",
|
||||
"title": "Clustering Complete",
|
||||
"message": "Created 5 clusters from 50 keywords",
|
||||
"is_read": false,
|
||||
"created_at": "2025-12-28T10:30:00Z",
|
||||
"read_at": null,
|
||||
"action_label": "View Clusters",
|
||||
"action_url": "/planner/clusters",
|
||||
"site": {"id": 1, "name": "My Blog"},
|
||||
"metadata": {
|
||||
"cluster_count": 5,
|
||||
"keyword_count": 50
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Get Unread Count
|
||||
|
||||
```http
|
||||
GET /api/v1/notifications/unread-count/
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"unread_count": 7
|
||||
}
|
||||
```
|
||||
|
||||
### Mark as Read
|
||||
|
||||
```http
|
||||
POST /api/v1/notifications/{id}/read/
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"is_read": true,
|
||||
"read_at": "2025-12-28T10:35:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Mark All as Read
|
||||
|
||||
```http
|
||||
POST /api/v1/notifications/read-all/
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"updated_count": 7,
|
||||
"message": "Marked 7 notifications as read"
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Notification
|
||||
|
||||
```http
|
||||
DELETE /api/v1/notifications/{id}/
|
||||
```
|
||||
|
||||
**Response:** `204 No Content`
|
||||
|
||||
---
|
||||
|
||||
## Frontend Implementation
|
||||
|
||||
### Notification Store
|
||||
|
||||
**File:** `frontend/src/store/notificationStore.ts`
|
||||
|
||||
**Features:**
|
||||
- Zustand store for state management
|
||||
- In-memory queue for optimistic UI updates
|
||||
- API sync for persistent notifications
|
||||
- Auto-fetch on mount and periodic sync (every 30 seconds)
|
||||
- Auto-refresh when dropdown opens (if stale > 1 minute)
|
||||
|
||||
**Key Methods:**
|
||||
```typescript
|
||||
// Add local notification (optimistic)
|
||||
addNotification(notification)
|
||||
|
||||
// Mark as read (optimistic + API sync)
|
||||
markAsRead(id)
|
||||
|
||||
// Mark all as read
|
||||
markAllAsRead()
|
||||
|
||||
// Fetch from API
|
||||
fetchNotifications()
|
||||
|
||||
// Sync unread count
|
||||
syncUnreadCount()
|
||||
```
|
||||
|
||||
### Notification Dropdown
|
||||
|
||||
**File:** `frontend/src/components/header/NotificationDropdown.tsx`
|
||||
|
||||
**Features:**
|
||||
- Badge with unread count (animated ping when > 0)
|
||||
- Dropdown with notification list
|
||||
- Click notification to mark read and navigate
|
||||
- "Mark all read" action
|
||||
- Empty state when no notifications
|
||||
- Auto-fetch on open if stale
|
||||
|
||||
**Icon Mapping:**
|
||||
```typescript
|
||||
auto_cluster → GroupIcon
|
||||
generate_ideas → BoltIcon
|
||||
generate_content → FileTextIcon
|
||||
generate_images → FileIcon
|
||||
system → AlertIcon
|
||||
success → CheckCircleIcon
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Business Logic
|
||||
|
||||
### Visibility Rules
|
||||
|
||||
1. **Account-wide notifications** (`user=NULL`):
|
||||
- Visible to ALL users in the account
|
||||
- Example: "Automation completed 10 tasks"
|
||||
|
||||
2. **User-specific notifications** (`user=User`):
|
||||
- Only visible to that specific user
|
||||
- Example: "Your content is ready for review"
|
||||
|
||||
3. **Site-filtered** (frontend):
|
||||
- Frontend can filter by site in UI
|
||||
- Backend always returns all account notifications
|
||||
|
||||
### Read Status
|
||||
|
||||
- Notifications start as `is_read=False`
|
||||
- Clicking notification marks it read (API call + optimistic update)
|
||||
- "Mark all read" bulk updates all unread notifications
|
||||
- Read notifications stay in dropdown (can be filtered out in future)
|
||||
|
||||
### Retention Policy
|
||||
|
||||
- Notifications never auto-delete (future: add retention policy)
|
||||
- Users can manually delete notifications
|
||||
- Admin can clean up old notifications via management command (future)
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Notifications not appearing | AIEngine not calling NotificationService | Fixed in v1.2.1 |
|
||||
| "Add to workflow" no notification | KeywordViewSet not calling NotificationService | Fixed in v1.2.1 |
|
||||
| Can't see notification history | No dedicated notifications page | Fixed in v1.2.2 - Page created at /account/notifications |
|
||||
| "View All" button → 404 | Link to `/notifications` but page doesn't exist | Fixed in v1.2.2 - Link updated to /account/notifications |
|
||||
| Duplicate notifications | Multiple AI task retries | Check task retry logic |
|
||||
| Missing notifications | Celery worker crashed | Check Celery logs |
|
||||
| Unread count wrong | Race condition in state | Refresh page or wait for sync |
|
||||
| Action URL not working | Incorrect route in action_url | Check NotificationService methods |
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
| From | To | Trigger |
|
||||
|------|----|---------|
|
||||
| AIEngine | NotificationService | AI task success/failure |
|
||||
| WordPress Publisher | NotificationService | Publishing/sync events |
|
||||
| Credit Service | NotificationService | Low credits/depleted |
|
||||
| Automation | NotificationService | Automation milestones (future) |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Notification preferences | 🔜 Planned | User can toggle notification types |
|
||||
| Email notifications | 🔜 Planned | Send email for critical notifications |
|
||||
| Push notifications | 🔜 Planned | Browser push for real-time alerts |
|
||||
| Notification retention | 🔜 Planned | Auto-delete after 30/60 days |
|
||||
| Notification categories | 🔜 Planned | Group by module (Planner, Writer, etc.) |
|
||||
| Notification sounds | 🔜 Planned | Audio alerts for important events |
|
||||
| Webhook notifications | 🔜 Planned | POST to external webhook URLs |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Changes |
|
||||
|---------|------|---------|
|
||||
| v1.2.2 | Dec 28, 2025 | **NEW:** Full notifications page at /account/notifications with filtering, bulk actions, and sidebar integration |
|
||||
| v1.2.1 | Dec 28, 2025 | **Fixed:** Notifications now created on AI task completion + keyword import |
|
||||
| v1.2.0 | Dec 27, 2025 | Initial notifications system implementation |
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Test Notification Creation
|
||||
|
||||
```python
|
||||
# In Django shell: docker exec -it igny8_backend python manage.py shell
|
||||
from igny8_core.business.notifications.services import NotificationService
|
||||
from igny8_core.auth.models import Account
|
||||
|
||||
account = Account.objects.first()
|
||||
|
||||
# Test clustering notification
|
||||
NotificationService.notify_clustering_complete(
|
||||
account=account,
|
||||
cluster_count=5,
|
||||
keyword_count=50
|
||||
)
|
||||
|
||||
# Test error notification
|
||||
NotificationService.notify_clustering_failed(
|
||||
account=account,
|
||||
error="Insufficient credits"
|
||||
)
|
||||
```
|
||||
|
||||
### Test Frontend
|
||||
|
||||
1. Run AI operation (clustering, idea generation, etc.)
|
||||
2. Check notification dropdown (bell icon in header)
|
||||
3. Verify unread badge appears
|
||||
4. Click notification to mark read and navigate
|
||||
5. Click "Mark all read" to clear badge
|
||||
|
||||
---
|
||||
|
||||
## Debug Commands
|
||||
|
||||
```bash
|
||||
# Check Celery logs for notification creation
|
||||
docker logs igny8_celery_worker -f | grep -i "notification"
|
||||
|
||||
# Check notifications in database
|
||||
docker exec -it igny8_backend python manage.py shell
|
||||
>>> from igny8_core.business.notifications.models import Notification
|
||||
>>> Notification.objects.count()
|
||||
>>> Notification.objects.filter(is_read=False).count()
|
||||
>>> Notification.objects.last().title
|
||||
|
||||
# Test notification API
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
http://localhost:8011/api/v1/notifications/
|
||||
|
||||
# Check notification creation code
|
||||
grep -r "notify_clustering_complete" backend/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
**Design Pattern:** Service Layer + Repository Pattern
|
||||
- `NotificationService` provides static methods for creating notifications
|
||||
- Each notification type has dedicated method with validation
|
||||
- `Notification.create_notification()` is class method for low-level creation
|
||||
- Views use `AccountModelViewSet` for automatic account filtering
|
||||
|
||||
**Why Not Signals?**
|
||||
- Explicit is better than implicit
|
||||
- Clear call sites for debugging
|
||||
- Easier to test and mock
|
||||
- No hidden side effects
|
||||
- Can pass context-specific data
|
||||
|
||||
**Lazy Imports:**
|
||||
- NotificationService uses lazy imports in AIEngine to avoid Django app loading issues
|
||||
- Import inside method, not at module level
|
||||
263
v2/Live Docs on Server/igny8-app-docs/10-MODULES/OPTIMIZER.md
Normal file
263
v2/Live Docs on Server/igny8-app-docs/10-MODULES/OPTIMIZER.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# Optimizer Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Version:** 1.8.4
|
||||
**Status:** ⏸️ Inactive (Disabled by Default)
|
||||
**Backend Path:** `backend/igny8_core/modules/optimizer/` + `backend/igny8_core/business/optimization/`
|
||||
**Frontend Path:** `frontend/src/pages/Optimizer/`
|
||||
|
||||
---
|
||||
|
||||
## Module Status
|
||||
|
||||
| Aspect | Current State | Notes |
|
||||
|--------|---------------|-------|
|
||||
| Backend API | ✅ Implemented | Endpoints functional |
|
||||
| Business Logic | ✅ Implemented | Optimization service exists |
|
||||
| Frontend Pages | ✅ Implemented | UI exists |
|
||||
| Sidebar Nav | ⚠️ Conditional | Hidden when disabled |
|
||||
| Route Protection | ❌ Not Protected | Direct URL access still works |
|
||||
| Dashboard References | ❌ Not Hidden | May show in dashboard |
|
||||
| Default State | Disabled | `optimizer_enabled = True` in model, but typically disabled |
|
||||
|
||||
**⚠️ Pending Implementation:** Extend module disable to route-level protection and all page references.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Views | `modules/optimizer/views.py` | `OptimizerViewSet` |
|
||||
| Models | `business/optimization/models.py` | `OptimizationTask` |
|
||||
| Services | `business/optimization/services/*.py` | Optimization logic |
|
||||
| AI Function | `ai/functions/optimize.py` | `OptimizeContentFunction` |
|
||||
| Frontend Pages | `pages/Optimizer/index.tsx` | Optimizer overview |
|
||||
| Frontend Pages | `pages/Optimizer/OptimizerContent.tsx` | Select content |
|
||||
| Frontend Pages | `pages/Optimizer/OptimizerPreview.tsx` | Analysis preview |
|
||||
| API Client | `api/optimizer.api.ts` | `optimizerApi` |
|
||||
| Module Control | `modules/system/settings_models.py` | `ModuleEnableSettings.optimizer_enabled` |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Optimizer module provides AI-powered content optimization:
|
||||
- Analyze existing content for SEO improvements
|
||||
- Suggest and apply optimizations
|
||||
- Track before/after scores
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### OptimizationTask
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| sector | FK | Parent sector |
|
||||
| content | FK | Target content |
|
||||
| entry_point | CharField | auto/igny8/wordpress/external/manual |
|
||||
| status | CharField | pending/analyzing/optimizing/completed/failed |
|
||||
| original_score | Integer | Score before optimization |
|
||||
| optimized_score | Integer | Score after optimization |
|
||||
| original_content | TextField | Content before changes |
|
||||
| optimized_content | TextField | Content after changes |
|
||||
| suggestions | JSON | Optimization suggestions |
|
||||
| applied_changes | JSON | Changes that were applied |
|
||||
| credits_used | Decimal | Credits consumed |
|
||||
| created_at | DateTime | Creation date |
|
||||
| completed_at | DateTime | Completion date |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| POST | `/api/v1/optimizer/optimize/` | `OptimizerViewSet.optimize` | Optimize single content |
|
||||
| POST | `/api/v1/optimizer/batch_optimize/` | `OptimizerViewSet.batch_optimize` | Optimize multiple items |
|
||||
| POST | `/api/v1/optimizer/analyze/` | `OptimizerViewSet.analyze` | Analyze without applying |
|
||||
|
||||
### Entry Points
|
||||
|
||||
| Entry Point | Description |
|
||||
|-------------|-------------|
|
||||
| `auto` | Auto-detect based on content source |
|
||||
| `igny8` | IGNY8-generated content |
|
||||
| `wordpress` | WordPress-synced content |
|
||||
| `external` | External platform content |
|
||||
| `manual` | Manual optimization trigger |
|
||||
|
||||
### Optimize Request
|
||||
|
||||
```json
|
||||
{
|
||||
"content_id": 123,
|
||||
"entry_point": "igny8",
|
||||
"optimization_goals": ["seo", "readability", "keyword_density"]
|
||||
}
|
||||
```
|
||||
|
||||
### Optimize Response
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": 456,
|
||||
"content_id": 123,
|
||||
"original_score": 65,
|
||||
"optimized_score": 85,
|
||||
"suggestions": [
|
||||
{
|
||||
"type": "keyword_density",
|
||||
"description": "Increase keyword 'SEO' usage",
|
||||
"applied": true
|
||||
},
|
||||
{
|
||||
"type": "meta_description",
|
||||
"description": "Meta description too short",
|
||||
"applied": true
|
||||
}
|
||||
],
|
||||
"credits_used": 15
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Business Logic
|
||||
|
||||
### Content Analysis
|
||||
|
||||
**Trigger:** User clicks "Analyze"
|
||||
**Credit Cost:** None (analysis only)
|
||||
|
||||
**Checks:**
|
||||
- Keyword density and distribution
|
||||
- Meta title and description optimization
|
||||
- Heading structure (H1, H2, H3)
|
||||
- Readability score
|
||||
- Content length
|
||||
- Internal/external link count
|
||||
- Image alt text presence
|
||||
|
||||
### Content Optimization (AI)
|
||||
|
||||
**Trigger:** User clicks "Optimize"
|
||||
**AI Function:** `OptimizeContentFunction`
|
||||
**Credit Cost:** Per 200 words
|
||||
|
||||
**Flow:**
|
||||
1. Load content with metadata
|
||||
2. Analyze current state
|
||||
3. AIEngine executes `OptimizeContentFunction`:
|
||||
- Reviews content against SEO best practices
|
||||
- Suggests improvements
|
||||
- Rewrites sections if needed
|
||||
4. Create `OptimizationTask` record
|
||||
5. Save original and optimized versions
|
||||
6. Return comparison view
|
||||
|
||||
---
|
||||
|
||||
## Module Enable Control
|
||||
|
||||
### Backend Model
|
||||
|
||||
```python
|
||||
# modules/system/settings_models.py
|
||||
class ModuleEnableSettings(AccountBaseModel):
|
||||
optimizer_enabled = models.BooleanField(default=True)
|
||||
```
|
||||
|
||||
### Frontend Check
|
||||
|
||||
```typescript
|
||||
// layout/AppSidebar.tsx
|
||||
if (isModuleEnabled('optimizer')) {
|
||||
workflowItems.push({
|
||||
name: "Optimizer",
|
||||
path: "/optimizer/content",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Current Limitation
|
||||
|
||||
Direct URL access to `/optimizer/*` routes still works even when module is disabled.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages
|
||||
|
||||
### Optimizer Overview (`/optimizer`)
|
||||
|
||||
- Module overview
|
||||
- Quick stats
|
||||
- Recent optimizations
|
||||
|
||||
### Select Content (`/optimizer/content`)
|
||||
|
||||
- List of content available for optimization
|
||||
- Filter by score, status
|
||||
- Start analysis button
|
||||
- Batch optimize action
|
||||
|
||||
### Analysis Preview (`/optimizer/preview`)
|
||||
|
||||
- Side-by-side comparison
|
||||
- Suggestion list
|
||||
- Accept/reject changes
|
||||
- Apply optimization button
|
||||
|
||||
---
|
||||
|
||||
## Optimization Scoring
|
||||
|
||||
| Metric | Weight | Description |
|
||||
|--------|--------|-------------|
|
||||
| Keyword Density | 20% | Target 1-3% density |
|
||||
| Meta Quality | 15% | Title 50-60 chars, desc 150-160 chars |
|
||||
| Heading Structure | 15% | Proper H1-H6 hierarchy |
|
||||
| Readability | 15% | Flesch-Kincaid score |
|
||||
| Content Length | 10% | Meets word count target |
|
||||
| Internal Links | 10% | 2-5 internal links |
|
||||
| Image Optimization | 10% | Alt text, sizing |
|
||||
| Mobile Friendly | 5% | No wide elements |
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
| From | To | Trigger |
|
||||
|------|----|---------|
|
||||
| Content | Optimizer | Manual analysis/optimize |
|
||||
| WordPress | Optimizer | Synced content optimization |
|
||||
| Optimizer | Content | Apply changes |
|
||||
| Automation | Optimizer | Automated optimization (future) |
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Module visible when disabled | Only sidebar hidden | Pending: route protection |
|
||||
| Optimization not improving | Content already good | Check original score |
|
||||
| High credit usage | Large content | Optimize sections |
|
||||
| Changes not saving | Apply not clicked | Click "Apply Changes" |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Route-level protection | 🔜 Pending | Block access when module disabled |
|
||||
| Dashboard card hiding | 🔜 Pending | Hide from dashboard when disabled |
|
||||
| Automation integration | 🔜 Planned | Add to automation pipeline |
|
||||
| Partial optimization | 🔜 Planned | Optimize specific sections only |
|
||||
| Competitor analysis | 🔜 Planned | Compare against top-ranking content |
|
||||
| A/B testing | 🔜 Planned | Track performance of optimizations |
|
||||
232
v2/Live Docs on Server/igny8-app-docs/10-MODULES/PLANNER.md
Normal file
232
v2/Live Docs on Server/igny8-app-docs/10-MODULES/PLANNER.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# Planner Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Version:** 1.8.4
|
||||
**Status:** ✅ Active
|
||||
**Backend Path:** `backend/igny8_core/modules/planner/`
|
||||
**Frontend Path:** `frontend/src/pages/Planner/`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Models | `modules/planner/models.py` | `Keywords`, `Clusters`, `ContentIdeas` |
|
||||
| Views | `modules/planner/views.py` | `KeywordViewSet`, `ClusterViewSet`, `ContentIdeaViewSet` |
|
||||
| Serializers | `modules/planner/serializers.py` | Keyword/Cluster/Idea serializers |
|
||||
| AI Functions | `ai/functions/clustering.py` | `AutoClusterFunction` |
|
||||
| AI Functions | `ai/functions/ideas.py` | `GenerateIdeasFunction` |
|
||||
| Frontend Pages | `pages/Planner/Keywords.tsx` | Keyword management |
|
||||
| Frontend Pages | `pages/Planner/Clusters.tsx` | Cluster management |
|
||||
| Frontend Pages | `pages/Planner/Ideas.tsx` | Content ideas |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Planner module manages the SEO content planning pipeline:
|
||||
|
||||
```
|
||||
SeedKeywords (Global) → Keywords (Site/Sector) → Clusters → ContentIdeas → Tasks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### Keywords
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| sector | FK | Parent sector |
|
||||
| seed_keyword | FK | Reference to global SeedKeyword |
|
||||
| keyword | CharField | Keyword text |
|
||||
| search_volume | Integer | Monthly search volume |
|
||||
| difficulty | Integer | SEO difficulty (0-100) |
|
||||
| cpc | Decimal | Cost per click |
|
||||
| search_intent | CharField | informational/commercial/transactional/navigational |
|
||||
| status | CharField | new/mapped/used |
|
||||
| cluster | FK | Assigned cluster (nullable) |
|
||||
| created_at | DateTime | Creation date |
|
||||
|
||||
### Clusters
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| sector | FK | Parent sector |
|
||||
| name | CharField | Cluster name |
|
||||
| description | TextField | Cluster description |
|
||||
| primary_keyword | FK | Main keyword |
|
||||
| status | CharField | draft/active/complete |
|
||||
| keyword_count | Integer | Number of keywords |
|
||||
| created_at | DateTime | Creation date |
|
||||
|
||||
### ContentIdeas
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| sector | FK | Parent sector |
|
||||
| cluster | FK | Source cluster |
|
||||
| title | CharField | Content title |
|
||||
| description | TextField | Content brief |
|
||||
| target_keywords | JSON | Keywords to target |
|
||||
| content_type | CharField | blog_post/guide/comparison/etc. |
|
||||
| word_count_target | Integer | Target word count |
|
||||
| status | CharField | draft/queued/used |
|
||||
| priority | Integer | Priority score |
|
||||
| created_at | DateTime | Creation date |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Keywords
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/planner/keywords/` | `KeywordViewSet.list` | List keywords with filtering |
|
||||
| POST | `/api/v1/planner/keywords/` | `KeywordViewSet.create` | Create single keyword |
|
||||
| GET | `/api/v1/planner/keywords/{id}/` | `KeywordViewSet.retrieve` | Get keyword detail |
|
||||
| PUT | `/api/v1/planner/keywords/{id}/` | `KeywordViewSet.update` | Update keyword |
|
||||
| DELETE | `/api/v1/planner/keywords/{id}/` | `KeywordViewSet.destroy` | Soft delete keyword |
|
||||
| POST | `/api/v1/planner/keywords/bulk_delete/` | `KeywordViewSet.bulk_delete` | Hard delete multiple |
|
||||
| POST | `/api/v1/planner/keywords/bulk_status/` | `KeywordViewSet.bulk_status` | Update status for multiple |
|
||||
| POST | `/api/v1/planner/keywords/add_to_workflow/` | `KeywordViewSet.add_to_workflow` | Add SeedKeywords to workflow |
|
||||
|
||||
**Filters:** `?site_id=`, `?sector_id=`, `?status=`, `?cluster_id=`, `?difficulty_min=`, `?difficulty_max=`, `?volume_min=`, `?volume_max=`
|
||||
|
||||
### Clusters
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/planner/clusters/` | `ClusterViewSet.list` | List clusters |
|
||||
| POST | `/api/v1/planner/clusters/` | `ClusterViewSet.create` | Create cluster manually |
|
||||
| POST | `/api/v1/planner/clusters/auto_cluster/` | `ClusterViewSet.auto_cluster` | AI-powered clustering |
|
||||
| POST | `/api/v1/planner/clusters/generate_ideas/` | `ClusterViewSet.generate_ideas` | Generate ideas from clusters |
|
||||
|
||||
### Content Ideas
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/planner/ideas/` | `ContentIdeaViewSet.list` | List ideas |
|
||||
| POST | `/api/v1/planner/ideas/` | `ContentIdeaViewSet.create` | Create idea manually |
|
||||
| POST | `/api/v1/planner/ideas/create_tasks/` | `ContentIdeaViewSet.create_tasks` | Convert ideas to tasks |
|
||||
|
||||
---
|
||||
|
||||
## Business Logic
|
||||
|
||||
### Auto Clustering (AI)
|
||||
|
||||
**Trigger:** User clicks "Auto Cluster" button
|
||||
**AI Function:** `AutoClusterFunction`
|
||||
**Credit Cost:** Based on AI tokens used (see AIModelConfig)
|
||||
|
||||
**Flow:**
|
||||
1. User selects keywords (or all unclustered)
|
||||
2. Frontend calls `POST /clusters/auto_cluster/`
|
||||
3. Backend validates minimum 5 keywords
|
||||
4. AIEngine executes `AutoClusterFunction`:
|
||||
- Sends keywords to GPT-4
|
||||
- AI groups by semantic similarity
|
||||
- Returns cluster assignments
|
||||
5. Creates/updates `Clusters` records
|
||||
6. Assigns keywords to clusters
|
||||
7. Returns created clusters
|
||||
|
||||
### Generate Ideas (AI)
|
||||
|
||||
**Trigger:** User clicks "Generate Ideas" on cluster(s)
|
||||
**AI Function:** `GenerateIdeasFunction`
|
||||
**Credit Cost:** Based on AI tokens used (see AIModelConfig)
|
||||
|
||||
**Flow:**
|
||||
1. User selects clusters
|
||||
2. Frontend calls `POST /clusters/generate_ideas/`
|
||||
3. Backend validates clusters have keywords
|
||||
4. AIEngine executes `GenerateIdeasFunction`:
|
||||
- Analyzes cluster keywords
|
||||
- Generates content titles + briefs
|
||||
- Suggests word counts and content types
|
||||
5. Creates `ContentIdeas` records
|
||||
6. Returns created ideas
|
||||
|
||||
### Add Keywords to Workflow
|
||||
|
||||
**Trigger:** User selects SeedKeywords in setup
|
||||
**Credit Cost:** None
|
||||
|
||||
**Flow:**
|
||||
1. User browses global SeedKeywords
|
||||
2. Selects keywords to add to their sector
|
||||
3. Frontend calls `POST /keywords/add_to_workflow/`
|
||||
4. Backend creates `Keywords` records linked to SeedKeywords
|
||||
5. Keywords appear in Planner for clustering
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages
|
||||
|
||||
### Keywords Page (`/planner/keywords`)
|
||||
|
||||
- Table of all keywords with filtering
|
||||
- Bulk actions: delete, update status
|
||||
- Add keywords from SeedKeyword library
|
||||
- Import keywords from CSV
|
||||
- Assign to clusters manually
|
||||
|
||||
### Clusters Page (`/planner/clusters`)
|
||||
|
||||
- Grid/list of clusters
|
||||
- Auto-cluster action (AI)
|
||||
- Generate ideas action (AI)
|
||||
- View keywords in cluster
|
||||
- Cluster status management
|
||||
|
||||
### Ideas Page (`/planner/ideas`)
|
||||
|
||||
- Table of content ideas
|
||||
- Convert to tasks action
|
||||
- Edit idea details
|
||||
- Priority management
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
| From | To | Trigger |
|
||||
|------|----|---------|
|
||||
| SeedKeywords | Keywords | Add to workflow |
|
||||
| Keywords | Clusters | Auto clustering |
|
||||
| Clusters | ContentIdeas | Generate ideas |
|
||||
| ContentIdeas | Tasks | Create tasks |
|
||||
| Automation Stage 1 | Clusters | Automated clustering |
|
||||
| Automation Stage 2 | ContentIdeas | Automated idea generation |
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Clustering fails | Less than 5 keywords | Ensure minimum keywords |
|
||||
| Ideas not generating | Cluster has no keywords | Assign keywords to cluster |
|
||||
| Keywords not showing | Wrong site/sector filter | Check active site/sector |
|
||||
| Duplicate keywords | Same keyword added twice | Check seed_keyword reference |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Keyword import improvements | 🔜 Planned | Better CSV parsing and validation |
|
||||
| Cluster merging | 🔜 Planned | Merge similar clusters |
|
||||
| Idea prioritization | 🔜 Planned | AI-based priority scoring |
|
||||
536
v2/Live Docs on Server/igny8-app-docs/10-MODULES/PUBLISHER.md
Normal file
536
v2/Live Docs on Server/igny8-app-docs/10-MODULES/PUBLISHER.md
Normal file
@@ -0,0 +1,536 @@
|
||||
# Publisher Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Status:** ✅ Active
|
||||
**Version:** 1.8.4
|
||||
**Backend Path:** `backend/igny8_core/modules/publisher/` + `backend/igny8_core/business/publishing/`
|
||||
**Frontend Path:** `frontend/src/pages/Publisher/`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Views | `modules/publisher/views.py` | `PublishingRecordViewSet`, `DeploymentViewSet`, `PublishContentViewSet` |
|
||||
| Models | `business/publishing/models.py` | `PublishingRecord`, `DeploymentRecord` |
|
||||
| Integration Models | `modules/integration/models.py` | `PublishingSettings` |
|
||||
| **Unified Settings** | `modules/integration/views/unified_settings.py` | **v1.8.0** Consolidated settings API |
|
||||
| Services | `business/publishing/services/*.py` | Publishing orchestration |
|
||||
| Scheduler Tasks | `igny8_core/tasks/publishing_scheduler.py` | Celery beat tasks |
|
||||
| URLs | `modules/publisher/urls.py` | Publisher endpoints |
|
||||
| Frontend | `pages/Publisher/ContentCalendar.tsx` | Content calendar view |
|
||||
| **Site Settings** | `pages/Sites/AIAutomationSettings.tsx` | **v1.8.0** Unified settings UI |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Publisher module manages:
|
||||
- Content publishing pipeline
|
||||
- **Publishing scheduler (automated publishing)**
|
||||
- Publishing record tracking
|
||||
- Deployment management
|
||||
- Multi-destination publishing
|
||||
- **Content calendar visualization**
|
||||
|
||||
**Settings Location (v1.8.0):** Site Settings → Automation tab
|
||||
|
||||
> ⚠️ **v1.8.0 Change:** The standalone `/publisher/settings` page has been removed. Publishing settings are now configured in Site Settings → Automation tab under "Capacity" and "Schedule" sections.
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### PublishingRecord
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| content | FK | Source content |
|
||||
| destination | CharField | wordpress/ghost/webflow |
|
||||
| external_id | CharField | ID on destination platform |
|
||||
| external_url | URLField | Published URL |
|
||||
| status | CharField | pending/published/failed/retracted |
|
||||
| published_at | DateTime | Publication time |
|
||||
| metadata | JSON | Additional data |
|
||||
| created_at | DateTime | Record creation |
|
||||
|
||||
### DeploymentRecord
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Target site |
|
||||
| deployment_type | CharField | full/incremental |
|
||||
| status | CharField | pending/deploying/completed/failed |
|
||||
| items_deployed | Integer | Number of items |
|
||||
| started_at | DateTime | Start time |
|
||||
| completed_at | DateTime | Completion time |
|
||||
| error_log | TextField | Errors encountered |
|
||||
| metadata | JSON | Deployment details |
|
||||
|
||||
### PublishingSettings (v1.3.2, updated v1.8.0)
|
||||
|
||||
Site-level publishing configuration:
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| site | OneToOne | Parent site (unique) |
|
||||
| auto_approval_enabled | Boolean | Auto-approve content |
|
||||
| auto_publish_enabled | Boolean | Auto-publish approved content |
|
||||
| daily_publish_limit | Integer | Max publications per day |
|
||||
| weekly_publish_limit | Integer | Max publications per week |
|
||||
| monthly_publish_limit | Integer | Max publications per month |
|
||||
| publish_days | JSON | Days of week for publishing ["mon","wed","fri"] |
|
||||
| publish_time_slots | JSON | Time slots for publishing [{"start":"09:00","end":"17:00"}] |
|
||||
| **total_items_per_run** | Integer | **v1.8.0** Computed capacity display |
|
||||
|
||||
### Content Site Status Fields (v1.3.2)
|
||||
|
||||
Added to Content model for scheduling:
|
||||
|
||||
| Field | Type | Values |
|
||||
|-------|------|--------|
|
||||
| site_status | CharField | not_published, scheduled, publishing, published, failed |
|
||||
| scheduled_publish_at | DateTime | Future publication time (nullable) |
|
||||
| site_status_updated_at | DateTime | Last status change timestamp |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Unified Settings API (v1.8.0)
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| **GET** | `/api/v1/integration/sites/{site_id}/unified-settings/` | Get all automation + publishing settings |
|
||||
| **PATCH** | `/api/v1/integration/sites/{site_id}/unified-settings/` | Update settings |
|
||||
|
||||
### Publishing & Records
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/publisher/records/` | `PublishingRecordViewSet.list` | List publishing records |
|
||||
| POST | `/api/v1/publisher/records/` | `PublishingRecordViewSet.create` | Create record |
|
||||
| GET | `/api/v1/publisher/deployments/` | `DeploymentViewSet.list` | List deployments |
|
||||
| POST | `/api/v1/publisher/publish/` | `PublishContentViewSet.publish` | Publish content immediately |
|
||||
| GET | `/api/v1/publisher/publish/status/` | `PublishContentViewSet.status` | Get publishing status |
|
||||
| GET | `/api/v1/publisher/site-definition/` | `SiteDefinitionViewSet.list` | Public site definitions |
|
||||
|
||||
### Scheduling Endpoints (v1.3.2+)
|
||||
|
||||
| Method | Path | Purpose | Request Body | Response |
|
||||
|--------|------|---------|--------------|----------|
|
||||
| **POST** | `/api/v1/writer/content/{id}/schedule/` | Schedule content for future publishing | `{ "scheduled_publish_at": "2025-01-20T09:00:00Z" }` | `{ "success": true, "scheduled_publish_at": "2025-01-20T09:00:00Z" }` |
|
||||
| **POST** | `/api/v1/writer/content/{id}/reschedule/` | Reschedule existing scheduled content | `{ "scheduled_at": "2025-01-21T10:00:00Z" }` | `{ "success": true, "scheduled_publish_at": "2025-01-21T10:00:00Z" }` |
|
||||
| **POST** | `/api/v1/writer/content/{id}/unschedule/` | Cancel scheduled publishing | `{}` | `{ "success": true, "message": "Content unscheduled" }` |
|
||||
| **POST** | `/api/v1/writer/content/bulk_schedule/` | Bulk schedule with site defaults | `{ "content_ids": [1,2,3], "use_site_defaults": true, "site_id": 5 }` | `{ "success": true, "scheduled_count": 3, "schedule_preview": [...] }` |
|
||||
| **POST** | `/api/v1/writer/content/bulk_schedule_preview/` | Preview bulk schedule times | `{ "content_ids": [1,2,3], "site_id": 5 }` | `{ "schedule_preview": [...], "site_settings": {...} }` |
|
||||
|
||||
### Legacy Publishing Settings (deprecated)
|
||||
|
||||
> ⚠️ **Deprecated in v1.8.0:** Use unified-settings API instead
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| ~~GET~~ | ~~`/api/v1/sites/{site_id}/settings?tab=publishing`~~ | ~~Get site publishing settings~~ |
|
||||
| ~~PUT~~ | ~~`/api/v1/sites/{site_id}/publishing-settings/`~~ | ~~Update publishing settings~~ |
|
||||
|
||||
---
|
||||
|
||||
## API Usage Examples
|
||||
|
||||
### Publish Content Immediately
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
POST /api/v1/publisher/publish/
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"content_id": 123,
|
||||
"destinations": ["wordpress"] # or ["shopify"], ["custom"]
|
||||
}
|
||||
```
|
||||
|
||||
**Success Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"success": true,
|
||||
"results": [
|
||||
{
|
||||
"destination": "wordpress",
|
||||
"success": true,
|
||||
"external_id": "456",
|
||||
"url": "https://mysite.com/article-title/",
|
||||
"publishing_record_id": 789,
|
||||
"platform_type": "wordpress"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Response:**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Publishing API error: Invalid credentials"
|
||||
}
|
||||
```
|
||||
|
||||
### Schedule Content for Future Publishing
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
POST /api/v1/writer/content/123/schedule/
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"scheduled_publish_at": "2025-01-20T09:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"scheduled_publish_at": "2025-01-20T09:00:00Z",
|
||||
"site_status": "scheduled"
|
||||
}
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Content `site_status` changes from `not_published` → `scheduled`
|
||||
- Celery task `process_scheduled_publications` will publish at scheduled time
|
||||
- Runs every 5 minutes, so publishing happens within 5 min of scheduled time
|
||||
|
||||
### Reschedule Content
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
POST /api/v1/writer/content/123/reschedule/
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"scheduled_at": "2025-01-21T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"scheduled_publish_at": "2025-01-21T10:00:00Z",
|
||||
"site_status": "scheduled"
|
||||
}
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Reschedule from `site_status='scheduled'` (change time)
|
||||
- Reschedule from `site_status='failed'` (retry at new time)
|
||||
|
||||
### Unschedule Content
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
POST /api/v1/writer/content/123/unschedule/
|
||||
Content-Type: application/json
|
||||
|
||||
{}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Content unscheduled successfully",
|
||||
"site_status": "not_published"
|
||||
}
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Removes content from publishing queue
|
||||
- Content returns to `site_status='not_published'`
|
||||
- Can be rescheduled or published immediately later
|
||||
|
||||
### Bulk Schedule with Site Defaults
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
POST /api/v1/writer/content/bulk_schedule/
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"content_ids": [123, 124, 125, 126],
|
||||
"use_site_defaults": true,
|
||||
"site_id": 45
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"scheduled_count": 4,
|
||||
"schedule_preview": [
|
||||
{
|
||||
"content_id": 123,
|
||||
"scheduled_at": "2025-01-17T09:00:00Z",
|
||||
"title": "First Article"
|
||||
},
|
||||
{
|
||||
"content_id": 124,
|
||||
"scheduled_at": "2025-01-17T09:15:00Z",
|
||||
"title": "Second Article"
|
||||
},
|
||||
{
|
||||
"content_id": 125,
|
||||
"scheduled_at": "2025-01-17T09:30:00Z",
|
||||
"title": "Third Article"
|
||||
},
|
||||
{
|
||||
"content_id": 126,
|
||||
"scheduled_at": "2025-01-17T09:45:00Z",
|
||||
"title": "Fourth Article"
|
||||
}
|
||||
],
|
||||
"site_settings": {
|
||||
"base_time": "09:00 AM",
|
||||
"stagger_interval": 15,
|
||||
"timezone": "America/New_York"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Uses site's default publishing schedule from Site Settings
|
||||
- Automatically staggers publications (e.g., 15 min intervals)
|
||||
- No limit on number of items (unlike direct publish which is limited to 5)
|
||||
- All items set to `site_status='scheduled'`
|
||||
|
||||
### Bulk Schedule Preview (Before Confirming)
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
POST /api/v1/writer/content/bulk_schedule_preview/
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"content_ids": [123, 124, 125],
|
||||
"site_id": 45
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"schedule_preview": [
|
||||
{"content_id": 123, "scheduled_at": "2025-01-17T09:00:00Z", "title": "Article 1"},
|
||||
{"content_id": 124, "scheduled_at": "2025-01-17T09:15:00Z", "title": "Article 2"},
|
||||
{"content_id": 125, "scheduled_at": "2025-01-17T09:30:00Z", "title": "Article 3"}
|
||||
],
|
||||
"site_settings": {
|
||||
"base_time": "09:00 AM",
|
||||
"stagger_interval": 15,
|
||||
"timezone": "America/New_York",
|
||||
"publish_days": ["mon", "tue", "wed", "thu", "fri"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use Case:**
|
||||
- Show user what times items will be scheduled before confirming
|
||||
- Allow user to adjust site settings if needed
|
||||
- User clicks "Confirm" to execute actual bulk_schedule
|
||||
|
||||
---
|
||||
|
||||
## Publishing Scheduler (v1.3.2)
|
||||
|
||||
### Celery Beat Tasks
|
||||
|
||||
Located in `igny8_core/tasks/publishing_scheduler.py`:
|
||||
|
||||
| Task | Schedule | Purpose |
|
||||
|------|----------|---------|
|
||||
| `schedule_approved_content` | Every 15 minutes | Assigns publish times to approved content |
|
||||
| `process_scheduled_publications` | Every 5 minutes | Publishes content when scheduled time arrives |
|
||||
| `cleanup_failed_publications` | Daily at midnight | Retries or cleans up failed publications |
|
||||
|
||||
### Scheduling Flow
|
||||
|
||||
```
|
||||
Approved Content → schedule_approved_content (every 15 min)
|
||||
↓
|
||||
Check PublishingSettings
|
||||
↓
|
||||
Assign scheduled_publish_at based on:
|
||||
- publish_days configuration
|
||||
- publish_time_slots configuration
|
||||
- daily/weekly/monthly limits
|
||||
↓
|
||||
Set site_status = "scheduled"
|
||||
↓
|
||||
process_scheduled_publications (every 5 min)
|
||||
↓
|
||||
If scheduled_publish_at <= now:
|
||||
- Set site_status = "publishing"
|
||||
- Execute publication
|
||||
- Set site_status = "published" or "failed"
|
||||
```
|
||||
|
||||
### Site Status State Machine
|
||||
|
||||
```
|
||||
not_published → scheduled → publishing → published
|
||||
↓ ↓
|
||||
↓ → failed
|
||||
↓
|
||||
not_published (if unscheduled)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages (v1.3.2)
|
||||
|
||||
### Content Calendar (`/publisher/content-calendar`)
|
||||
|
||||
**Purpose:** Visualize and manage scheduled content
|
||||
|
||||
**Features:**
|
||||
- Calendar view of scheduled publications
|
||||
- List view alternative
|
||||
- Filter by site
|
||||
- Schedule/unschedule actions
|
||||
- Drag-and-drop rescheduling (planned)
|
||||
|
||||
**Components:**
|
||||
- `ContentCalendar.tsx` - Main page component
|
||||
- `CalendarItemTooltip` - Hover details for calendar items
|
||||
- `SiteInfoBar` - Site context header
|
||||
|
||||
### Publishing Queue (`/publisher/publishing-queue`)
|
||||
|
||||
**Purpose:** Review upcoming publications
|
||||
|
||||
**Features:**
|
||||
- List of content pending publication
|
||||
- Status indicators (scheduled, publishing, failed)
|
||||
- Publish now / unschedule actions
|
||||
|
||||
---
|
||||
|
||||
## Publishing Flow
|
||||
|
||||
### Single Content Publish
|
||||
|
||||
**Trigger:** User clicks "Publish" or API call
|
||||
**Flow:**
|
||||
1. Validate content is in publishable state
|
||||
2. Get site integration credentials
|
||||
3. Transform content for destination:
|
||||
- Format HTML for platform
|
||||
- Process images
|
||||
- Map taxonomies
|
||||
4. Push to destination API
|
||||
5. Create `PublishingRecord` with external ID
|
||||
6. Update Content status to `published`
|
||||
|
||||
### Batch Deployment
|
||||
|
||||
**Trigger:** Deployment action
|
||||
**Flow:**
|
||||
1. Create `DeploymentRecord`
|
||||
2. Gather all pending content
|
||||
3. Process each item:
|
||||
- Publish to destination
|
||||
- Create publishing record
|
||||
- Update content status
|
||||
4. Update deployment status
|
||||
5. Log any errors
|
||||
|
||||
---
|
||||
|
||||
## Destination Adapters
|
||||
|
||||
### WordPress Adapter
|
||||
|
||||
- Uses WordPress REST API
|
||||
- Supports posts, pages, custom post types
|
||||
- Handles media upload
|
||||
- Maps categories/tags
|
||||
|
||||
### Ghost Adapter (Planned)
|
||||
|
||||
- Ghost Admin API integration
|
||||
- Post publishing
|
||||
- Image handling
|
||||
|
||||
### Webflow Adapter (Planned)
|
||||
|
||||
- Webflow CMS API
|
||||
- Collection item publishing
|
||||
- Asset management
|
||||
|
||||
---
|
||||
|
||||
## Publishing States
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| pending | Ready to publish |
|
||||
| published | Successfully published |
|
||||
| failed | Publishing failed |
|
||||
| retracted | Unpublished/removed |
|
||||
|
||||
---
|
||||
|
||||
## Site Definition Endpoint
|
||||
|
||||
**Purpose:** Public endpoint for headless CMS use cases
|
||||
|
||||
Returns site structure for external consumption:
|
||||
- Site metadata
|
||||
- Available taxonomies
|
||||
- Content types
|
||||
- URL structure
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
| From | To | Trigger |
|
||||
|------|----|---------|
|
||||
| Writer | Publisher | Publish action |
|
||||
| Automation Stage 7 | Publisher | Auto-publish (future) |
|
||||
| Publisher | Integrations | Destination APIs |
|
||||
| Publisher | Content | Status updates |
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Publish failed | Invalid credentials | Check integration settings |
|
||||
| Duplicate posts | Published twice | Check existing publishing record |
|
||||
| Images missing | Upload failed | Check media library access |
|
||||
| Wrong category | Mapping issue | Verify taxonomy sync |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Ghost integration | 🔜 Planned | Ghost CMS publishing |
|
||||
| Webflow integration | 🔜 Planned | Webflow publishing |
|
||||
| ~~Scheduled publishing~~ | ✅ Implemented (v1.3.2) | Future-date publishing |
|
||||
| Republish detection | 🔜 Planned | Detect and handle updates |
|
||||
| ~~Publishing queue~~ | ✅ Implemented (v1.3.2) | Batch publishing with queue |
|
||||
| Drag-and-drop calendar | 🔜 Planned | Reschedule via drag-and-drop |
|
||||
@@ -0,0 +1,293 @@
|
||||
# System Settings Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Version:** 1.8.4
|
||||
**Status:** ✅ Active
|
||||
**Backend Path:** `backend/igny8_core/modules/system/`
|
||||
**Frontend Path:** `frontend/src/pages/Settings/`
|
||||
|
||||
> **Note (v1.8.0):** AI & Automation settings have been consolidated into Site Settings → Automation tab. See [AUTOMATION.md](AUTOMATION.md) for the unified settings API.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Global Models | `modules/system/global_settings_models.py` | `GlobalIntegrationSettings`, `GlobalAIPrompt`, `GlobalAuthorProfile` |
|
||||
| Account Models | `modules/system/settings_models.py` | `SystemSettings`, `UserSettings` |
|
||||
| Email Models | `modules/system/email_models.py` | `EmailSettings`, `EmailTemplate`, `EmailLog` |
|
||||
| AI Settings | `modules/system/ai_settings.py` | `SystemAISettings` |
|
||||
| Provider Model | `modules/system/models.py` | `IntegrationProvider` |
|
||||
| Views | `modules/system/settings_views.py` | Settings ViewSets |
|
||||
| Integration Views | `modules/system/integration_views.py` | AI integration settings |
|
||||
| **Unified Settings** | `api/unified_settings.py` | **v1.8.0** Site-level consolidated settings |
|
||||
| Frontend | `pages/Settings/*.tsx` | Settings pages |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The System Settings module manages:
|
||||
- Platform-wide global settings (API keys, defaults)
|
||||
- Per-account settings overrides
|
||||
- AI prompts and configurations
|
||||
- Module enable/disable
|
||||
- Author profiles and content strategies
|
||||
|
||||
---
|
||||
|
||||
## Settings Hierarchy
|
||||
|
||||
```
|
||||
Global Settings (Platform-wide)
|
||||
↓
|
||||
Account Settings (Per-account overrides)
|
||||
↓
|
||||
User Settings (Per-user preferences)
|
||||
```
|
||||
|
||||
**Priority:** User > Account > Global
|
||||
|
||||
---
|
||||
|
||||
## Global Settings Models
|
||||
|
||||
### GlobalIntegrationSettings (Singleton)
|
||||
|
||||
**Admin:** `/admin/system/globalintegrationsettings/`
|
||||
**Purpose:** Platform-wide API keys and defaults
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| openai_api_key | CharField | OpenAI API key (all accounts) |
|
||||
| openai_model | CharField | Default model (gpt-4o-mini) |
|
||||
| openai_temperature | Float | Default temperature (0.7) |
|
||||
| openai_max_tokens | Integer | Default max tokens (8192) |
|
||||
| dalle_api_key | CharField | DALL-E API key |
|
||||
| dalle_model | CharField | Default model (dall-e-3) |
|
||||
| dalle_size | CharField | Default size (1024x1024) |
|
||||
| dalle_quality | CharField | Default quality (standard) |
|
||||
| dalle_style | CharField | Default style (vivid) |
|
||||
| anthropic_api_key | CharField | Anthropic API key |
|
||||
| runware_api_key | CharField | Runware API key |
|
||||
|
||||
**Critical:** This is a singleton (only 1 record, pk=1). All accounts use these API keys.
|
||||
|
||||
### GlobalAIPrompt
|
||||
|
||||
**Admin:** `/admin/system/globalaiprompt/`
|
||||
**Purpose:** Default AI prompt templates
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| prompt_type | CharField | clustering/ideas/content_generation/etc. |
|
||||
| prompt_value | TextField | The actual prompt text |
|
||||
| description | TextField | What this prompt does |
|
||||
| variables | JSON | Available variables ({keyword}, {industry}) |
|
||||
| version | Integer | Prompt version |
|
||||
| is_active | Boolean | Enable/disable |
|
||||
|
||||
### GlobalAuthorProfile
|
||||
|
||||
**Admin:** `/admin/system/globalauthorprofile/`
|
||||
**Purpose:** Default author persona templates
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| name | CharField | Profile name |
|
||||
| description | TextField | Description |
|
||||
| tone | CharField | professional/casual/technical |
|
||||
| language | CharField | en/es/fr |
|
||||
| structure_template | JSON | Content structure config |
|
||||
| category | CharField | saas/ecommerce/blog/technical |
|
||||
| is_active | Boolean | Enable/disable |
|
||||
|
||||
---
|
||||
|
||||
## Account Settings Models
|
||||
|
||||
### IntegrationSettings
|
||||
|
||||
**Purpose:** Per-account AI model overrides (NOT API keys)
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| integration_type | CharField | openai/runware/image_generation |
|
||||
| config | JSON | Model, temperature, max_tokens overrides |
|
||||
| is_active | Boolean | Enable/disable |
|
||||
|
||||
**Important:**
|
||||
- Free plan cannot create overrides
|
||||
- Starter/Growth/Scale can override model/settings
|
||||
- API keys ALWAYS come from GlobalIntegrationSettings
|
||||
|
||||
### ModuleEnableSettings
|
||||
|
||||
**Purpose:** Enable/disable modules per account
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| planner_enabled | Boolean | Enable Planner |
|
||||
| writer_enabled | Boolean | Enable Writer |
|
||||
| thinker_enabled | Boolean | Enable Thinker (AI settings) |
|
||||
| automation_enabled | Boolean | Enable Automation |
|
||||
| site_builder_enabled | Boolean | Enable Site Builder |
|
||||
| linker_enabled | Boolean | Enable Linker |
|
||||
| optimizer_enabled | Boolean | Enable Optimizer |
|
||||
| publisher_enabled | Boolean | Enable Publisher |
|
||||
|
||||
**Current Implementation:**
|
||||
- Controls sidebar navigation visibility
|
||||
- ⚠️ **Pending:** Extend to other pages and references
|
||||
|
||||
### AIPrompt (Account-Level)
|
||||
|
||||
**Purpose:** Per-account prompt customizations
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| prompt_type | CharField | Prompt type |
|
||||
| prompt_value | TextField | Current prompt (custom or default) |
|
||||
| default_prompt | TextField | Original default (for reset) |
|
||||
| is_customized | Boolean | True if user modified |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Integration Settings
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/system/settings/integrations/openai/` | Get OpenAI settings | Get current model/params |
|
||||
| PUT | `/api/v1/system/settings/integrations/openai/` | Save OpenAI settings | Save overrides |
|
||||
| GET | `/api/v1/system/settings/integrations/image_generation/` | Get image settings | Get DALL-E/Runware settings |
|
||||
| PUT | `/api/v1/system/settings/integrations/image_generation/` | Save image settings | Save overrides |
|
||||
| POST | `/api/v1/system/settings/integrations/test/` | Test connection | Test API connectivity |
|
||||
|
||||
### Prompts
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/system/prompts/` | List prompts | Get all prompts |
|
||||
| GET | `/api/v1/system/prompts/{type}/` | Get prompt | Get specific prompt |
|
||||
| PUT | `/api/v1/system/prompts/{type}/` | Save prompt | Save customization |
|
||||
| POST | `/api/v1/system/prompts/{type}/reset/` | Reset prompt | Reset to default |
|
||||
|
||||
### Module Settings
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/system/modules/` | Get module settings | Get enable/disable state |
|
||||
| PUT | `/api/v1/system/modules/` | Save module settings | Update enabled modules |
|
||||
|
||||
---
|
||||
|
||||
## Settings Flow (Frontend → Backend)
|
||||
|
||||
### Getting OpenAI Settings
|
||||
|
||||
1. Frontend requests: `GET /system/settings/integrations/openai/`
|
||||
2. Backend checks account's `IntegrationSettings`
|
||||
3. Gets global defaults from `GlobalIntegrationSettings`
|
||||
4. Merges: account overrides > global defaults
|
||||
5. Returns (NEVER includes API keys):
|
||||
```json
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 8192,
|
||||
"using_global": true
|
||||
}
|
||||
```
|
||||
|
||||
### Saving OpenAI Settings
|
||||
|
||||
1. Frontend sends: `PUT /system/settings/integrations/openai/`
|
||||
2. Backend STRIPS any API key fields (security)
|
||||
3. Validates account plan allows overrides
|
||||
4. Saves to `IntegrationSettings.config`
|
||||
5. Returns updated settings
|
||||
|
||||
---
|
||||
|
||||
## Module Enable/Disable
|
||||
|
||||
### How It Works (Current)
|
||||
|
||||
1. On app load, frontend fetches module settings
|
||||
2. `useModuleStore.isModuleEnabled(name)` checks state
|
||||
3. `AppSidebar.tsx` conditionally renders menu items:
|
||||
|
||||
```typescript
|
||||
if (isModuleEnabled('linker')) {
|
||||
workflowItems.push({
|
||||
name: "Linker",
|
||||
path: "/linker/content",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Current Limitations (⚠️ Pending Implementation)
|
||||
|
||||
- Only hides sidebar menu items
|
||||
- Direct URL access still works
|
||||
- Other page references still show module links
|
||||
- Dashboard cards may still show disabled modules
|
||||
|
||||
### Required Extension
|
||||
|
||||
Need to add `ModuleGuard` component to:
|
||||
- Route-level protection
|
||||
- Dashboard cards/widgets
|
||||
- Cross-module references
|
||||
- Settings page links
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages
|
||||
|
||||
### AI Settings (`/settings/ai`)
|
||||
|
||||
- OpenAI model selection
|
||||
- Temperature and max tokens
|
||||
- Image generation settings
|
||||
- Test connection button
|
||||
|
||||
### Prompts (`/thinker/prompts`)
|
||||
|
||||
- List all prompt types
|
||||
- Edit prompt text
|
||||
- Reset to default
|
||||
- Variable reference
|
||||
|
||||
### Module Settings (Admin Only)
|
||||
|
||||
- Enable/disable modules
|
||||
- Per-account configuration
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Settings not saving | Plan restriction | Upgrade plan |
|
||||
| API key exposed | Security flaw | Should never happen - check code |
|
||||
| Module still visible | Cache stale | Clear cache, reload |
|
||||
| Prompt reset not working | default_prompt missing | Re-run migration |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Module guard extension | 🔜 Pending | Extend disable to all pages, not just sidebar |
|
||||
| AIModelConfig database | 🔜 Planned | Move model pricing to database |
|
||||
| Strategy templates | 🔜 Planned | Global strategy library |
|
||||
| Per-user preferences | 🔜 Planned | User-level setting overrides |
|
||||
295
v2/Live Docs on Server/igny8-app-docs/10-MODULES/WRITER.md
Normal file
295
v2/Live Docs on Server/igny8-app-docs/10-MODULES/WRITER.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Writer Module
|
||||
|
||||
**Last Verified:** January 20, 2026
|
||||
**Version:** 1.8.4
|
||||
**Status:** ✅ Active
|
||||
**Backend Path:** `backend/igny8_core/modules/writer/`
|
||||
**Frontend Path:** `frontend/src/pages/Writer/`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | File | Key Items |
|
||||
|------|------|-----------|
|
||||
| Models | `modules/writer/models.py` | `Tasks`, `Content`, `Images`, `ContentTaxonomy` |
|
||||
| Views | `modules/writer/views.py` | `TaskViewSet`, `ContentViewSet`, `ImageViewSet` |
|
||||
| AI Functions | `ai/functions/content.py` | `GenerateContentFunction` |
|
||||
| AI Functions | `ai/functions/images.py` | `GenerateImagesFunction`, `GenerateImagePromptsFunction` |
|
||||
| Frontend Pages | `pages/Writer/Tasks.tsx` | Task management |
|
||||
| Frontend Pages | `pages/Writer/Content.tsx` | Content listing |
|
||||
| Frontend Pages | `pages/Writer/ContentViewer.tsx` | Content preview/edit |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
The Writer module manages the content creation pipeline:
|
||||
|
||||
```
|
||||
ContentIdeas → Tasks → Content → Images → Review → Publish
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### Tasks
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| sector | FK | Parent sector |
|
||||
| content_idea | FK | Source idea (nullable) |
|
||||
| title | CharField | Task/content title |
|
||||
| description | TextField | Task brief |
|
||||
| target_keywords | JSON | Keywords to target |
|
||||
| content_type | CharField | blog_post/guide/comparison/etc. |
|
||||
| word_count_target | Integer | Target word count |
|
||||
| status | CharField | pending/in_progress/completed/cancelled |
|
||||
| assigned_to | FK | Assigned user (nullable) |
|
||||
| due_date | DateTime | Due date (nullable) |
|
||||
| priority | Integer | Priority level |
|
||||
| created_at | DateTime | Creation date |
|
||||
|
||||
### Content
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| sector | FK | Parent sector |
|
||||
| task | FK | Source task (nullable) |
|
||||
| title | CharField | Content title |
|
||||
| slug | SlugField | URL slug |
|
||||
| content_body | TextField | HTML content |
|
||||
| excerpt | TextField | Short summary |
|
||||
| meta_title | CharField | SEO meta title |
|
||||
| meta_description | CharField | SEO meta description |
|
||||
| word_count | Integer | Actual word count |
|
||||
| status | CharField | draft/review/approved/published |
|
||||
| content_type | CharField | post/page/product |
|
||||
| content_structure | CharField | article/guide/comparison/review/listicle |
|
||||
| source | CharField | igny8/wordpress/manual |
|
||||
| wordpress_id | Integer | WP post ID (if synced) |
|
||||
| published_at | DateTime | Publication date |
|
||||
| created_at | DateTime | Creation date |
|
||||
|
||||
### Images
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| sector | FK | Parent sector |
|
||||
| content | FK | Parent content |
|
||||
| image_type | CharField | featured/desktop/mobile/in_article |
|
||||
| prompt | TextField | Generation prompt |
|
||||
| image_url | URLField | Image URL |
|
||||
| alt_text | CharField | Alt text for SEO |
|
||||
| status | CharField | pending/generating/completed/failed |
|
||||
| position | Integer | Order for in-article images |
|
||||
| provider | CharField | dalle/runware |
|
||||
| model | CharField | dall-e-3/runware:97@1 |
|
||||
| created_at | DateTime | Creation date |
|
||||
|
||||
### ContentTaxonomy
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| account | FK | Owner account |
|
||||
| site | FK | Parent site |
|
||||
| name | CharField | Category/tag name |
|
||||
| slug | SlugField | URL slug |
|
||||
| taxonomy_type | CharField | category/tag |
|
||||
| parent | FK | Parent taxonomy (for hierarchy) |
|
||||
| description | TextField | Description |
|
||||
| wordpress_id | Integer | WP term ID (if synced) |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Tasks
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/writer/tasks/` | `TaskViewSet.list` | List tasks |
|
||||
| POST | `/api/v1/writer/tasks/` | `TaskViewSet.create` | Create task |
|
||||
| POST | `/api/v1/writer/tasks/bulk_create/` | `TaskViewSet.bulk_create` | Create multiple tasks |
|
||||
| POST | `/api/v1/writer/tasks/{id}/generate_content/` | `TaskViewSet.generate_content` | AI content generation |
|
||||
|
||||
### Content
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/writer/content/` | `ContentViewSet.list` | List content |
|
||||
| POST | `/api/v1/writer/content/` | `ContentViewSet.create` | Create content manually |
|
||||
| PUT | `/api/v1/writer/content/{id}/` | `ContentViewSet.update` | Update content |
|
||||
| POST | `/api/v1/writer/content/{id}/update_content/` | `ContentViewSet.update_content` | Update with validation |
|
||||
| POST | `/api/v1/writer/content/{id}/generate_images/` | `ContentViewSet.generate_images` | Generate images |
|
||||
| POST | `/api/v1/writer/content/{id}/publish_to_wordpress/` | `ContentViewSet.publish_to_wordpress` | Publish to WP |
|
||||
|
||||
### Images
|
||||
|
||||
| Method | Path | Handler | Purpose |
|
||||
|--------|------|---------|---------|
|
||||
| GET | `/api/v1/writer/images/` | `ImageViewSet.list` | List images |
|
||||
| POST | `/api/v1/writer/images/generate_for_content/` | `ImageViewSet.generate_for_content` | Generate images |
|
||||
| POST | `/api/v1/writer/images/regenerate/` | `ImageViewSet.regenerate` | Regenerate image |
|
||||
|
||||
---
|
||||
|
||||
## Business Logic
|
||||
|
||||
### Content Generation (AI)
|
||||
|
||||
**Trigger:** User clicks "Generate" on task
|
||||
**AI Function:** `GenerateContentFunction`
|
||||
**Credit Cost:** Based on AI tokens used (see AIModelConfig)
|
||||
|
||||
**Flow:**
|
||||
1. User has task with title, keywords, word count target
|
||||
2. Frontend calls `POST /tasks/{id}/generate_content/`
|
||||
3. Backend validates task and credits
|
||||
4. AIEngine executes `GenerateContentFunction`:
|
||||
- Loads account's AI prompts and strategy
|
||||
- Sends to GPT-4 with structured output
|
||||
- Receives HTML content with proper structure
|
||||
5. Creates `Content` record linked to task
|
||||
6. Updates task status to `completed`
|
||||
7. Returns content for review
|
||||
|
||||
### Image Prompt Generation (AI)
|
||||
|
||||
**Trigger:** Part of content generation or explicit
|
||||
**AI Function:** `GenerateImagePromptsFunction`
|
||||
**Credit Cost:** Based on AI tokens used (see AIModelConfig)
|
||||
|
||||
**Flow:**
|
||||
1. Content exists with body
|
||||
2. AI analyzes content sections
|
||||
3. Generates prompts for:
|
||||
- Featured image (1)
|
||||
- In-article images (configurable count)
|
||||
4. Creates `Images` records with prompts, status=pending
|
||||
|
||||
### Image Generation (AI)
|
||||
|
||||
**Trigger:** User clicks "Generate Images" or automation
|
||||
**AI Function:** `GenerateImagesFunction`
|
||||
**Credit Cost:** Deducted per image (see AIModelConfig.credits_per_image)
|
||||
|
||||
**Flow:**
|
||||
1. Image record exists with prompt, status=pending
|
||||
2. Backend calls DALL-E or Runware API
|
||||
3. Receives image URL
|
||||
4. Updates `Images` record with URL, status=completed
|
||||
|
||||
### WordPress Publishing
|
||||
|
||||
**Trigger:** User clicks "Publish to WordPress"
|
||||
**Credit Cost:** None
|
||||
|
||||
**Flow:**
|
||||
1. Content is in `approved` status
|
||||
2. Site has WordPress integration configured
|
||||
3. Backend calls WordPress REST API:
|
||||
- Creates/updates post
|
||||
- Uploads featured image
|
||||
- Sets categories/tags
|
||||
4. Updates Content with `wordpress_id`
|
||||
5. Sets status to `published`
|
||||
|
||||
---
|
||||
|
||||
## Content Structures
|
||||
|
||||
| Structure | Purpose | Typical Sections |
|
||||
|-----------|---------|------------------|
|
||||
| article | General blog post | Intro, Body, Conclusion |
|
||||
| guide | How-to content | Steps, Tips, Summary |
|
||||
| comparison | Product comparison | Features, Pros/Cons, Verdict |
|
||||
| review | Product review | Overview, Features, Rating |
|
||||
| listicle | List-based content | Numbered items with details |
|
||||
| pillar | Long-form authority | Multiple sections with depth |
|
||||
|
||||
---
|
||||
|
||||
## Frontend Pages
|
||||
|
||||
### Tasks Page (`/writer/tasks`)
|
||||
|
||||
- Table of all tasks
|
||||
- Filter by status, assigned user
|
||||
- Generate content action
|
||||
- Bulk actions
|
||||
|
||||
### Content Page (`/writer/content`)
|
||||
|
||||
- Table of all content
|
||||
- Filter by status, content type
|
||||
- Quick preview
|
||||
- Publish actions
|
||||
|
||||
### Content Viewer (`/writer/content/{id}`)
|
||||
|
||||
- Full content preview
|
||||
- Edit mode
|
||||
- Image management
|
||||
- Publish to WordPress
|
||||
- Status management
|
||||
|
||||
### Draft/Review/Approved Tabs
|
||||
|
||||
- Filtered views by status
|
||||
- Different actions per status
|
||||
- **v1.2.0**: "Published" tab renamed to "Approved"
|
||||
|
||||
---
|
||||
|
||||
## Content Status Flow
|
||||
|
||||
```
|
||||
draft → review → approved → published
|
||||
↓
|
||||
(rejected) → draft (revision)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
| From | To | Trigger |
|
||||
|------|----|---------|
|
||||
| ContentIdeas | Tasks | Create tasks |
|
||||
| Tasks | Content | Generate content |
|
||||
| Content | Images | Generate images |
|
||||
| Content | WordPress | Publish |
|
||||
| WordPress | Content | Sync imports |
|
||||
| Automation Stage 4 | Content | Automated generation |
|
||||
| Automation Stage 5-6 | Images | Automated image generation |
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Issue | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| Content too short | Low word count target | Increase target in task |
|
||||
| Images not generating | No prompts created | Run image prompt generation first |
|
||||
| WordPress publish fails | Invalid credentials | Check integration settings |
|
||||
| Content stuck in draft | No manual status update | Update status via UI or API |
|
||||
| Duplicate content | Re-running generation | Check if content already exists |
|
||||
|
||||
---
|
||||
|
||||
## Planned Changes
|
||||
|
||||
| Feature | Status | Description |
|
||||
|---------|--------|-------------|
|
||||
| Content revisions | 🔜 Planned | Track content version history |
|
||||
| Multi-language | 🔜 Planned | Generate content in different languages |
|
||||
| Batch generation | 🔜 Planned | Generate multiple content pieces at once |
|
||||
| Regenerate sections | 🔜 Planned | AI regenerate specific sections |
|
||||
Reference in New Issue
Block a user