temproary docs uplaoded

This commit is contained in:
IGNY8 VPS (Salman)
2026-03-23 09:02:49 +00:00
parent cb6eca4483
commit 128b186865
113 changed files with 68897 additions and 0 deletions

View File

@@ -0,0 +1,484 @@
# IGNY8 Billing System Comprehensive Audit
**Date:** January 20, 2026
**Status:** CRITICAL - Multiple issues identified
**Reviewer:** System Audit
---
## Executive Summary
The billing system has **fundamental architectural flaws** that cause incorrect behavior across multiple payment methods, credit management, and account activation flows. This audit identifies all issues and provides a detailed analysis of the current implementation.
---
## Table of Contents
1. [Critical Issues](#1-critical-issues)
2. [Account Status & Activation Flow](#2-account-status--activation-flow)
3. [Credit System Analysis](#3-credit-system-analysis)
4. [Invoice Management](#4-invoice-management)
5. [Payment Method Flows](#5-payment-method-flows)
6. [Subscription Renewal Flow](#6-subscription-renewal-flow)
7. [Credit Package Purchases](#7-credit-package-purchases)
8. [Data Model Issues](#8-data-model-issues)
9. [Missing Features](#9-missing-features)
10. [Recommended Fixes](#10-recommended-fixes)
---
## 1. Critical Issues
### Issue #1: Credit Package Approval Adds WRONG Credits (CRITICAL)
**Location:** `AdminBillingViewSet.approve_payment()` and `BillingViewSet.approve_payment()`
**Current Behavior:**
- When admin approves a credit package payment, the code adds `subscription.plan.included_credits` (e.g., 200 credits from the plan)
- It should add the `credit_package.credits` from the purchased package (e.g., 500 credits)
**Evidence:**
```
User bought 500-credit package → Admin approved → System added 200 credits (plan credits)
```
**Root Cause:** Both approval methods look for subscription/plan credits instead of checking if the invoice is for a credit package purchase via `invoice.metadata.credit_package_id`.
---
### Issue #2: Invoice Types Not Differentiated (CRITICAL)
**Problem:** No clear distinction between:
- Subscription invoices (for plan activation/renewal)
- Credit package invoices (for additional credit purchases)
**Impact:**
- Frontend checks `hasPendingInvoice` which includes BOTH types
- Creating a credit package invoice should NOT affect account status
- Creating a credit package invoice should NOT block other purchases
---
### Issue #3: Credits Have No Validity/Expiration (CRITICAL)
**Current State:** Credits are a simple integer counter on Account model:
```python
credits = models.IntegerField(default=0)
```
**Missing:**
- No expiration date for credits
- No validity period tracking (1 month as business rule)
- No automatic reset mechanism
- Purchased credits vs subscription credits not tracked separately
**Impact:** Users can accumulate credits indefinitely, no monthly reset logic.
---
### Issue #4: Subscription Renewal Does NOT Reset Credits Correctly (CRITICAL)
**Current Behavior:**
- Stripe webhook `_handle_invoice_paid()` calls `CreditService.reset_credits_for_renewal()`
- Manual payment approval uses `CreditService.add_credits()` instead of reset ✗
**Problem with add_credits:**
- If user had 50 credits remaining
- Plan gives 200 credits monthly
- `add_credits(200)` → User now has 250 credits
- Should be: Reset to 200 credits
---
### Issue #5: No Invoice Expiration/Cancellation Mechanism
**Current State:**
- Pending invoices remain pending indefinitely
- No automatic void/cancellation after timeout (e.g., 24-48 hours)
- User cannot cancel/void their own pending credit package invoices
- No cleanup task for stale invoices
---
## 2. Account Status & Activation Flow
### Status Values
| Status | Description | When Set |
|--------|-------------|----------|
| `active` | Account fully operational | After successful payment or trial activation |
| `trial` | Free trial period | On signup with free plan |
| `pending_payment` | Awaiting first payment | After signup with paid plan, before payment |
| `suspended` | Manually suspended | Admin action |
| `cancelled` | Subscription cancelled | After cancellation |
### Current Flow Problems
#### New User with Paid Plan (Bank Transfer):
1. User signs up selecting paid plan with bank transfer → `status = pending_payment`
2. Invoice created → Invoice `status = pending`
3. User submits bank transfer confirmation → Payment `status = pending_approval`
4. Admin approves payment → Account `status = active`
5. **BUG:** Credits added = plan credits (not credit package credits if that's what was purchased)
#### Existing Active User Buying Credits (Bank Transfer):
1. User has `status = active`
2. User creates credit package invoice → Invoice created with `status = pending`
3. **BUG:** Frontend incorrectly interprets this as "account has pending invoice"
4. **BUG:** Some UI elements get disabled unnecessarily
5. Admin approves → **BUG:** Wrong credits added (plan credits instead of package credits)
---
## 3. Credit System Analysis
### Current Data Model
**Account.credits** (integer)
- Single counter for all credits
- No distinction between:
- Plan/subscription credits
- Purchased credit packages
- Bonus/promotional credits
- No expiration tracking
**CreditTransaction** (history log)
- Records additions and deductions
- Transaction types: `purchase`, `subscription`, `refund`, `deduction`, `adjustment`
- Has `balance_after` field for audit trail
**CreditUsageLog** (detailed usage)
- Per-operation usage tracking
- Links to site, operation type, model used
- Used for analytics and billing reports
### Credit Flow Issues
| Scenario | Expected Behavior | Current Behavior | Status |
|----------|-------------------|------------------|--------|
| New subscription | Reset credits to plan amount | Add credits to existing | ❌ BUG |
| Subscription renewal (Stripe) | Reset credits to plan amount | Reset credits | ✓ OK |
| Subscription renewal (Manual) | Reset credits to plan amount | Add credits | ❌ BUG |
| Credit package purchase | Add package credits | Add plan credits | ❌ BUG |
| Monthly reset | Reset subscription credits, keep purchased | No reset logic | ❌ MISSING |
### Credit Validity Rules (Business Requirement - NOT IMPLEMENTED)
Per business rules, credits should have a **1-month validity period**:
- Subscription credits reset monthly on billing cycle
- Purchased credits have 1-month validity from purchase date
- Expired credits should be voided
**Current Implementation:** None - credits never expire or reset.
---
## 4. Invoice Management
### Invoice Types (Implicit, Not Enforced)
| Type | Identifier | Purpose |
|------|-----------|---------|
| Subscription | `invoice.subscription IS NOT NULL` | Plan activation/renewal |
| Credit Package | `invoice.metadata.credit_package_id EXISTS` | Additional credit purchase |
| Custom | Neither | Admin-created invoices |
### Invoice Status Flow
```
draft → pending → paid/void/uncollectible
```
### Issues
1. **No Expiration:** Invoices stay pending forever
2. **No User Cancellation:** Users cannot cancel their own credit package invoices
3. **No Automatic Cleanup:** No task to void stale invoices
4. **No Type Enforcement:** Invoice type determined by checking multiple fields
### Recommended Invoice Statuses
| Status | Description | Auto-Transition |
|--------|-------------|-----------------|
| `draft` | Created, not sent | → pending when finalized |
| `pending` | Awaiting payment | → void after 48h (credit packages) |
| `paid` | Payment received | Terminal |
| `void` | Cancelled/expired | Terminal |
| `uncollectible` | Cannot be collected | Terminal |
---
## 5. Payment Method Flows
### Supported Payment Methods
| Method | Countries | Auto-Approval | Account Activation |
|--------|-----------|---------------|-------------------|
| Stripe (Card) | All except PK | Yes | Immediate |
| PayPal | All except PK | Yes | Immediate |
| Bank Transfer | PK only | No (Manual) | After admin approval |
### Stripe Flow (Working ✓)
1. User initiates checkout → Stripe session created
2. User completes payment on Stripe
3. Webhook `checkout.session.completed` → Account activated, credits added
4. Webhook `invoice.paid` (renewal) → Credits reset
### PayPal Flow (Working ✓)
1. User initiates checkout → PayPal order created
2. User approves on PayPal
3. Payment captured → Account activated, credits added
### Bank Transfer Flow (BUGGY ❌)
1. User initiates → Invoice created, account status may change incorrectly
2. User submits confirmation → Payment created with `pending_approval`
3. Admin approves → **WRONG CREDITS ADDED**
4. User rejection flow exists but no automatic expiration
---
## 6. Subscription Renewal Flow
### Automatic (Stripe/PayPal)
- Stripe handles automatic billing
- Webhook `invoice.paid` triggers renewal
- `reset_credits_for_renewal()` correctly resets credits ✓
### Manual (Bank Transfer)
1. Task `process_subscription_renewals` creates renewal invoice
2. Subscription status → `pending_renewal`
3. Grace period (7 days) starts
4. User pays manually
5. **BUG:** Admin approval uses `add_credits()` instead of `reset_credits_for_renewal()`
6. Task `check_expired_renewals` expires after grace period ✓
---
## 7. Credit Package Purchases
### Current Flow
1. User selects package
2. For Stripe/PayPal: Redirect to checkout
3. For Bank Transfer: Create invoice → Show payment form
### Issues
| Step | Issue | Severity |
|------|-------|----------|
| Invoice Creation | Uses `InvoiceService.create_credit_package_invoice()` | OK |
| Invoice stores `credit_package_id` in metadata | OK | OK |
| Approval reads `subscription.plan.included_credits` | **WRONG** | CRITICAL |
| Should read `invoice.metadata.credit_package_id` → get package → add `package.credits` | Missing | CRITICAL |
### Stripe Credit Purchase (Partially Working)
- Webhook handler `_handle_credit_purchase()` correctly reads metadata
- Uses `CreditService.add_credits()` with correct amount ✓
### Manual Credit Purchase (BROKEN)
- `approve_payment()` does NOT check if invoice is credit package
- Adds plan credits instead of package credits
- Completely wrong behavior
---
## 8. Data Model Issues
### Missing Fields
**Invoice Model:**
- `invoice_type` enum: `subscription`, `credit_package`, `custom`
- `expires_at` datetime for auto-void
- `credit_package_id` FK (instead of in metadata)
**Account Model:**
- `subscription_credits` (monthly reset)
- `purchased_credits` (expire after 1 month)
- `credits_valid_until` datetime
**CreditTransaction:**
- `expires_at` datetime
- `source_type`: `subscription`, `purchase`, `bonus`
### Proposed Schema Changes
```
Account:
+ subscription_credits: int (resets monthly)
+ purchased_credits: int (separate pool)
+ credits_last_reset: datetime
Invoice:
+ invoice_type: enum('subscription', 'credit_package', 'custom')
+ expires_at: datetime (nullable)
CreditPurchase: (NEW MODEL)
- account: FK
- credit_package: FK
- credits: int
- purchase_date: datetime
- expires_at: datetime
- credits_remaining: int
- status: enum('active', 'expired', 'used')
```
---
## 9. Missing Features
### Invoice Management
- [ ] User can cancel own pending credit package invoices
- [ ] Auto-void invoices after 24-48 hours
- [ ] Invoice expiration emails
- [ ] Invoice reminder emails (before expiration)
### Credit Management
- [ ] Credit expiration tracking
- [ ] Monthly credit reset task
- [ ] Separate subscription vs purchased credits
- [ ] Credit validity period configuration
- [ ] Low credit warnings before operations
### Payment Management
- [ ] Payment retry for failed payments
- [ ] Automatic payment reminder emails
- [ ] Payment receipt PDFs
- [ ] Refund processing
### Admin Features
- [ ] Bulk invoice void
- [ ] Credit adjustment with reason
- [ ] Payment history export
- [ ] Revenue reports
---
## 10. Recommended Fixes
### Priority 1: Critical (Immediate Fix Required)
#### Fix 1.1: Credit Package Approval Logic
**File:** `billing_views.py` - `approve_payment()` method
**Change:** Before adding credits, check if invoice has `credit_package_id` in metadata:
```
IF invoice.metadata.credit_package_id EXISTS:
package = CreditPackage.get(invoice.metadata.credit_package_id)
credits_to_add = package.credits
ELSE:
credits_to_add = subscription.plan.included_credits
```
#### Fix 1.2: Manual Renewal Should Reset Credits
**File:** `billing_views.py` - `approve_payment()` method
**Change:** For subscription renewals, use `reset_credits_for_renewal()` instead of `add_credits()`.
#### Fix 1.3: Separate Invoice Type Checking in Frontend
**File:** `PlansAndBillingPage.tsx`
**Already Fixed:** `hasPendingSubscriptionInvoice` and `hasPendingCreditInvoice` now separate.
### Priority 2: High (Fix Within 1 Week)
#### Fix 2.1: Add Invoice Expiration
- Add `expires_at` field to Invoice model
- Set expiration on creation (48 hours for credit packages)
- Create Celery task to void expired invoices
- Send reminder email 24 hours before expiration
#### Fix 2.2: User Invoice Cancellation
- Add API endpoint for user to cancel own pending credit package invoices
- Add "Cancel" button in frontend invoice list
- Set invoice status to `void` on cancellation
### Priority 3: Medium (Fix Within 1 Month)
#### Fix 3.1: Credit Expiration System
- Add credit validity tracking
- Create monthly reset task for subscription credits
- Track purchased credits separately with expiration
#### Fix 3.2: Invoice Type Field
- Add `invoice_type` enum field
- Migrate existing invoices based on metadata
- Update all queries to use type field
### Priority 4: Low (Future Enhancement)
- Payment retry automation
- Revenue reporting
- Credit pool separation
- Advanced analytics
---
## Appendix A: Current Code Locations
| Component | File Path |
|-----------|-----------|
| Account Model | `auth/models.py` |
| Plan Model | `auth/models.py` |
| Subscription Model | `auth/models.py` |
| Invoice Model | `business/billing/models.py` |
| Payment Model | `business/billing/models.py` |
| CreditPackage Model | `business/billing/models.py` |
| CreditTransaction Model | `business/billing/models.py` |
| CreditService | `business/billing/services/credit_service.py` |
| InvoiceService | `business/billing/services/invoice_service.py` |
| PaymentService | `business/billing/services/payment_service.py` |
| Admin Approval (v1) | `business/billing/billing_views.py` |
| Admin Approval (v2) | `modules/billing/views.py` |
| Stripe Webhooks | `business/billing/views/stripe_views.py` |
| PayPal Webhooks | `business/billing/views/paypal_views.py` |
| Renewal Tasks | `business/billing/tasks/subscription_renewal.py` |
---
## Appendix B: Test Scenarios Required
| Scenario | Current Result | Expected Result |
|----------|---------------|-----------------|
| PK user buys 500 credit package via bank transfer | Gets 200 credits (plan) | Gets 500 credits |
| Active user creates credit invoice | Account blocked | Account stays active |
| Subscription renewal (manual) | Credits added | Credits reset |
| Invoice unpaid after 48 hours | Stays pending | Auto-void |
| User wants to cancel own invoice | No option | Can cancel |
| Credits unused for 1 month | Stay forever | Should expire |
---
## Appendix C: Affected User Flows
### Flow 1: New PK User Subscription
```
Signup → Select Plan → Bank Transfer → Submit Confirmation → Wait for Approval → Account Active
```
**Issues:** None if subscription only
### Flow 2: Active PK User Buys Credits
```
Go to Credits → Select Package → Bank Transfer → Invoice Created → Submit Confirmation → Wait → WRONG CREDITS ADDED
```
**Issues:**
- Invoice creation may show incorrect UI states
- Wrong credits added on approval
### Flow 3: Active Non-PK User Buys Credits
```
Go to Credits → Select Package → Stripe Checkout → Payment → Credits Added
```
**Issues:** None - Stripe handler works correctly
### Flow 4: Subscription Renewal (Manual)
```
Renewal Date → Invoice Created → User Pays → Admin Approves → CREDITS ADDED (not reset)
```
**Issues:** Should reset credits, not add
---
**End of Audit Report**
**Next Steps:**
1. Review this audit with team
2. Prioritize fixes based on business impact
3. Implement Priority 1 fixes immediately
4. Create automated tests for all scenarios
5. Schedule remaining fixes

View File

@@ -0,0 +1,907 @@
# IGNY8 Frontend Component Audit & TailAdmin Migration Plan
> **Generated:** 2026-03-15
> **Scope:** `frontend/src/` — 483 TypeScript/TSX files
> **Base Template:** TailAdmin Free React v1.8.4 (Tailwind CSS 4, React 19)
---
## 1. Executive Summary
### Total Component Count
| Category | Count |
|----------|-------|
| **Total .ts/.tsx files** | 483 |
| **UI primitive components** (`components/ui/`) | 52 files → ~40 unique components |
| **Common/shared components** (`components/common/`) | 51 files |
| **Page files** (`pages/`) | 68 files |
| **Feature components** (dashboard, billing, automation, etc.) | 80 files |
| **Form components** (`components/form/`) | 25 files |
| **Header components** | 6 files |
| **Stores** (Zustand) | 11 files |
| **Services/API** | 8 files (5,626 LOC) |
| **Templates** | 5 files |
| **Config files** | 24 files |
| **Hooks** | 9 files |
| **Utils** | 14 files |
| **Context providers** | 5 files |
| **Layout/shell** | 5 files |
| **Marketing site** | 22 files |
### Origin Breakdown
| Origin | Count | Percentage |
|--------|-------|------------|
| **Custom** (IGNY8-specific) | ~340 | ~70% |
| **TailAdmin-original** (unchanged) | ~25 | ~5% |
| **TailAdmin-modified** (customized) | ~20 | ~4% |
| **TailAdmin-derived** (structural inheritance) | ~15 | ~3% |
| **TailAdmin demo** (static examples) | ~10 | ~2% |
| **Third-party** | ~5 | ~1% |
| **Infrastructure** (config, types, barrel exports) | ~68 | ~14% |
### Key Findings
1. **Package name confirms TailAdmin base**: `package.json``"name": "tailadmin-react"`, version `1.8.4`
2. **Core shell is TailAdmin**: Layout, sidebar, header, dark mode, backdrop all follow TailAdmin Free patterns
3. **Design system is heavily customized**: Tailwind v4 CSS-first config with `color-mix()` computed palettes, 6 base hex tokens only
4. **Config-driven table system**: 10 pages use `TablePageTemplate` with declarative column/filter/action configs — the most mature pattern
5. **Massive API monolith**: `api.ts` is 3,056 lines containing 90+ functions
6. **Three-way route divergence**: Routes defined independently in `App.tsx`, `AppSidebar.tsx`, and `routes.config.ts`
7. **Significant duplication**: Settings forms (6 pages), billing pages (3 directories), filter boilerplate (8 pages), manual tables (4+ pages)
8. **MUI contamination**: One component (`BulkWordPressPublish` in `BulkWordPressPublish/`) uses `@mui/material` — inconsistent with rest of codebase
9. **No `cn()` utility**: TailAdmin's `cn()` pattern not used; `clsx` + `twMerge` called separately
10. **Style-locked components**: 7 files have `🔒 STYLE LOCKED` headers (Button, IconButton, Badge, Table, WordPressPublish components)
---
## 2. App Shell & Layout
### Layout Structure
```
<ErrorBoundary>
<ThemeProvider> ← dark mode (class strategy)
<HeaderMetricsProvider> ← credit/page metric badges
<ToastProvider> ← toast notifications
<BrowserRouter>
<PageProvider> ← page title/badge context
<HelmetProvider>
<Routes>
<ProtectedRoute> ← JWT auth guard
<SidebarProvider> ← sidebar expand/collapse state
<PageLoadingProvider>
<AppLayout> ← flex shell (sidebar + main)
<AppSidebar> ← left nav (290px / 90px)
<Backdrop> ← mobile overlay
<AppHeader> ← sticky top bar
<Outlet> ← page content
```
**Origin:** `AppLayout`, `AppSidebar`, `AppHeader`, `Backdrop`**TailAdmin-modified**. Core structure from TailAdmin Free but heavily extended with Zustand stores, credit metrics, module system, and page context.
### Sidebar Configuration
| Aspect | Implementation |
|--------|---------------|
| **Nav structure** | **Hardcoded** in `AppSidebar.tsx` via `useMemo` arrays |
| **Sections** | Dashboard (standalone), SETUP (Setup Wizard, Sites, Keywords Library, Thinker), WORKFLOW (Planner, Writer, Publisher, Automation) |
| **Collapsible groups** | Yes — TailAdmin pattern with `ChevronDownIcon` rotation, animated height |
| **Module gating** | `useModuleStore().isModuleEnabled()` (API-driven) |
| **Admin filtering** | User role check for admin-only items |
| **Config-driven?** | No — `routes.config.ts` exists but is **not used** by sidebar |
| **TailAdmin patterns** | `menu-item` / `menu-item-active` / `menu-item-inactive` CSS utility classes, 90px→290px hover expansion, `no-scrollbar` |
### Header Structure
- Sticky header with `z-99999`
- Mobile: hamburger toggle + logo
- Desktop: page title with colored badge (from `PageContext`), sidebar toggle
- Right side: `HeaderMetrics` (credit badges), route-conditional site/sector selectors (3 variants based on URL), search (⌘K), `ThemeToggleButton`, `NotificationDropdown`, `UserDropdown`
- **Origin:** TailAdmin-modified — dropdown patterns and responsive layout from TailAdmin, but metric badges, page context, and site selectors are custom
### Routing
**Three independent route definitions exist (maintenance risk):**
| Source | Purpose | Route Count | Synchronized? |
|--------|---------|-------------|---------------|
| `App.tsx` | Actual React Router routes | ~100+ | Source of truth |
| `AppSidebar.tsx` | Sidebar navigation items | ~25 | Subset of App.tsx |
| `routes.config.ts` | Breadcrumb metadata | ~15 | Partial, stale |
- All page components (except auth) are **lazy-loaded** via `React.lazy()`
- `ProtectedRoute` wraps all authenticated routes
- `AdminRoute` wraps Thinker/Integration routes
- `<Navigate>` handles legacy route redirects
---
## 3. Design System Status
### Color System
**Architecture:** 6 base hex tokens → `color-mix()` generates full palettes → Tailwind `@theme` consumes them
| Token | Value | Palette Generated |
|-------|-------|-------------------|
| `--color-primary` | `#1c86d1` | `brand-25``brand-950` |
| `--color-success` | `#3fcd9f` | `success-25``success-950` |
| `--color-warning` | `#f87f4c` | `warning-25``warning-950` |
| `--color-danger` | `#ff656f` | `error-25``error-950` |
| `--color-info` | `#18b2c4` | `info-25``info-950` |
| `--color-gray-base` | `#243249` | `gray-25``gray-950` + `gray-dark` |
**All default Tailwind color palettes are disabled** (`--color-red-*: initial`, etc.). Only the above semantic palettes exist.
Additional derived tokens: `--navy`, `--surface`, `--panel`, `--text`, `--text-dim`, `--stroke`, gradients.
**Module color mapping** in `colors.config.ts`: Every module stage (keywords, clusters, ideas, tasks, content, images, review, approved, published) maps to `bg`, `bgLight`, `text`, `border`, `gradient`, and `cssVar` tokens.
### Typography
| Property | Value |
|----------|-------|
| **Font family** | `Outfit` (Google Fonts), `ui-monospace` for code |
| **Title sizes** | `title-2xl` (72px) → `title-sm` (30px) |
| **Body sizes** | `theme-xl` (20px), `theme-sm` (14px), `theme-xs` (12px) |
| **CMS fonts** | Separate system — `Inter` for published content |
### Spacing
Standard Tailwind spacing scale with custom additions:
- Border radius: `xs` (2px) → `4xl` (20px)
- Shadows: `theme-xs``theme-xl`, plus `datepicker`, `focus-ring`, `tooltip`
- Z-index: 6 tiers from `z-index-1` to `z-index-999999`
### Dark Mode
| Aspect | Implementation |
|--------|---------------|
| **Strategy** | Tailwind `class` strategy — `.dark` on `<html>` |
| **Storage** | `localStorage` key `"theme"` |
| **Toggle** | `ThemeContext``toggleTheme()` |
| **Default** | Light |
| **CSS** | `@custom-variant dark (&:is(.dark *))` in design-system.css |
| **Override vars** | Surface, panel, text, stroke, shadow overrides in `.dark` block |
### Responsive Approach
| Breakpoint | Value | Usage |
|------------|-------|-------|
| `2xsm` | 375px | Mobile-small |
| `xsm` | 425px | Mobile |
| `sm` | 640px | Small tablet |
| **`md`** | **768px** | **Mobile detection** (SidebarContext) |
| **`lg`** | **1024px** | **Primary:** sidebar show/hide, header layout |
| **`xl`** | **1280px** | Flex layout trigger |
| `2xl` | 1536px | Wide desktop |
| `3xl` | 2000px | Ultra-wide |
---
## 4. Component Inventory
### 4.1 UI Primitives (`components/ui/`)
| Path | Component | Purpose | Origin | Key UI Info |
|------|-----------|---------|--------|-------------|
| `ui/button/Button.tsx` | `Button` | Multi-variant button (primary/secondary/outline/ghost/gradient) × 5 tones × 6 sizes | custom | 🔒 STYLE LOCKED. `twMerge` + `clsx` |
| `ui/button/IconButton.tsx` | `IconButton` | Icon-only button with tone/variant/shape system | custom | 🔒 STYLE LOCKED |
| `ui/button/ButtonWithTooltip.tsx` | `ButtonWithTooltip` | Wraps Button + Tooltip for disabled state hint | custom | |
| `ui/card/Card.tsx` | `Card` + 7 sub-components | Card container system (surface/panel/frosted/borderless/gradient variants) | custom | Uses `--color-panel` CSS var |
| `ui/modal/index.tsx` | `Modal` | Portal-based modal with backdrop blur, ESC, scroll lock | tailadmin-derived | `z-99999` convention |
| `ui/table/index.tsx` | `Table` + 4 sub-components | Unstyled table primitives | custom | 🔒 STYLE LOCKED |
| `ui/tabs/Tabs.tsx` | `Tabs`, `TabList`, `Tab`, `TabPanel` | Segmented control/tabs with render-prop state | tailadmin-derived | `shadow-theme-xs` token |
| `ui/badge/Badge.tsx` | `Badge` | 4 variants × 12 tones × 3 sizes with backward-compat | custom | 🔒 STYLE LOCKED |
| `ui/alert/Alert.tsx` | `Alert` | Inline notification banner with variant icons | tailadmin-derived | SVG icons match TailAdmin |
| `ui/alert/AlertModal.tsx` | `AlertModal` | Centered modal alert with decorative icons | custom | |
| `ui/accordion/Accordion.tsx` | `Accordion`, `AccordionItem` | Collapsible with animated height | custom | |
| `ui/breadcrumb/Breadcrumb.tsx` | `Breadcrumb` | Navigation breadcrumb from items array | tailadmin-derived | Chevron SVG matches TailAdmin |
| `ui/dropdown/Dropdown.tsx` | `Dropdown` | Portal-based positioned dropdown with edge detection | custom | Major rewrite from TailAdmin |
| `ui/dropdown/DropdownItem.tsx` | `DropdownItem` | Menu item (button or link) | tailadmin-derived | |
| `ui/spinner/Spinner.tsx` | `Spinner` | CSS border-based loading spinner | custom | |
| `ui/pagination/Pagination.tsx` | `Pagination` | Full pagination with ellipsis | tailadmin-derived | Arrow SVGs match TailAdmin |
| `ui/pagination/CompactPagination.tsx` | `CompactPagination` | Compact pagination + page size selector | custom | |
| `ui/progress/ProgressBar.tsx` | `ProgressBar` | Horizontal progress bar | custom | |
| `ui/tooltip/Tooltip.tsx` | `Tooltip` | Portal-based tooltip with directional arrow | custom | |
| `ui/tooltip/EnhancedTooltip.tsx` | `EnhancedTooltip` | Rich-content tooltip with delay | custom | |
| `ui/tooltip/CalendarItemTooltip.tsx` | `CalendarItemTooltip` | Calendar event tooltip | custom | Business-specific |
| `ui/toast/Toast.tsx` | `ToastItem` | Individual toast notification (5s auto-dismiss) | custom | `animate-slide-in-right` |
| `ui/toast/ToastContainer.tsx` | `ToastProvider`, `useToast` | Context-based toast system | custom | Portal, fixed top-right |
| `ui/ribbon/Ribbon.tsx` | `Ribbon` | Decorative ribbon overlay | tailadmin-derived | Corner variant matches TailAdmin |
| `ui/list/List.tsx` | `List` + 5 sub-components | Multi-variant list with checkbox/radio items | tailadmin-derived | Extended with checkbox/radio |
| `ui/button-group/ButtonGroup.tsx` | `ButtonGroup`, `ButtonGroupItem` | Grouped button strip | tailadmin-derived | `shadow-theme-xs` |
| `ui/dataview/DataView.tsx` | `DataView` + 3 sub-components | Data display container with header/toolbar/empty | custom | SaaS-specific |
| `ui/pricing-table/PricingTable.tsx` | `PricingTable` (3 variants) | Full pricing table with toggle, discount calc | tailadmin-derived | Extended with dynamic pricing |
| `ui/pricing-table/index.tsx` | `PricingTable` (named export) | Alternate SaaS-specific pricing table | custom | **Duplicate** of above |
| `ui/avatar/Avatar.tsx` | `Avatar` | User avatar with status dot | tailadmin-derived | |
| `ui/videos/*.tsx` | Video aspect ratio components | Hardcoded YouTube embeds | tailadmin-demo | **Bug**: SixteenIsToNine uses 4:3 |
| `ui/images/*.tsx` | Image grid components | Hardcoded demo image grids | tailadmin-demo | Static — cleanup candidates |
### 4.2 Common Components (`components/common/`)
| Path | Component | Purpose | Origin |
|------|-----------|---------|--------|
| `common/PageBreadCrumb.tsx` | `PageBreadcrumb` | Simple Home > Page breadcrumb | tailadmin-original |
| `common/ComponentCard.tsx` | `ComponentCard` | Generic card wrapper with title/description | tailadmin-modified |
| `common/GridShape.tsx` | `GridShape` | Decorative SVG background shapes | tailadmin-original |
| `common/ThemeTogglerTwo.tsx` | `ThemeTogglerTwo` | Floating round theme toggle button | tailadmin-original |
| `common/PageMeta.tsx` | `PageMeta` | `<title>` and `<meta>` via react-helmet | tailadmin-original |
| `common/ScrollToTop.tsx` | `ScrollToTop` | Auto-scroll to top on route change | tailadmin-original |
| `common/ChartTab.tsx` | `ChartTab` | Chart time period segmented control | tailadmin-original |
| `common/ThemeToggleButton.tsx` | `ThemeToggleButton` | Theme toggle using IconButton | tailadmin-modified |
| `common/SiteSelector.tsx` | `SiteAndSectorSelector` | Combined site + sector dropdown | custom |
| `common/SingleSiteSelector.tsx` | `SingleSiteSelector` | Site-only dropdown | custom |
| `common/SiteWithAllSitesSelector.tsx` | `SiteWithAllSitesSelector` | Site dropdown with "All Sites" | custom |
| `common/SiteAndSectorSelector.tsx` | `SiteAndSectorSelector` | Combined site + sector (dual-mode) | custom |
| `common/SectorSelector.tsx` | `SectorSelector` | Standalone sector dropdown | custom |
| `common/SiteInfoBar.tsx` | `SiteInfoBar` | Horizontal site info bar with badges | custom |
| `common/SiteCard.tsx` | `SiteCard` | Site card with status toggle and actions | custom |
| `common/FormModal.tsx` | `FormModal` | Generic modal with auto-generated form | custom |
| `common/ProgressModal.tsx` | `ProgressModal` | Multi-step AI progress modal | custom |
| `common/ConfirmDialog.tsx` | `ConfirmDialog` | Generic confirmation modal | custom |
| `common/ContentViewerModal.tsx` | `ContentViewerModal` | Article preview modal | custom |
| `common/ErrorDetailsModal.tsx` | `ErrorDetailsModal` | Publishing error detail modal | custom |
| `common/BulkStatusUpdateModal.tsx` | `BulkStatusUpdateModal` | Bulk status change modal | custom |
| `common/BulkScheduleModal.tsx` | `BulkScheduleModal` | Bulk scheduling modal | custom |
| `common/BulkSchedulePreviewModal.tsx` | `BulkSchedulePreviewModal` | Schedule preview before confirm | custom |
| `common/BulkExportModal.tsx` | `BulkExportModal` | Bulk export modal | custom |
| `common/BulkPublishingModal.tsx` | `BulkPublishingModal` | Bulk publishing queue modal | custom |
| `common/PublishingProgressModal.tsx` | `PublishingProgressModal` | Single-content publishing progress | custom |
| `common/PublishLimitModal.tsx` | `PublishLimitModal` | Publish limit warning | custom |
| `common/ImageQueueModal.tsx` | `ImageQueueModal` | Image generation queue | custom |
| `common/ScheduleContentModal.tsx` | `ScheduleContentModal` | Schedule single content | custom |
| `common/SearchModal.tsx` | `SearchModal` | Global search (⌘K) | custom |
| `common/SingleRecordStatusUpdateModal.tsx` | `SingleRecordStatusUpdateModal` | Single record status change | custom |
| `common/ColumnSelector.tsx` | `ColumnSelector` | Table column visibility toggle | custom |
| `common/ViewToggle.tsx` | `ViewToggle` | List/grid view toggle | custom |
| `common/ToggleTableRow.tsx` | `ToggleTableRow` | Expandable table row | custom |
| `common/HTMLContentRenderer.tsx` | `HTMLContentRenderer` | Safe HTML/JSON content renderer | custom |
| `common/ContentImageCell.tsx` | `ContentImageCell` | Table cell with image status | custom |
| `common/ImageResultCard.tsx` | `ImageResultCard` | AI image result display | custom |
| `common/ImageGenerationCard.tsx` | `ImageGenerationCard` | Image generation test form | custom |
| `common/ImageServiceCard.tsx` | `ImageServiceCard` | Image service provider card | custom |
| `common/IntegrationCard.tsx` | `IntegrationCard` | API integration toggle card | custom |
| `common/ValidationCard.tsx` | `ValidationCard` | Validation status card | custom |
| `common/WorkflowInsights.tsx` | `WorkflowInsights` | Collapsible insights notifications | custom |
| `common/PageHeader.tsx` | `PageHeader` | Page header (sets PageContext) | custom |
| `common/PageTransition.tsx` | `PageTransition` | Route transition animation | custom |
| `common/PageLoader.tsx` | `PageLoader` | Global loading indicator | custom |
| `common/SuspenseLoader.tsx` | `SuspenseLoader` | Suspense fallback spinner | custom |
| `common/ErrorBoundary.tsx` | `ErrorBoundary` | Top-level error boundary | custom |
| `common/PageErrorBoundary.tsx` | `PageErrorBoundary` | Per-page error boundary | custom |
| `common/GlobalErrorDisplay.tsx` | `GlobalErrorDisplay` | Global error overlay | custom |
| `common/LoadingStateMonitor.tsx` | `LoadingStateMonitor` | Stuck-loading detector (10s timeout) | custom |
| `common/ModuleGuard.tsx` | `ModuleGuard` | **DEPRECATED** — pass-through | custom |
| `common/DebugSiteSelector.tsx` | `DebugSiteSelector` | Debug site selector | custom |
### 4.3 Form Components (`components/form/`)
| Path | Component | Purpose | Origin |
|------|-----------|---------|--------|
| `form/input/InputField.tsx` | `InputField` | Text input with states | tailadmin-modified |
| `form/input/TextArea.tsx` | `TextArea` | Textarea with states | tailadmin-modified |
| `form/input/Checkbox.tsx` | `Checkbox` | Custom checkbox with SVG | tailadmin-modified |
| `form/input/Radio.tsx` | `Radio` | Custom radio button | tailadmin-original |
| `form/input/RadioSm.tsx` | `RadioSm` | Small radio variant | tailadmin-original |
| `form/input/FileInput.tsx` | `FileInput` | File input with pseudo-styling | tailadmin-original |
| `form/switch/Switch.tsx` | `Switch` | Toggle switch | tailadmin-modified |
| `form/group-input/PhoneInput.tsx` | `PhoneInput` | Phone with country code | tailadmin-original |
| `form/Form.tsx` | `Form` | Simple form wrapper | tailadmin-original |
| `form/Label.tsx` | `Label` | Label primitive | tailadmin-original |
| `form/Select.tsx` | `Select` | Native `<select>` styled | tailadmin-original |
| `form/SelectDropdown.tsx` | `SelectDropdown` | Custom dropdown select | custom |
| `form/MultiSelect.tsx` | `MultiSelect` | Multi-select with chips | tailadmin-modified |
| `form/FormFieldRenderer.tsx` | `FormFieldRenderer` | Dynamic field renderer from config | custom |
| `form/date-picker.tsx` | `DatePicker` | flatpickr-based date picker | third-party |
| `form/form-elements/*.tsx` | 9 demo components | Form element showcase pages | tailadmin-original |
### 4.4 Header Components (`components/header/`)
| Path | Component | Purpose | Origin |
|------|-----------|---------|--------|
| `header/Header.tsx` | `Header` | Main header with search, theme, dropdowns | tailadmin-modified |
| `header/SiteSwitcher.tsx` | `SiteSwitcher` | Site switch dropdown | custom |
| `header/HeaderMetrics.tsx` | `HeaderMetrics` | Credit/page metric badges | custom |
| `header/NotificationDropdown.tsx` | `NotificationDropdown` | Notification dropdown with API sync | custom |
| `header/NotificationDropdownNew.tsx` | `NotificationDropdownNew` | Alternate notification dropdown (older?) | custom |
| `header/UserDropdown.tsx` | `UserDropdown` | User profile menu | tailadmin-modified |
### 4.5 Feature Components
#### Dashboard (`components/dashboard/`) — 22 components, ALL custom
| Component | Purpose |
|-----------|---------|
| `EnhancedMetricCard` | KPI card with accent border, trend, tooltip |
| `WorkflowPipeline` / `WorkflowPipelineWidget` | Visual 7-stage pipeline |
| `NeedsAttentionBar` | Collapsible alert bar |
| `StandardizedModuleWidget` | Module pipeline stats |
| `WorkflowCompletionWidget` | Time-filtered workflow stats |
| `AIOperationsWidget` | AI operation statistics |
| `RecentActivityWidget` | Last 5 activities |
| `CreditsUsageWidget` / `CreditBalanceWidget` / `CreditAvailabilityWidget` | Credit displays |
| `AutomationStatusWidget` | Automation run status |
| `SitesOverviewWidget` | Site list with status |
| `UsageChartWidget` | Usage chart with date range |
| `KeywordLibraryStatsWidget` | Keyword library stats |
| `AccountInfoWidget` | Account billing info |
| `SiteConfigWidget` | Site setup checklist |
| `QuickActionsWidget` | 8-step workflow guide |
| `ContentVelocityWidget` | Content production rates |
| `OperationsCostsWidget` | AI operation costs |
| `ThreeWidgetFooter` / `StandardThreeWidgetFooter` | 3-column footer layouts |
#### Billing (`components/billing/`) — 17 components, ALL custom
Key components: `BillingBalancePanel`, `BillingUsagePanel`, `CreditInsightsCharts`, `PayInvoiceModal`, `InsufficientCreditsModal`, `PendingPaymentBanner`, `PendingPaymentView`, `PaymentHistory`, `UsageLimitsPanel`, `CreditCostBreakdownPanel`, `CreditCostsPanel`
#### Automation (`components/Automation/`) — 20 components, ALL custom
Top-level: `ConfigModal`, `CurrentProcessingCard`/V2, `GlobalProgressBar`, `RunHistory`, `StageCard`, `ActivityLog`
DetailView: `RunSummaryCard`, `StageAccordion`, `InsightsPanel`, `EfficiencyMetrics`, `CreditBreakdownChart`, `PredictiveCostAnalysis`, `ProductionSummary`, `EnhancedRunHistory`, `MeaningfulRunHistory`, `PipelineOverviewCard`, `AttentionItemsAlert`, `RunStatisticsSummary`
#### Sites (`components/sites/`) — 11 components, ALL custom
Key: `SiteSetupChecklist`, `SiteProgressWidget`, `WordPressIntegrationCard`/`Form`/`Modal`, `TemplateLibrary`, `TemplateCustomizer`, `StyleEditor`, `LayoutSelector`/`Preview`, `SiteTypeBadge`
#### WordPress Publish (`components/WordPressPublish/`) — 4 components, ALL custom STYLE LOCKED
`WordPressPublish`, `BulkWordPressPublish`, `ContentActionsMenu`
#### Other Feature Components — ALL custom
| Directory | Components |
|-----------|-----------|
| `onboarding/` | `OnboardingWizard`, `WorkflowGuide`, Steps 1-5 (7 files) |
| `keywords-library/` | `SectorCardsGrid`, `SectorMetricCard`, `SmartSuggestions`, `BulkAddConfirmation` |
| `tasks/` | `KanbanBoard` (tailadmin-modified), `TaskList` (tailadmin-modified), `RelationshipMap` |
| `content/` | `ContentFilter`, `SourceBadge`, `SyncStatusBadge` |
| `linker/` | `LinkResults` |
| `optimizer/` | `OptimizationScores`, `ScoreComparison` |
| `integration/` | `IntegrationStatus`, `PlatformSelector`, `SiteIntegrationsSection` |
| `publishing/` | `PublishingRules` |
| `auth/` | `AdminRoute`, `ProtectedRoute`, `SignInForm`, `SignUpFormUnified` |
| `navigation/` | `ModuleNavigationTabs` |
| `UserProfile/` | `UserAddressCard`, `UserInfoCard`, `UserMetaCard` (tailadmin-original) |
---
## 5. Shared Components — Usage Patterns
### Most-Used Shared Components
| Component | Estimated Usage Count | Notes |
|-----------|----------------------|-------|
| `Button` | 50+ pages | Universal — primary UI action element |
| `Modal` | 30+ components | All modals in codebase |
| `Badge` | 25+ pages | Status/label badges |
| `Card` | 20+ widgets | Dashboard widgets, cards |
| `InputField` | 15+ forms | All text inputs |
| `SelectDropdown` | 12+ forms | Custom dropdown selects |
| `Dropdown`/`DropdownItem` | 10+ menus | Header, site selectors, actions |
| `Tooltip` / `EnhancedTooltip` | 10+ | Pipeline, metrics, help text |
| `CompactPagination` | 10 pages | All TablePageTemplate pages |
| `Alert` | 8+ pages | Inline notifications |
| `Switch` | 6+ | Settings toggles |
| `Spinner` | 5+ | Loading states |
| `Accordion` | 4+ | Automation details, settings |
### Duplicate Patterns Found
| Pattern | Instances | Details |
|---------|-----------|---------|
| **Site selector variants** | 4 | `SiteSelector`, `SingleSiteSelector`, `SiteWithAllSitesSelector`, `SiteAndSectorSelector` — could be 1 component with props |
| **Notification dropdown** | 2 | `NotificationDropdown` + `NotificationDropdownNew` — one is likely stale |
| **Pricing table** | 2 | `PricingTable.tsx` (default) + `index.tsx` (named) — overlapping implementations |
| **Theme toggle** | 2 | `ThemeTogglerTwo` (floating) + `ThemeToggleButton` (header icon) |
| **BulkWordPressPublish** | 2 | `WordPressPublish/BulkWordPressPublish.tsx` (custom UI) + `BulkWordPressPublish/BulkWordPressPublish.tsx` (**MUI-based** — different library!) |
| **Error boundary** | 2 | `ErrorBoundary` (top-level) + `PageErrorBoundary` (per-page) — similar patterns |
| **Three-widget footer** | 2 | `ThreeWidgetFooter` + `StandardThreeWidgetFooter` |
| **Date formatting** | 2 | `dateUtils.ts` + `date.ts` — overlapping functions (`formatDateTime` exists in both) |
### Inconsistencies
| Issue | Details |
|-------|---------|
| **Icon library mixing** | `PaymentGatewaySelector.tsx` uses `lucide-react` instead of `../../icons`; `BulkWordPressPublish/` uses `@mui/icons-material` |
| **Import path inconsistency** | `PaymentGatewaySelector.tsx` uses `@/` alias; rest of codebase uses relative paths |
| **Inline buttons** | `PageErrorBoundary`, `ErrorBoundary` use raw `<button>` instead of shared `Button` component |
| **Inline table styling** | Sites Dashboard, Automation pages have inline table implementations instead of using `Table` primitives |
| **Settings form patterns** | No shared settings form template — each settings page builds its own form layout |
---
## 6. Page-by-Page Breakdown
### Template Distribution
| Template | Pages Using It |
|----------|---------------|
| **`TablePageTemplate`** (config-driven) | Keywords, Clusters, Ideas, Tasks, Content, Review, Approved, Images, Sites/List, Setup/IndustriesSectorsKeywords |
| **`ContentViewTemplate`** | ContentView (single article detail) |
| **`DashboardTemplate`** (structure, not widely used) | Available but most dashboard pages are custom |
| **`FormPageTemplate`** (settings layout) | Available but not widely adopted |
| **Custom layouts** | ~50 pages |
| **TailAdmin demo/reference** | UIElements, Components |
| **Placeholder/stub** | 5 pages |
### Planner Module (4 pages)
| Page | Template | Notes |
|------|----------|-------|
| Keywords | `TablePageTemplate` | Config-driven via `keywords.config.tsx`. Auto-cluster, import/export. |
| Clusters | `TablePageTemplate` | Config-driven via `clusters.config.tsx`. Auto-generate ideas. |
| ClusterDetail | Custom | Tabbed detail view (keywords, ideas, stats). Manual table. |
| Ideas | `TablePageTemplate` | Config-driven via `ideas.config.tsx`. Bulk queue to writer. |
### Writer Module (6 pages)
| Page | Template | Notes |
|------|----------|-------|
| Tasks | `TablePageTemplate` | Config-driven. Auto-generate content/images. Has Kanban/List toggles. |
| Content | `TablePageTemplate` | Config-driven. Inline editing disabled. |
| Review | `TablePageTemplate` | Config-driven. Content review workflow. |
| Approved | `TablePageTemplate` | Config-driven. Scheduling, publishing. |
| Images | `TablePageTemplate` | Config-driven. Dynamic in-article image columns. |
| ContentView | `ContentViewTemplate` | Single article detail with scheduling, image management. |
### Publisher Module (2 pages)
| Page | Template | Notes |
|------|----------|-------|
| ContentCalendar | Custom | FullCalendar integration with month/week/list views. Custom scheduling modals. |
| PublishSettings | Custom | Multi-tab settings (general, frequency, scheduling). Inline form. |
### Sites Module (11 pages)
| Page | Template | Notes |
|------|----------|-------|
| List | `TablePageTemplate` | Sites management list. |
| Dashboard | Custom | Multi-section dashboard with widget grid. Manual tables. |
| Settings | Custom | Tab-based site settings. Inline forms. |
| Content | Custom | Site content management with source filtering. |
| ContentStructure | Custom | Content structure config. |
| PostEditor | Custom | Post editing interface. |
| PageManager | Custom | Site page management. |
| SyncDashboard | Custom | WordPress sync status. |
| PublishingQueue | Custom | Publishing queue with status tracking. |
| AIAutomationSettings | Custom | Per-site automation config. |
| DeploymentPanel | Custom | **Deprecated** placeholder. |
### Automation Module (4 pages)
| Page | Template | Notes |
|------|----------|-------|
| AutomationPage | Custom | Main automation view with config, processing, history. |
| AutomationOverview | Custom | Multi-site overview with eligibility status. |
| AutomationRunDetail | Custom | Detailed run breakdown with stage accordion. |
| PipelineSettings | Custom | Pipeline stage configuration form. |
### Thinker Module (5 pages)
| Page | Template | Notes |
|------|----------|-------|
| Prompts | Custom | Prompt library with CRUD and categories. |
| AuthorProfiles | Custom | Author voice profiles with CRUD. |
| Strategies | Custom | Content strategy management. |
| Profile | Custom | User profile view (TailAdmin-original layout). |
| ImageTesting | Custom | Image generation testing interface. |
### Other Modules
| Module | Page | Template | Notes |
|--------|------|----------|-------|
| Optimizer | ContentSelector | Custom | Content selection for optimization. |
| Optimizer | AnalysisPreview | Custom | Optimization results preview. |
| Linker | ContentList | Custom | Internal linking content list. |
| Setup | SetupWizard | Custom | Multi-step onboarding wizard. |
| Help | Help | Custom | Help center page. |
| Reference | Industries, SeedKeywords | Custom | Read-only reference data. |
### Settings (12 pages) — High duplication risk
| Page | Notes |
|------|-------|
| General | Inline form layout |
| Publishing | Tab-based publishing config |
| Integration | Integration CRUD |
| Plans | Plan display (duplicated in account/) |
| Subscriptions | Subscription management (duplicated) |
| CreditsAndBilling | Billing info (duplicated) |
| Industries | Industry selection |
| Account | Account settings (duplicated in account/) |
| Sites | Sites management |
| Users | User management (admin) |
| System | System admin dashboard |
| WordPressIntegrationDebug | Debug tool |
### Account (7 pages) — Supersedes Settings billing
| Page | Notes |
|------|-------|
| AccountSettingsPage | Account settings + profile |
| ContentSettingsPage | Content generation settings |
| NotificationsPage | Notification preferences |
| PlansAndBillingPage | Plan + billing unified view |
| PurchaseCreditsPage | Credit purchase flow |
| UsageDashboardPage | Usage overview |
| UsageAnalyticsPage | Detailed usage analytics |
### Auth Pages (7 pages)
| Page | Origin | Notes |
|------|--------|-------|
| AuthPageLayout | tailadmin-modified | Split-screen auth layout |
| SignIn | tailadmin-modified | Custom logic, TailAdmin layout |
| SignUp | tailadmin-modified | Custom with inline pricing |
| ForgotPassword | tailadmin-modified | TailAdmin layout |
| ResetPassword | tailadmin-modified | TailAdmin layout |
| VerifyEmail | custom | Email verification flow |
| Unsubscribe | custom | Email unsubscribe |
### Demo/Reference Pages
| Page | Origin | Notes |
|------|--------|-------|
| UIElements | tailadmin-original | 737 lines — TailAdmin component showcase |
| Components | tailadmin-original | 680 lines — TailAdmin demo page |
| NotFound | tailadmin-original | 404 page with grid shapes |
---
## 7. Stores & API Layer
### Zustand Stores (11 stores, 1,802 LOC)
| Store | State Managed | LOC | Persistence | API Calls |
|-------|--------------|-----|-------------|-----------|
| `authStore` | User, tokens, auth state | 540 | localStorage (`auth-storage`) | 5 endpoints (raw `fetch`) |
| `siteStore` | Active site | 140 | localStorage (`site-storage`) | `GET /v1/auth/sites/` |
| `sectorStore` | Active sector, sector list | 168 | localStorage (`sector-storage`) | `GET /v1/auth/sites/{id}/sectors/` |
| `plannerStore` | Keywords, clusters | 110 | None | 2 endpoints (stubs for CUD) |
| `billingStore` | Balance, usage, limits | 82 | None | 3 billing endpoints |
| `notificationStore` | Notifications, unread count | 336 | None (in-memory) | 5 notification endpoints |
| `onboardingStore` | Guide state | 102 | localStorage (`onboarding-storage`) | 3 user-setting endpoints |
| `settingsStore` | Account/module settings | 195 | localStorage (`settings-storage` v4) | 6 settings endpoints |
| `moduleStore` | Module enablement | 56 | None | 1 endpoint |
| `columnVisibilityStore` | Column visibility per page | 53 | Direct localStorage | None |
| `pageSizeStore` | Global page size | 20 | localStorage (`igny8-page-size`) | None |
### API Service Layer (8 files, 5,626 LOC)
| File | LOC | Organization | Endpoints |
|------|-----|-------------|-----------|
| **`api.ts`** | **3,056** | **Monolithic** — 90+ functions | 75+ endpoints across planner, writer, sites, sectors, settings, billing, keywords-library |
| `billing.api.ts` | 1,507 | Well-organized (sectioned) | 50+ billing/payment/subscription endpoints |
| `automationService.ts` | 438 | Well-organized | 19 automation endpoints |
| `unifiedSettings.api.ts` | 207 | Well-organized | 2 unified settings endpoints |
| `integration.api.ts` | 158 | Well-organized | 7 integration endpoints |
| `notifications.api.ts` | 142 | Well-organized | 5 notification endpoints |
| `optimizer.api.ts` | 86 | Well-organized | 3 optimizer endpoints |
| `linker.api.ts` | 32 | Well-organized | 2 linker endpoints |
**Auth pattern:** All APIs use JWT Bearer tokens via `fetchAPI()`. Token sourced from Zustand → localStorage fallback. Auto-refresh on 401. Force-logout on refresh failure.
**`api.ts` should be split** — it contains planner, writer, sites, sectors, settings, billing, and keywords-library endpoints in a single file. The other service files demonstrate the correct pattern.
### API Directory (`api/`)
| File | Exports | Purpose |
|------|---------|---------|
| `optimizer.api.ts` | `optimizerApi` | Optimize, batch optimize, analyze |
| `linker.api.ts` | `linkerApi` | Process, batch process internal links |
---
## 8. Styling Audit
### Tailwind Configuration
| Aspect | Details |
|--------|---------|
| **Version** | Tailwind CSS 4 (CSS-first config via `@theme` blocks) |
| **Config file** | No `tailwind.config.js` — all in `design-system.css` `@theme` |
| **PostCSS** | `@tailwindcss/postcss` plugin only |
| **Dark mode** | `@custom-variant dark (&:is(.dark *))` |
| **Default palettes** | **All disabled** (`--color-red-*: initial`, etc.) |
| **Custom palettes** | 6 semantic scales via `color-mix()` |
| **Font** | `Outfit` (Google Fonts) |
| **Breakpoints** | Standard + `2xsm`(375), `xsm`(425), `3xl`(2000) |
### CSS Files
| File | Lines | Purpose |
|------|-------|---------|
| `styles/design-system.css` | 1,006 | Master design system — tokens, `@theme`, base, utilities, components |
| `marketing/styles/marketing.css` | 210 | Marketing site — re-imports design-system + own `@theme` |
| `components/shared/blocks/blocks.css` | 125 | CMS block component styles |
| `components/shared/layouts/layouts.css` | 80 | CMS layout scaffolding |
### Custom CSS Utilities
| Category | Classes | Purpose |
|----------|---------|---------|
| Sidebar nav | `menu-item`, `menu-item-active`, `menu-item-inactive`, `menu-item-icon-*`, `menu-dropdown-*` | TailAdmin sidebar styling via `@utility` |
| Tables | `igny8-table-*`, `igny8-data-row`, `igny8-skeleton-row` | Compact table styling, loading transitions |
| Header | `igny8-header-metric*` | Header metric badges |
| Filters | `igny8-filter-bar` | Filter bar layout |
| Selects | `igny8-select-styled` | Native select custom chevron |
| Keywords | `keywords-library-sector-*` | Keyword sector cards |
| Misc | `no-scrollbar`, `chat-height`, `inbox-height` | Utility classes |
| Animations | `animate-slide-in-right` | Toast slide-in |
### CMS Theme System (`styles/cms/`)
Separate TypeScript-based theme system for CMS-published content (not admin UI):
- `colors.ts` — Color scheme definitions (blue, purple, green)
- `typography.ts` — Typography presets (modern, classic, editorial, minimal, tech)
- `components.ts` — Component style defaults (buttons, cards, inputs)
- `presets.ts` — Complete theme presets (modern, classic, minimal, bold, elegant, tech)
### Style Issues Found
| Issue | Severity | Details |
|-------|----------|---------|
| Marketing CSS hex conflict | Medium | `marketing.css` has hardcoded hex color scales that conflict with `design-system.css` `color-mix()` values |
| Hardcoded hex in blocks.css/layouts.css | Low | CMS CSS uses raw hex instead of CSS variables |
| Dual `@custom-variant dark` | Low | Declared in both design-system.css and marketing.css (redundant) |
| 30 `!important` usages | Low | For ApexCharts/jVectorMap overrides — acceptable |
| 4 commented-out palettes | Low | Development artifacts in design-system.css `:root` |
---
## 9. Duplication & Inconsistency Report
### Critical Duplications
| # | Pattern | Locations | Impact |
|----|---------|-----------|--------|
| 1 | **Settings form layout** | 6+ Settings pages + 7 Account pages (13 total, partially overlapping) | No shared `FormPageTemplate` adoption. Each page builds its own form layout. |
| 2 | **Billing/Credits pages** | `Settings/CreditsAndBilling` + `Billing/` (3 pages) + `account/PlansAndBillingPage` + `account/PurchaseCreditsPage` + `account/UsageDashboardPage` | 7+ pages covering similar billing/credit ground |
| 3 | **API monolith** | `api.ts` (3,056 LOC) | 90+ functions in one file. Other service files demonstrate the pattern to follow. |
| 4 | **Site selector components** | 4 components (`SiteSelector`, `SingleSiteSelector`, `SiteWithAllSitesSelector`, `SiteAndSectorSelector`) | Could be unified into 1 component with mode prop |
| 5 | **Manual tables** | Sites/Dashboard, Automation pages, Billing pages, Thinker pages | Should use `Table` primitives or `TablePageTemplate` |
| 6 | **Filter boilerplate** | 8 table pages each have ~50-100 lines of cascading filter state | Now handled by `TablePageTemplate` — older pages not migrated |
| 7 | **Three-way route config** | `App.tsx` + `AppSidebar.tsx` + `routes.config.ts` | Navigation/routing defined in 3 places independently |
| 8 | **Date formatting** | `dateUtils.ts` + `date.ts` | Overlapping `formatDateTime` functions |
| 9 | **BulkWordPressPublish** | `WordPressPublish/` (custom UI) + `BulkWordPressPublish/` (MUI) | Two implementations using different UI libraries |
| 10 | **Notification dropdown** | `NotificationDropdown` + `NotificationDropdownNew` | One appears stale |
### Inconsistent UI Patterns
| Pattern | Expected | Actual |
|---------|----------|--------|
| Icon library | `../../icons` (custom SVG) | `lucide-react` in `PaymentGatewaySelector`, `@mui/icons-material` in `BulkWordPressPublish/` |
| Component library | Custom `ui/` | MUI (`@mui/material`) in `BulkWordPressPublish/` |
| Import paths | Relative (`../../`) | `@/` alias in `PaymentGatewaySelector` |
| Button usage | `<Button>` component | Raw `<button>` in error boundaries |
| Table implementation | `Table` primitives or `TablePageTemplate` | Inline `<table>` in 4+ pages |
| Form layout | Should use `FormPageTemplate` | Each settings page custom |
| Tab component | `<Tabs>` from `ui/tabs/` | Inline tab implementations in 3+ pages |
### Components That Should Be Shared But Aren't
| Pattern | Pages Implementing It | Proposed Shared Component |
|---------|----------------------|--------------------------|
| Settings form page with sections + save bar | 13 settings/account pages | Adopt `FormPageTemplate` universally |
| Status badge (content status) | 10+ pages | Already exists as `Badge` but inline styling still found |
| Metric summary cards (4-up grid) | Dashboard, Billing, Automation | `MetricCardGrid` component |
| Confirm + Execute pattern (modal → API → toast) | 20+ actions across pages | `useConfirmAction` hook |
| Pagination + page size | Manual in 4+ pages | Already handled by `TablePageTemplate` — migrate remaining |
---
## 10. TailAdmin Pro Migration Plan
### TailAdmin Pro Components That Would Replace Existing
| TailAdmin Pro Component | Replaces Current | Priority |
|------------------------|------------------|----------|
| **Layout shell** (Sidebar, Header, Footer) | `layout/AppSidebar.tsx`, `layout/AppHeader.tsx`, `layout/Backdrop.tsx`, `layout/AppLayout.tsx` | Phase 1 |
| **Button** (all variants) | `ui/button/Button.tsx` (🔒 STYLE LOCKED — needs careful migration) | Phase 1 |
| **Modal/Dialog** | `ui/modal/index.tsx` | Phase 1 |
| **Form inputs** (Input, TextArea, Select, Checkbox, Radio, Switch, FileInput) | All `form/input/` + `form/switch/` + `form/Select.tsx` | Phase 2 |
| **Table** | `ui/table/index.tsx` (🔒 STYLE LOCKED) | Phase 2 |
| **Badge** | `ui/badge/Badge.tsx` (🔒 STYLE LOCKED) | Phase 2 |
| **Tabs** | `ui/tabs/Tabs.tsx` | Phase 2 |
| **Alert** | `ui/alert/Alert.tsx` | Phase 2 |
| **Breadcrumb** | `ui/breadcrumb/Breadcrumb.tsx` | Phase 3 |
| **Dropdown** | `ui/dropdown/Dropdown.tsx` (custom portal — evaluate if Pro is better) | Phase 3 |
| **Pagination** | `ui/pagination/Pagination.tsx` | Phase 3 |
| **Avatar** | `ui/avatar/Avatar.tsx` | Phase 3 |
| **Tooltip** | `ui/tooltip/Tooltip.tsx` (custom portal — may keep) | Phase 3 |
| **Toast/Notification** | `ui/toast/ToastContainer.tsx` (custom system — evaluate) | Phase 3 |
| **Progress Bar** | `ui/progress/ProgressBar.tsx` | Phase 3 |
| **Accordion** | `ui/accordion/Accordion.tsx` | Phase 3 |
| **Ribbon** | `ui/ribbon/Ribbon.tsx` | Phase 3 |
| **List** | `ui/list/List.tsx` | Phase 3 |
| **ButtonGroup** | `ui/button-group/ButtonGroup.tsx` | Phase 3 |
| **Pricing Table** | `ui/pricing-table/PricingTable.tsx` + `index.tsx` | Phase 3 |
| **Card** | `ui/card/Card.tsx` (custom system — evaluate) | Phase 3 |
| **Spinner** | `ui/spinner/Spinner.tsx` | Phase 3 |
| **Date Picker** | `form/date-picker.tsx` (flatpickr — Pro may include) | Phase 3 |
| **Auth pages** | `AuthPages/SignIn.tsx`, `SignUp.tsx`, etc. | Phase 4 |
| **Profile pages** | `UserProfile/*.tsx` | Phase 4 |
| **DataView** | `ui/dataview/DataView.tsx` (custom SaaS — may not exist in Pro) | Evaluate |
| **Video/Image demos** | `ui/videos/`, `ui/images/` | Delete — use Pro examples |
### Custom Components That MUST Be Kept
These contain IGNY8-specific business logic and have no TailAdmin Pro equivalent:
| Category | Components | Reason |
|----------|-----------|--------|
| **All 5 templates** | `TablePageTemplate`, `ContentViewTemplate`, `DashboardTemplate`, `FormPageTemplate`, `SystemPageTemplate` | Core IGNY8 page architecture — config-driven |
| **All page configs** | 12 config files in `config/pages/` | Business-specific table/filter/action definitions |
| **All config snippets** | `columns.snippets.ts`, `filters.snippets.ts`, `actions.snippets.ts` | Reusable table config pieces |
| **All stores** | 11 Zustand stores | Application state management |
| **All services/API** | 8 service files | Backend API integration |
| **All hooks** | 9 custom hooks (except `useModal`) | Business logic hooks |
| **All utils** | 14 utility files | Business helpers |
| **Dashboard widgets** | 22 components | IGNY8 dashboard-specific |
| **Billing components** | 17 components | Billing/payment business logic |
| **Automation components** | 20 components | Automation pipeline UI |
| **WordPress integration** | 15+ components across sites/, WordPressPublish/ | CMS integration |
| **Onboarding wizard** | 7 components | IGNY8 setup flow |
| **Content management** | ContentFilter, SourceBadge, SyncStatusBadge | Content workflow-specific |
| **Keywords library** | 4 components | Keyword management UI |
| **Module navigation** | ModuleNavigationTabs | IGNY8 module system |
| **Header metrics** | HeaderMetrics, HeaderMetricsContext | Credit display system |
| **Page context system** | PageContext, PageHeader, PageLoader | IGNY8 page metadata |
| **Search modal** | SearchModal (⌘K) | App-wide search |
| **Site selectors** | 4 site/sector selector variants | Multi-site architecture |
| **All bulk action modals** | 8+ modal components | Workflow-specific bulk operations |
| **Colors config** | `colors.config.ts` | Module color mapping system |
| **CMS theme system** | `styles/cms/*.ts` | Published content theming |
| **Icons** | `icons/index.ts`, `icons/lazy.ts` | Custom icon registry (may need to merge with Pro icons) |
### Migration Order (Least Risk First)
#### Phase 1: Layout Shell (Highest Impact, Moderate Risk)
1. **Install TailAdmin Pro** alongside existing code
2. **Map design tokens**: Extract IGNY8's 6 base colors + `color-mix()` system → TailAdmin Pro Tailwind config
3. **Migrate layout components**: Replace `AppLayout`, `AppSidebar`, `AppHeader`, `Backdrop` with Pro equivalents. Preserve:
- Module-gated sidebar items
- Credit metric badges in header
- Site/sector selectors
- Page context system
4. **Migrate auth pages**: SignIn, SignUp, ForgotPassword, ResetPassword — closest to TailAdmin originals
5. **Delete demo files**: `UIElements.tsx`, `Components.tsx`, `ui/videos/`, `ui/images/`
#### Phase 2: Core UI Primitives (High Impact, Higher Risk)
1. **Button** → Pro Button. Must preserve: tone/variant/size matrix, gradient variant, `🔒 STYLE LOCKED` contract. Create adapter if Pro API differs.
2. **Table** → Pro Table. Must work with `TablePageTemplate` config system.
3. **Form inputs** → Pro inputs. Must preserve: error/success/disabled states, `twMerge`+`clsx` composition.
4. **Badge** → Pro Badge. Must preserve: 12 tones, 4 variants, backward-compat `color` prop.
5. **Modal** → Pro Modal. Must preserve: portal rendering, ESC/backdrop close, scroll lock.
6. **Tabs** → Pro Tabs.
#### Phase 3: Secondary UI Components (Lower Impact, Low Risk)
Breadcrumb, Dropdown, Pagination, Avatar, Tooltip, Toast, ProgressBar, Accordion, Ribbon, List, ButtonGroup, PricingTable, Card, Spinner, DatePicker.
Each is a drop-in replacement with prop mapping. Can be done component-by-component.
#### Phase 4: Page Templates & Cleanup
1. **Unify route definitions**: Consolidate `App.tsx`, `AppSidebar.tsx`, `routes.config.ts` into a single config
2. **Migrate manual tables**: Convert 4+ pages with inline tables to `TablePageTemplate`
3. **Consolidate settings pages**: Adopt `FormPageTemplate` across all settings/account pages
4. **Clean up billing duplication**: Merge Settings billing + Billing pages + Account pages
5. **Split `api.ts`**: Break into `planner.api.ts`, `writer.api.ts`, `sites.api.ts`, `settings.api.ts`, `keywords-library.api.ts`
6. **Consolidate site selectors**: Unify 4 variants into 1 configurable component
7. **Remove stale components**: `ModuleGuard` (deprecated), `NotificationDropdownNew` (likely stale), `BulkWordPressPublish/` (MUI version)
8. **Merge date utils**: Consolidate `dateUtils.ts` + `date.ts`
9. **Fix MUI contamination**: Replace `@mui/material` usage in `BulkWordPressPublish/` with custom UI components
### Color Scheme Extraction for TailAdmin Pro Config
```
Primary: #1c86d1 → brand-* scale (via color-mix)
Success: #3fcd9f → success-* scale
Warning: #f87f4c → warning-* scale
Danger: #ff656f → error-* scale
Info: #18b2c4 → info-* scale
Gray Base: #243249 → gray-* scale
Font: Outfit (Google Fonts)
Dark mode: class strategy on <html>
```
These 6 hex values + the `color-mix()` derivation system are the **only** color configuration needed in TailAdmin Pro's theme config. All other colors are computed.
### Estimated Scope Per Section
| Phase | Components Affected | Files to Modify | Risk Level |
|-------|-------------------|-----------------|------------|
| Phase 1: Layout Shell | 10 | ~20 | ⚠️ Moderate — touches every page via layout |
| Phase 2: Core Primitives | 15 | ~80 (imports update) | ⚠️ High — Button/Table/Badge used everywhere |
| Phase 3: Secondary UI | 15 | ~30 | ✅ Low — isolated replacements |
| Phase 4: Cleanup | 30+ | ~50 | ⚠️ Moderate — structural changes |
| **Total** | **~70 UI components** | **~180 files** | |
### Pre-Migration Checklist
- [ ] Obtain TailAdmin Pro React bundle
- [ ] Verify TailAdmin Pro supports Tailwind CSS 4 (CSS-first config)
- [ ] Map Pro component API to current component prop interfaces
- [ ] Create component adapter layer for breaking changes
- [ ] Set up visual regression testing (screenshots before/after)
- [ ] Document all `🔒 STYLE LOCKED` components — these need extra care
- [ ] Back up current `design-system.css` and all 6 hex tokens
- [ ] Plan for icon merging (current custom SVGs + Pro icons)
- [ ] Audit `@theme` block compatibility between current CSS and Pro CSS
- [ ] Remove `@mui/material` dependency before migration starts
---
## Appendix A: Third-Party Dependencies (UI-Related)
| Package | Version | Used By | Notes |
|---------|---------|---------|-------|
| `react` | ^19.0.0 | All | Core framework |
| `react-dom` | ^19.0.0 | All | DOM rendering |
| `react-router-dom` | ^7.9.5 | Routing, Link | v7 — latest |
| `zustand` | ^5.0.8 | 11 stores | State management |
| `tailwind-merge` | ^3.0.1 | Button, IconButton | Class dedup |
| `clsx` | ^2.1.1 | 10+ components | Conditional classes |
| `apexcharts` / `react-apexcharts` | ^4.1.0 / ^1.7.0 | Charts | Dashboard widgets |
| `@fullcalendar/*` | ^6.1.15 | ContentCalendar | Calendar view |
| `flatpickr` | ^4.6.13 | DatePicker | Date picker |
| `react-dropzone` | ^14.3.5 | DropZone | File upload |
| `react-dnd` / `react-dnd-html5-backend` | ^16.0.1 | KanbanBoard | Drag-and-drop |
| `swiper` | ^11.2.3 | Marketing site | Carousel |
| `@react-jvectormap/*` | ^1.0.4 / ^1.1.2 | Map widgets | Vector maps |
| `react-helmet-async` | ^2.0.5 | PageMeta/SEO | Head management |
| `lucide-react` | ^0.554.0 | PaymentGatewaySelector only | **Should not be used** — use `icons/` |
| `@heroicons/react` | ^2.2.0 | Icons (via icons/) | SVG icon source |
## Appendix B: File Count by Directory
| Directory | .tsx/.ts Files |
|-----------|---------------|
| `components/ui/` | 52 |
| `components/common/` | 51 |
| `components/dashboard/` | 22 |
| `components/Automation/` | 20 |
| `components/billing/` | 17 |
| `components/form/` | 25 |
| `components/sites/` | 11 |
| `components/onboarding/` | 7 |
| `components/header/` | 6 |
| `components/WordPressPublish/` | 4 |
| `components/auth/` | 4 |
| `components/tasks/` | 4 |
| `components/keywords-library/` | 5 |
| `components/content/` | 4 |
| `components/integration/` | 3 |
| `components/optimizer/` | 2 |
| `components/shared/` | 22 |
| `components/UserProfile/` | 3 |
| `components/navigation/` | 1 |
| `components/publishing/` | 1 |
| `components/linker/` | 1 |
| `components/BulkWordPressPublish/` | 1 |
| `pages/` | 68 |
| `store/` | 11 |
| `services/` | 6 |
| `api/` | 2 |
| `config/` | 24 |
| `templates/` | 5 |
| `hooks/` | 9 |
| `utils/` | 14 |
| `context/` | 5 |
| `layout/` | 5 |
| `icons/` | 2 |
| `styles/` | 5 |
| `marketing/` | 22 |
| `constants/` | 1 |
| `types/` | 1 |
| Other (main.tsx, App.tsx, etc.) | 4 |
| **Total** | **483** |

View File

@@ -0,0 +1,596 @@
# IGNY8 ↔ WordPress Integration Complete Security Audit
**Audit Date:** 2026-01-13
**Auditor:** System Audit (Documentation Calibrated)
**Scope:** Backend (Django) + WordPress Plugin + Documentation Review
**Status:** COMPLETE - CALIBRATED WITH DOCUMENTATION
---
## EXECUTIVE SUMMARY
This comprehensive audit examined the IGNY8 backend and WordPress plugin integration system, cross-referencing against all existing documentation. The audit identified **17 security vulnerabilities** (3 critical, 5 high, 6 medium, 3 low), **6 unused/redundant database fields**, **2 major data duplication issues**, and several **documentation vs. implementation gaps**.
**Key Findings:**
- API keys stored in plain text (no encryption) despite documentation claims
- Public endpoints expose sensitive configuration data
- Timing attack vulnerability in Bearer token validation
- Documentation states "credentials encrypted at rest" but encryption NOT implemented
- Several fields documented but never used
- Missing scheduled publishing task from Celery Beat (documented but not configured)
---
## PART 1: DOCUMENTATION VS IMPLEMENTATION GAPS
### 1.1 Critical Discrepancies Found
| Documentation Claim | Reality | Impact |
|---------------------|---------|--------|
| "API keys encrypted at rest" (INTEGRATIONS.md) | Plain text CharField in database | **CRITICAL** - False security assumption |
| "Webhook signature verification" (INTEGRATIONS.md) | No signature verification implemented | **HIGH** - Anyone with key can spoof webhooks |
| "Rate limiting on webhook endpoint" (INTEGRATIONS.md) | Webhooks explicitly disable throttling (`NoThrottle`) | **HIGH** - DoS possible |
| "auto_publish_enabled triggers WordPress push" | Automation Stage 7 sets status but does NOT trigger WP push | **MEDIUM** - Manual publish required |
| "Scheduled auto-publish task active" | Task exists but NOT in Celery Beat schedule | **MEDIUM** - Content sits unpublished |
| SiteIntegration.api_key field (INTEGRATIONS.md line 97) | API key stored in Site.wp_api_key, NOT SiteIntegration | Documentation outdated |
| "credentials_json stores WordPress credentials" | credentials_json is empty for WordPress; Site.wp_api_key is source of truth | Documentation outdated |
### 1.2 Documentation Accuracy Summary
| Document | Accuracy | Issues Found |
|----------|----------|--------------|
| WORDPRESS-INTEGRATION-FLOW.md | 90% | Accurate on auth, slight gaps on scheduling |
| WORDPRESS-INTEGRATION.md | 85% | Missing encryption truth, good on plugin distribution |
| PUBLISHER.md | 95% | Accurate on models and flows |
| INTEGRATIONS.md | 70% | Contains outdated field references, false encryption claim |
| SCHEDULED-CONTENT-PUBLISHING.md | 95% | Accurate on Celery tasks, correctly notes site_status vs status |
| CONTENT-PIPELINE.md | 90% | Accurate on stages, slight gap on Stage 8 auto-publish |
---
## PART 2: SYSTEM ARCHITECTURE (VERIFIED)
### 2.1 Actual Data Flow (Verified Against Code)
```
IGNY8 Backend WordPress Plugin
┌──────────────────────┐ ┌──────────────────────┐
│ Site Model │ │ wp_options │
│ ├─ wp_api_key ◄──────┼────────────────┼─► igny8_api_key │
│ │ (SINGLE SOURCE) │ │ igny8_site_id │
│ ├─ domain │ │ igny8_integration_id│
│ └─ wp_url (LEGACY) │ │ │
│ │ │ Post Meta │
│ SiteIntegration │ │ ├─ _igny8_content_id │
│ ├─ config_json │ │ ├─ _igny8_task_id │
│ │ (site_url only) │ │ └─ _igny8_last_synced│
│ ├─ credentials_json │ │ │
│ │ (EMPTY for WP!) │ │ │
│ └─ sync_status │ │ │
│ │ │ │
│ Content Model │ │ │
│ ├─ external_id ◄─────┼────────────────┼─► post_id │
│ ├─ external_url │ │ │
│ ├─ status (editorial)│ │ │
│ └─ site_status │ │ │
│ (publishing) │ │ │
└──────────────────────┘ └──────────────────────┘
```
### 2.2 Authentication Flow (Verified)
**IGNY8 → WordPress:**
```python
# publisher_service.py (line 136, 144)
destination_config = {
'api_key': site.wp_api_key, # FROM SITE MODEL
'site_url': site.domain or site.wp_url # Fallback to legacy
}
# wordpress_adapter.py
headers = {
'X-IGNY8-API-KEY': api_key, # Primary method
'Content-Type': 'application/json'
}
```
**WordPress → IGNY8:**
```php
// Plugin: check_permission() method
$stored_api_key = get_option('igny8_api_key');
$header_api_key = $request->get_header('x-igny8-api-key');
// CORRECT: hash_equals for X-IGNY8-API-KEY
if (hash_equals($stored_api_key, $header_api_key)) return true;
// VULNERABLE: strpos for Bearer token
if (strpos($auth_header, 'Bearer ' . $stored_api_key) !== false) return true;
```
### 2.3 API Key Generation (Verified)
```
POST /api/v1/integration/integrations/generate-api-key/
Body: { "site_id": 123 }
Key Format: igny8_site_{site_id}_{timestamp_ms}_{random_10_chars}
Example: igny8_site_123_1736780400000_a7b9c3d2e1
Storage: Site.wp_api_key (CharField, plain text, max 255)
Recovery: NOT POSSIBLE - shown once on generation
Revocation: Sets Site.wp_api_key = None
```
---
## PART 3: CONTENT STATUS SYSTEM (DOCUMENTED CORRECTLY)
### 3.1 Two-Status Architecture
The documentation correctly describes the dual-status system:
| Field | Purpose | Values |
|-------|---------|--------|
| `Content.status` | Editorial workflow | draft → review → approved → (published - legacy) |
| `Content.site_status` | WordPress publishing | not_published → scheduled → publishing → published / failed |
### 3.2 Publishing Flow Paths
**Path 1: Manual Publish (Working)**
```
User clicks "Publish" → ContentViewSet.publish_to_wordpress
→ Celery task: publish_content_to_wordpress
→ WordPress API call
→ Update external_id, external_url, site_status='published'
```
**Path 2: Scheduled Publish (Partially Working)**
```
schedule_approved_content (hourly)
→ Find approved content with site_status='not_published'
→ Assign scheduled_publish_at
→ Set site_status='scheduled'
process_scheduled_publications (every 5 min)
→ Find content where scheduled_publish_at <= now
→ Queue publish_content_to_wordpress task
→ WordPress API call
```
**Path 3: Automation Stage 7 (NOT TRIGGERING WP PUSH)**
```
Automation Stage 7:
→ Content.status = 'published' (legacy status change)
→ NO site_status change
→ NO WordPress API call queued
→ Content sits with site_status='not_published'
```
**GAP IDENTIFIED:** Automation Stage 7 does NOT call publishing scheduler or queue WordPress task.
---
## PART 4: SECURITY VULNERABILITIES (VERIFIED)
### 4.1 CRITICAL ISSUES
#### CRITICAL-1: API Keys Stored in Plain Text
**Location:** Backend - `Site.wp_api_key` field (auth/models.py line 491)
**Documentation Claim:** "Credentials encrypted at rest" (INTEGRATIONS.md line 345)
**Reality:** CharField stores plain text - NO encryption
**Evidence:**
```python
# auth/models.py
wp_api_key = models.CharField(max_length=255, blank=True, null=True,
help_text="API key for WordPress integration via IGNY8 WP Bridge plugin")
```
**Impact:** Database compromise exposes ALL WordPress API keys
**Risk Score:** 9/10
---
#### CRITICAL-2: Timing Attack in Bearer Token Validation
**Location:** Plugin - `class-igny8-rest-api.php:140`
**Impact:** API key can be guessed character-by-character
**Vulnerable Code:**
```php
// Uses strpos (VULNERABLE)
if (strpos($auth_header, 'Bearer ' . $stored_api_key) !== false)
// Should use hash_equals (SAFE)
if (hash_equals($stored_api_key, substr($auth_header, 7)))
```
**Risk Score:** 8/10
---
#### CRITICAL-3: Diagnostic Logging Exposes Sensitive Data
**Location:** Plugin - `class-igny8-rest-api.php:533-565`
**Impact:** Full request bodies logged including all content
**Evidence:**
```php
error_log('========== RAW REQUEST BODY ==========');
error_log($raw_body);
error_log('========== PARSED JSON DATA ==========');
error_log(print_r($content_data, true));
```
**Risk Score:** 8/10
---
### 4.2 HIGH SEVERITY ISSUES
#### HIGH-1: Public Endpoints Expose Configuration
**Verified Endpoints:**
| Endpoint | Permission | Data Exposed |
|----------|------------|--------------|
| `/wp-json/igny8/v1/status` | `__return_true` (PUBLIC) | has_api_key, connected, versions |
| `/wp-json/igny8/v1/site-metadata/` | `__return_true` (PUBLIC) | post_types, taxonomies, counts |
**Note:** Documentation (WORDPRESS-INTEGRATION-FLOW.md line 86-91) does NOT flag these as security issues.
---
#### HIGH-2: Permission Architecture Inconsistency
**Location:** `test_connection_collection` endpoint
**Permission:** `AllowAny` (then checks auth manually inside view)
**Documentation:** Does not mention this inconsistency.
---
#### HIGH-3: No Webhook Signature Verification
**Documentation Claim:** "Optional signature verification" (INTEGRATIONS.md line 349)
**Reality:** Only API key validation, NO HMAC signature verification
**Code Evidence (webhooks.py):**
```python
api_key = request.headers.get('X-IGNY8-API-KEY')
if not stored_api_key or stored_api_key != api_key:
return error_response('Invalid API key', ...)
# NO signature validation
```
---
#### HIGH-4: Webhooks Disable Rate Limiting
**Documentation Claim:** "Rate limiting on webhook endpoint" (INTEGRATIONS.md line 351)
**Reality:** Webhooks explicitly use `NoThrottle` class
---
#### HIGH-5: Encryption Falls Back Silently
**Location:** Plugin - `functions.php:31-48`
**Impact:** Admin unaware if encryption fails
---
### 4.3 MEDIUM SEVERITY ISSUES
1. **SSRF Risk in Connection Testing** - No IP validation
2. **credentials_json Exposed in Serializer** - All fields returned
3. **Flexible ID Lookup Enables Enumeration** - `/post-status/{id}` accepts both IDs
4. **Excessive API Request Logging** - Keys in logs
5. **WordPress URL Not Validated** - No HTTPS enforcement
6. **API Key Partially Visible** - `/verify-key` shows 15-char prefix
### 4.4 LOW SEVERITY ISSUES
1. Version disclosure in multiple endpoints
2. No CORS headers defined on plugin endpoints
3. Webhook logs stored long-term (500 entries)
---
## PART 5: REST API ENDPOINTS INVENTORY (VERIFIED)
### 5.1 Backend Endpoints (Verified Against urls.py)
| Endpoint | Method | Permission | Documentation |
|----------|--------|------------|---------------|
| `/api/v1/integration/integrations/` | CRUD | IsAuthenticatedAndActive + IsEditorOrAbove | Correct |
| `/api/v1/integration/integrations/test-connection/` (collection) | POST | **AllowAny** | NOT documented as AllowAny |
| `/api/v1/integration/integrations/{id}/test-connection/` | POST | Authenticated | Correct |
| `/api/v1/integration/integrations/generate-api-key/` | POST | Authenticated | Correct |
| `/api/v1/integration/integrations/revoke-api-key/` | POST | Authenticated | Correct |
| `/api/v1/integration/webhooks/wordpress/status/` | POST | AllowAny (header auth) | Correct |
| `/api/v1/integration/webhooks/wordpress/metadata/` | POST | AllowAny (header auth) | Correct |
| `/api/v1/publisher/publish/` | POST | Authenticated | Correct |
| `/api/v1/writer/content/{id}/publish_to_wordpress/` | POST | Authenticated | Correct |
| `/api/v1/writer/content/{id}/schedule/` | POST | Authenticated | Correct (v1.3.2) |
| `/api/v1/writer/content/{id}/unschedule/` | POST | Authenticated | Correct (v1.3.2) |
### 5.2 Plugin Endpoints (Verified Against class-igny8-rest-api.php)
| Endpoint | Method | Permission | Documentation Match |
|----------|--------|------------|---------------------|
| `/wp-json/igny8/v1/status` | GET | **PUBLIC** | Not flagged as security issue |
| `/wp-json/igny8/v1/site-metadata/` | GET | **PUBLIC** (internal check) | Not flagged |
| `/wp-json/igny8/v1/verify-key` | GET | check_permission | Correct |
| `/wp-json/igny8/v1/publish` | POST | check_permission | Correct |
| `/wp-json/igny8/v1/post-by-content-id/{id}` | GET | check_permission | Correct |
| `/wp-json/igny8/v1/post-by-task-id/{id}` | GET | check_permission | Correct |
| `/wp-json/igny8/v1/post-status/{id}` | GET | check_permission | Correct |
| `/wp-json/igny8/v1/event` | POST | verify_webhook_secret | Correct |
---
## PART 6: DATA STORAGE ANALYSIS (VERIFIED)
### 6.1 Site Model Fields (auth/models.py)
| Field | Documentation Status | Actual Usage | Action |
|-------|---------------------|--------------|--------|
| `wp_api_key` | Documented as primary | ACTIVE - single source of truth | KEEP + ENCRYPT |
| `domain` | Documented | ACTIVE - WordPress URL | KEEP |
| `wp_url` | Documented as "deprecated" | LEGACY - fallback in publisher_service | EVALUATE for removal |
| `wp_username` | Documented as "deprecated" | **ZERO USAGE** in codebase | **REMOVE** |
| `wp_app_password` | Documented as "deprecated" | **ZERO USAGE** in codebase | **REMOVE** |
### 6.2 SiteIntegration Model Fields
| Field | Documentation Status | Actual Usage | Action |
|-------|---------------------|--------------|--------|
| `site` | Documented | ACTIVE | KEEP |
| `platform` | Documented | ACTIVE ('wordpress') | KEEP |
| `config_json` | Documented as storing URL | ACTIVE (site_url only) | KEEP |
| `credentials_json` | Documented as storing creds | **EMPTY** for WordPress | Documentation outdated |
| `sync_status` | Documented | ACTIVE | KEEP |
**Documentation Gap:** INTEGRATIONS.md line 97-98 shows `api_key` and `username` as SiteIntegration fields, but WordPress actually uses Site.wp_api_key.
### 6.3 Content Model Fields
| Field | Documentation Status | Actual Usage | Action |
|-------|---------------------|--------------|--------|
| `status` | Correctly documented as editorial | ACTIVE | KEEP |
| `site_status` | Correctly documented as publishing | ACTIVE | KEEP |
| `external_id` | Documented | ACTIVE - WordPress post ID | KEEP |
| `external_url` | Documented | ACTIVE - WordPress URL | KEEP |
| `external_type` | Not documented | **NEVER USED** | **REMOVE** |
| `external_metadata` | Not documented | Only set to {} | **REMOVE** |
| `sync_status` (on Content) | Not documented | **NEVER USED** | **REMOVE** |
### 6.4 ContentTaxonomy Model Fields
| Field | Documentation Status | Actual Usage | Action |
|-------|---------------------|--------------|--------|
| `external_id` | Documented | ACTIVE | KEEP |
| `external_taxonomy` | Documented | ACTIVE | KEEP |
| `sync_status` | Not documented | **NEVER USED** | **REMOVE** |
---
## PART 7: CELERY TASKS STATUS (VERIFIED)
### 7.1 Scheduled Tasks (celery.py Beat Schedule)
| Task | Schedule | Documentation | Status |
|------|----------|---------------|--------|
| `schedule_approved_content` | Every hour | Documented (SCHEDULED-CONTENT-PUBLISHING.md) | **ACTIVE** |
| `process_scheduled_publications` | Every 5 min | Documented | **ACTIVE** |
| `publish_content_to_wordpress` | On-demand | Documented | **ACTIVE** |
### 7.2 Missing Tasks (Documented but NOT Scheduled)
**`process_pending_wordpress_publications`** - WORDPRESS-INTEGRATION-FLOW.md mentions this task exists but notes:
> "CURRENT STATUS: This task is NOT in Celery Beat schedule!"
This matches the documentation correctly.
---
## PART 8: PLUGIN DISTRIBUTION SYSTEM (VERIFIED)
### 8.1 Plugin Models (Correctly Documented)
| Model | Documentation | Implementation | Match |
|-------|---------------|----------------|-------|
| `Plugin` | WORDPRESS-INTEGRATION.md | plugins/models.py | ✅ |
| `PluginVersion` | Documented with all fields | Matches implementation | ✅ |
| `PluginInstallation` | Documented | Matches implementation | ✅ |
| `PluginDownload` | Documented | Matches implementation | ✅ |
### 8.2 Plugin API Endpoints (Correctly Documented)
| Endpoint | Documentation | Implementation |
|----------|---------------|----------------|
| `/api/plugins/{slug}/download/` | INDEX.md line 24 | ✅ Working |
| `/api/plugins/{slug}/check-update/` | INDEX.md line 25 | ✅ Working |
| `/api/plugins/{slug}/info/` | INDEX.md line 26 | ✅ Working |
| `/api/plugins/{slug}/register/` | INDEX.md line 27 | ✅ Working |
| `/api/plugins/{slug}/health-check/` | INDEX.md line 28 | ✅ Working |
---
## PART 9: UNUSED/DEAD CODE INVENTORY
### 9.1 Fields to Remove (Verified Zero Usage)
| Model | Field | Evidence |
|-------|-------|----------|
| Site | `wp_username` | `grep -r "wp_username" --include="*.py"` = 0 results |
| Site | `wp_app_password` | `grep -r "wp_app_password" --include="*.py"` = 0 results |
| Content | `external_type` | Never read in codebase |
| Content | `external_metadata` | Only set to {} in publisher_service.py |
| Content | `sync_status` | SiteIntegration.sync_status used instead |
| ContentTaxonomy | `sync_status` | Never read or written |
### 9.2 Redundant Code Paths
1. **Duplicate Connection Testing** - Two endpoints with same logic
2. **Duplicate API Key Validation** - Same validation in 4+ files
3. **Dead Admin Bulk Actions** - `bulk_trigger_sync()`, `bulk_test_connection()` have TODO comments
### 9.3 Plugin Unused Options
| Option | Status |
|--------|--------|
| `igny8_access_token` | Redundant with igny8_api_key |
| `igny8_access_token_issued` | Referenced but not used in auth |
---
## PART 10: DATA DUPLICATION ISSUES
### 10.1 API Key Duplication
| Location | Field | Role |
|----------|-------|------|
| Django Site model | `wp_api_key` | **PRIMARY** (single source of truth) |
| Django SiteIntegration | `credentials_json` | EMPTY for WordPress |
| WordPress | `igny8_api_key` | COPY (must match primary) |
| WordPress | `igny8_access_token` | REDUNDANT (should remove) |
**Risk:** Documentation mentions credentials_json but WordPress doesn't use it.
### 10.2 URL Duplication
| Location | Field | Role |
|----------|-------|------|
| Site.domain | Primary | ACTIVE |
| Site.wp_url | Legacy | Fallback only |
| SiteIntegration.config_json['site_url'] | Configuration | ACTIVE |
---
## PART 11: RECOMMENDATIONS
### Immediate Actions (Critical)
| Priority | Issue | Action | Documentation Update |
|----------|-------|--------|---------------------|
| 1 | Plain text API keys | Implement field encryption | Update INTEGRATIONS.md |
| 2 | Timing attack (strpos) | Use hash_equals everywhere | Update plugin docs |
| 3 | Diagnostic logging | Remove or conditional | Update plugin docs |
| 4 | Public endpoints | Secure /status and /site-metadata | Update WORDPRESS-INTEGRATION-FLOW.md |
### Short-term Actions (High)
| Priority | Issue | Action | Documentation Update |
|----------|-------|--------|---------------------|
| 5 | Permission inconsistency | Fix test_connection_collection | Update ENDPOINTS.md |
| 6 | No webhook signatures | Implement HMAC verification | Update INTEGRATIONS.md |
| 7 | No rate limiting | Enable throttling on webhooks | Update INTEGRATIONS.md |
| 8 | API key in response | Remove from verify-key | N/A |
### Documentation Updates Required
| Document | Updates Needed |
|----------|----------------|
| INTEGRATIONS.md | Remove "encrypted at rest" claim, fix field references |
| WORDPRESS-INTEGRATION-FLOW.md | Flag public endpoints as security concern |
| ENDPOINTS.md | Note AllowAny on test-connection collection |
### Cleanup Actions
| Action | Fields to Remove | Migration Required |
|--------|------------------|-------------------|
| Remove from Site | wp_username, wp_app_password | Yes |
| Remove from Content | external_type, external_metadata, sync_status | Yes |
| Remove from ContentTaxonomy | sync_status | Yes |
| Remove from Plugin | igny8_access_token option | Plugin update |
---
## PART 12: PUBLISHING WORKFLOW SUMMARY
### What's Working
| Flow | Status | Notes |
|------|--------|-------|
| Manual publish button | ✅ Working | ContentViewSet.publish_to_wordpress |
| Scheduled publishing | ✅ Working | Celery Beat tasks active |
| Plugin distribution | ✅ Working | Auto-update mechanism functional |
| Webhook status sync | ✅ Working | WordPress → IGNY8 updates |
### What's NOT Working
| Flow | Status | Issue |
|------|--------|-------|
| Automation Stage 7 → WP | ❌ Broken | Sets status but no WP push |
| Content update sync | ❌ Missing | No republish capability |
| WordPress → IGNY8 import | ❌ Missing | No pull sync feature |
### Documented But Not Implemented
| Feature | Documentation Reference | Status |
|---------|------------------------|--------|
| Webhook signature verification | INTEGRATIONS.md line 349 | NOT implemented |
| Webhook rate limiting | INTEGRATIONS.md line 351 | NOT implemented |
| Credential encryption | INTEGRATIONS.md line 345 | NOT implemented |
---
## APPENDIX A: FILES AUDITED
### Backend Files
- `backend/igny8_core/auth/models.py` - Site model, wp_api_key
- `backend/igny8_core/business/integration/models.py` - SiteIntegration, SyncEvent
- `backend/igny8_core/business/publishing/models.py` - PublishingRecord
- `backend/igny8_core/business/content/models.py` - Content, external fields
- `backend/igny8_core/modules/integration/views.py` - API endpoints
- `backend/igny8_core/modules/integration/webhooks.py` - Webhook handlers
- `backend/igny8_core/business/publishing/services/publisher_service.py`
- `backend/igny8_core/business/publishing/services/adapters/wordpress_adapter.py`
- `backend/igny8_core/tasks/publishing_scheduler.py` - Celery tasks
- `backend/igny8_core/tasks/wordpress_publishing.py` - Publishing task
- `backend/igny8_core/celery.py` - Beat schedule
### Plugin Files
- `plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-rest-api.php`
- `plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-api.php`
- `plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-webhooks.php`
- `plugins/wordpress/source/igny8-wp-bridge/includes/functions.php`
### Documentation Files Reviewed
- `docs/60-PLUGINS/WORDPRESS-INTEGRATION.md`
- `docs/60-PLUGINS/INDEX.md`
- `docs/60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md`
- `docs/50-DEPLOYMENT/WORDPRESS-INTEGRATION-FLOW.md`
- `docs/10-MODULES/PUBLISHER.md`
- `docs/10-MODULES/INTEGRATIONS.md`
- `docs/40-WORKFLOWS/CONTENT-PIPELINE.md`
- `docs/40-WORKFLOWS/SCHEDULED-CONTENT-PUBLISHING.md`
- `docs/00-SYSTEM/ARCHITECTURE.md`
- `docs/20-API/ENDPOINTS.md`
---
## APPENDIX B: VULNERABILITY SEVERITY MATRIX
| ID | Title | CVSS | Documentation Claim | Reality |
|----|-------|------|---------------------|---------|
| CRITICAL-1 | Plain text API keys | 9.0 | "Encrypted at rest" | Plain text CharField |
| CRITICAL-2 | Timing attack | 8.0 | Not mentioned | strpos vulnerability |
| CRITICAL-3 | Diagnostic logging | 8.0 | Not mentioned | Full request bodies logged |
| HIGH-1 | Public endpoints | 7.0 | Not flagged | Information disclosure |
| HIGH-2 | Permission inconsistency | 6.5 | Not documented | AllowAny misuse |
| HIGH-3 | No webhook signatures | 6.0 | "Optional" | Not implemented at all |
| HIGH-4 | No rate limiting | 5.5 | "Rate limiting enabled" | NoThrottle class used |
| HIGH-5 | Silent encryption fail | 6.0 | Not mentioned | Falls back to plain text |
---
**End of Calibrated Audit Report**
**Next Steps:**
1. Review findings with development team
2. Prioritize critical security fixes
3. Update documentation to match reality
4. Create migration plan for field removal
5. Implement encryption before production

File diff suppressed because it is too large Load Diff