docs & ux improvmeents

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-25 20:31:58 +00:00
parent 90e6e96b2b
commit 4bffede052
247 changed files with 6869 additions and 53517 deletions

View File

@@ -1,79 +0,0 @@
# System Architecture Overview
## Purpose
Describe how IGNY8 is structured across backend, frontend, and integrations, grounded in the current codebase. Covers core services, middleware, and platform composition.
## Code Locations (exact paths)
- Backend project root: `backend/igny8_core/`
- Settings and service wiring: `backend/igny8_core/settings.py`
- URL routing: `backend/igny8_core/urls.py`
- Middleware: `backend/igny8_core/middleware/request_id.py`, `backend/igny8_core/middleware/resource_tracker.py`, `backend/igny8_core/auth/middleware.py`
- Auth models and tenancy bases: `backend/igny8_core/auth/models.py`
- DRF base behaviors: `backend/igny8_core/api/base.py`
- Custom auth classes: `backend/igny8_core/api/authentication.py`
- Frontend SPA: `frontend/` (Vite + React; dependencies in `frontend/package.json`)
## High-Level Responsibilities
- Django/DRF backend providing multi-tenant APIs for planner, writer, system, billing, automation, linker, optimizer, publisher, and integration modules.
- Middleware adds per-request IDs, tenant context, and optional resource tracking for admin diagnostics.
- REST API routing under `/api/v1/*` with unified response/error handling and scoped throttling.
- Celery-backed async work (configured in settings) for AI, automation, and publishing.
- React/Vite frontend consuming the API; authentication via JWT or session; API key support for WordPress bridge.
## Detailed Behavior
- Backend apps registered in `INSTALLED_APPS` include auth, AI framework, planner, writer, system, billing, automation, optimization, publishing, integration, linker, optimizer, and publisher modules; these are loaded via Django app configs in `settings.py`.
- Middleware order enforces security, CORS, session, CSRF, Django auth, then custom layers: request ID, account context, resource tracking, messages, and clickjacking protection.
- URL map (`urls.py`) exposes admin, CSV admin utilities for industries/seed keywords, and module routers for auth, account, planner, writer, system, billing (user + admin), automation, linker, optimizer, publisher, and integration. OpenAPI docs available at `/api/docs` and `/api/redoc`.
- REST framework defaults use custom pagination, filtering, search, ordering, and a custom exception handler (enabled unless `IGNY8_USE_UNIFIED_EXCEPTION_HANDLER` is false). Authentication stack orders API key, JWT, CSRF-exempt session, then basic auth. Throttle scopes are predefined per domain (AI, content, auth, planner, writer, system, billing, linker, optimizer, integration).
- CORS allows the IGNY8 domains plus local development hosts; credentials and specific debug headers are permitted, and request/resource tracking IDs are exposed in response headers.
- Celery is configured to use Redis by default for broker and result backend, with JSON serialization, task time limits, and single-prefetch workers.
- Logging writes to console and rotating files for publish/sync, WordPress API calls, and webhooks, with request IDs available via middleware.
## Data Structures / Models Involved (no code, just explanation)
- Multi-tenancy base classes (`AccountBaseModel`, `SiteSectorBaseModel`) add `account`, `site`, and `sector` scoping plus validation; defined in `auth/models.py`.
- Core tenancy entities: `Account`, `Plan`, `Subscription`, `Site`, `Sector`, `Industry`, `IndustrySector`, `SeedKeyword`, `SiteUserAccess`, `User`, and `PasswordResetToken` in `auth/models.py`.
- These bases are consumed by downstream modules (planner, writer, billing, automation, etc.) to enforce tenant, site, and sector ownership.
## Execution Flow
- Incoming HTTP requests enter Django middleware: security → WhiteNoise → CORS → session → common/CSRF → Django auth → `RequestIDMiddleware` (assigns `X-Request-ID`) → `AccountContextMiddleware` (sets `request.account` via session or JWT) → `ResourceTrackingMiddleware` (when enabled for admins) → messages → clickjacking.
- DRF viewsets inherit from base classes in `api/base.py` to auto-filter querysets by account (and site/sector where applicable) and emit unified responses.
- URL dispatch routes to module-specific routers under `/api/v1/*`. CSV admin helpers are mounted before admin to avoid routing conflicts.
- Background tasks are dispatched via Celery using Redis endpoints from `settings.py`.
## Cross-Module Interactions
- Auth middleware sets `request.account` consumed by `AccountModelViewSet` and `SiteSectorModelViewSet` to filter data.
- API key auth (WordPress bridge) sets both `request.account` and `request.site` for integration endpoints.
- Resource tracking middleware exposes metrics via cache keyed by the request-specific tracking ID added to responses.
- OpenAPI generation (drf-spectacular) documents all modules and respects unified response schemas.
## State Transitions (if applicable)
- Request lifecycle: ID assignment → tenant resolution → optional resource profiling → viewset execution → unified response with optional pagination and request/resource IDs in headers.
- Tenancy lifecycle: Account/Plan/Subscription fields in models track status and retention; soft delete support on models that implement it.
## Error Handling
- Global DRF exception handler (custom when enabled) wraps errors into unified JSON with `success=false`.
- Account middleware denies access when account or plan is missing/inactive, returning structured JSON and logging out session users.
- Viewset overrides in `api/base.py` return unified error payloads for validation and 404/500 cases.
## Tenancy Rules
- Account is injected via middleware (session or JWT). Base viewsets filter querysets by `account`, unless the user is admin/developer/system account (override path).
- `SiteSectorModelViewSet` adds site/sector filtering when models carry those fields; validation ensures site/sector belong to the same account.
- Role helpers (`User.is_admin_or_developer`, `User.is_system_account_user`) allow bypass for privileged users; otherwise, data is restricted to the resolved account (and site/sector for derived models).
## Billing Rules (if applicable)
- Plan and account billing fields live in `auth/models.py` (`Plan.included_credits`, `Account.credits`, Stripe IDs). Status enforcement occurs in `AccountContextMiddleware` by requiring an active plan; billing modules implement credit logic (covered in backend/billing docs).
## Background Tasks / Schedulers (if applicable)
- Celery configuration in `settings.py` sets Redis broker/backend, JSON serialization, time limits, and worker tuning. Task modules (AI, automation, publishing) use this setup; scheduling uses Celery Beat state.
## Key Design Considerations
- Middleware-first tenant resolution ensures consistent scoping before view logic.
- Admin/developer/system-account overrides allow cross-tenant operations for ops while protecting system accounts from deletion.
- Unified API responses and throttling scopes enforce consistent client behavior and rate safety.
- Redis-backed Celery keeps async workloads out of request path with strict time limits.
## How Developers Should Work With This Module
- Add new apps to `INSTALLED_APPS` and mount URLs under `/api/v1/*` in `urls.py`.
- Inherit from `AccountModelViewSet` or `SiteSectorModelViewSet` to automatically enforce tenant/site/sector scoping and unified responses.
- Use existing middleware; do not reorder request ID or account context layers, as downstream views rely on them.
- Configure environment via `settings.py` variables (DB, JWT, Celery, CORS, Stripe/PayPal) rather than hardcoding values.

View File

@@ -0,0 +1,289 @@
# System Architecture
**Last Verified:** December 25, 2025
**Backend Path:** `backend/igny8_core/`
**Frontend Path:** `frontend/src/`
---
## Tech Stack
| Layer | Technology | Version | Purpose |
|-------|------------|---------|---------|
| **Backend Framework** | Django | 5.1 | Web framework |
| **API Layer** | Django REST Framework | 3.15 | REST API |
| **Database** | PostgreSQL | 16 | Primary data store |
| **Cache/Queue** | Redis | 7 | Caching, Celery broker |
| **Task Queue** | Celery | 5.4 | Async task processing |
| **Frontend Framework** | React | 18 | UI framework |
| **Build Tool** | Vite | 5 | Frontend bundler |
| **Styling** | Tailwind CSS | 3 | Utility CSS |
| **State Management** | Zustand | 4 | React state |
| **Language** | TypeScript | 5 | Frontend typing |
| **Web Server** | Caddy | 2 | Reverse proxy, SSL |
| **Containerization** | Docker | - | Deployment |
---
## High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ React SPA (app.igny8.com) │ WordPress Plugin │ Admin │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ CADDY (Reverse Proxy) │
│ SSL termination, routing, static files │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ DJANGO REST FRAMEWORK │
│ │
│ Middleware Stack: │
│ SecurityMiddleware → WhiteNoise → CORS → Session → CSRF → │
│ DjangoAuth → RequestIDMiddleware → AccountContextMiddleware → │
│ ResourceTrackingMiddleware │
│ │
│ API: /api/v1/* → ViewSets → Services → Models │
└─────────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────────────────┐
│ PostgreSQL │ │ Redis │
│ Primary Database │ │ Sessions, Cache, Celery Broker │
└─────────────────────┘ └─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ CELERY WORKERS │
│ AI Tasks, Automation, Publishing, Background Jobs │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ OpenAI API (GPT-4, DALL-E) │ Runware │ WordPress Sites │
└─────────────────────────────────────────────────────────────────┘
```
---
## Backend Structure
```
backend/igny8_core/
├── settings.py # Django settings, all config
├── urls.py # Root URL routing
├── celery.py # Celery configuration
├── wsgi.py / asgi.py # WSGI/ASGI entry points
├── auth/ # Authentication & tenancy
│ ├── models.py # User, Account, Site, Sector, Plan
│ ├── views.py # Login, register, password reset
│ ├── middleware.py # AccountContextMiddleware
│ └── urls.py # /api/v1/auth/*
├── api/ # API infrastructure
│ ├── base.py # AccountModelViewSet, SiteSectorModelViewSet
│ ├── authentication.py # JWT, API key auth classes
│ └── pagination.py # Unified pagination
├── ai/ # AI engine
│ ├── engine.py # AIEngine orchestrator
│ ├── functions/ # AutoCluster, GenerateIdeas, GenerateContent, etc.
│ ├── registry.py # Function registry
│ └── progress.py # Progress tracking
├── modules/ # Feature modules (API layer)
│ ├── planner/ # Keywords, Clusters, Ideas
│ ├── writer/ # Tasks, Content, Images
│ ├── billing/ # Credits, usage, transactions
│ ├── integration/ # WordPress integration
│ ├── system/ # Settings, prompts, AI config
│ ├── linker/ # Internal linking (inactive)
│ ├── optimizer/ # Content optimization (inactive)
│ └── publisher/ # Publishing pipeline
├── business/ # Business logic (services)
│ ├── automation/ # 7-stage automation pipeline
│ ├── billing/ # Credit service, payment processing
│ ├── content/ # Content generation orchestration
│ ├── integration/ # Sync services
│ ├── linking/ # Link processing
│ ├── optimization/ # Content optimization
│ ├── planning/ # Clustering, idea generation
│ └── publishing/ # Publishing orchestration
├── middleware/ # Custom middleware
│ ├── request_id.py # X-Request-ID header
│ └── resource_tracker.py # Resource tracking for admins
└── tasks/ # Celery tasks
└── *.py # Background job definitions
```
---
## Frontend Structure
```
frontend/src/
├── main.tsx # Entry point
├── App.tsx # Root component, routing
├── index.css # Global styles, Tailwind
├── api/ # API clients
│ ├── linker.api.ts
│ ├── optimizer.api.ts
│ └── ...
├── services/
│ └── api.ts # Main API service (2500+ lines)
├── store/ # Zustand stores
│ ├── authStore.ts # Authentication state
│ ├── siteStore.ts # Active site
│ ├── sectorStore.ts # Active sector
│ ├── billingStore.ts # Billing state
│ ├── moduleStore.ts # Module enable/disable
│ └── ...
├── pages/ # Route pages
│ ├── Dashboard/
│ ├── Planner/
│ ├── Writer/
│ ├── Automation/
│ ├── Linker/
│ ├── Optimizer/
│ ├── Settings/
│ ├── Billing/
│ └── Auth/
├── components/ # Reusable components
│ ├── ProgressModal.tsx # AI progress display
│ ├── ConfirmDialog.tsx
│ └── ...
├── layout/ # Layout components
│ ├── AppLayout.tsx
│ └── AppSidebar.tsx
├── hooks/ # Custom hooks
├── context/ # React contexts
└── utils/ # Utility functions
```
---
## Key Design Patterns
### 1. Multi-Tenant Data Isolation
All data is scoped to Account, with optional Site and Sector filtering:
```
Account (Tenant)
└── Site (e.g., myblog.com)
└── Sector (e.g., Technology, Health)
└── Keywords/Clusters/Ideas/Content
```
**Implementation:**
- `AccountBaseModel` - Base class for account-scoped models
- `SiteSectorBaseModel` - Base class for site/sector-scoped models
- `AccountModelViewSet` - Auto-filters queryset by request.account
- `SiteSectorModelViewSet` - Auto-filters by account + site + sector
### 2. Middleware-First Tenant Resolution
```python
# Order in settings.py MIDDLEWARE
1. SecurityMiddleware
2. WhiteNoiseMiddleware
3. CorsMiddleware
4. SessionMiddleware
5. DjangoAuthenticationMiddleware
6. RequestIDMiddleware # Assigns X-Request-ID
7. AccountContextMiddleware # Sets request.account from session/JWT
8. ResourceTrackingMiddleware # Optional admin profiling
```
### 3. Unified API Responses
All API responses follow this structure:
```json
{
"success": true,
"data": { ... },
"message": "Optional message"
}
```
Error responses:
```json
{
"success": false,
"error": "Error message",
"code": "ERROR_CODE"
}
```
### 4. Credit-Based Operations
All AI operations check and deduct credits:
1. Pre-check: `CreditService.check_credits()`
2. Execute: AI function runs
3. Post-deduct: `CreditService.deduct_credits_for_operation()`
---
## Environment Configuration
Key environment variables (from `settings.py`):
| Variable | Purpose |
|----------|---------|
| `DATABASE_URL` | PostgreSQL connection |
| `REDIS_URL` | Redis connection |
| `SECRET_KEY` | Django secret |
| `JWT_SECRET_KEY` | JWT signing key |
| `CORS_ALLOWED_ORIGINS` | Allowed frontend origins |
| `CELERY_BROKER_URL` | Celery broker (Redis) |
AI keys are stored in `GlobalIntegrationSettings` (database), not env vars.
---
## Deployment Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Docker Compose │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Frontend │ │ Backend │ │ Worker │ │
│ │ (Caddy) │ │ (Django) │ │ (Celery) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ PostgreSQL │ │ Redis │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
---
## Planned Changes
| Feature | Status | Description |
|---------|--------|-------------|
| AIModelConfig Database | 🔜 Planned | Move AI model pricing from constants to database |
| Module Guard Extension | 🔜 Planned | Extend linker/optimizer disable to all pages (currently sidebar only) |
| Multi-provider AI | 🔜 Planned | Support for Anthropic, Google AI |

View File

@@ -0,0 +1,254 @@
# Authentication & Authorization
**Last Verified:** December 25, 2025
**Backend Path:** `backend/igny8_core/auth/`
**Frontend Path:** `frontend/src/store/authStore.ts`
---
## Quick Reference
| What | File | Key Functions |
|------|------|---------------|
| User Model | `auth/models.py` | `User`, `Account`, `Plan` |
| Auth Views | `auth/views.py` | `LoginView`, `RegisterView`, `RefreshTokenView` |
| Middleware | `auth/middleware.py` | `AccountContextMiddleware` |
| JWT Auth | `api/authentication.py` | `JWTAuthentication`, `CookieJWTAuthentication` |
| API Key Auth | `api/authentication.py` | `APIKeyAuthentication` |
| Frontend Store | `store/authStore.ts` | `useAuthStore` |
---
## Authentication Methods
### 1. JWT Token Authentication (Primary)
**Flow:**
1. User logs in via `/api/v1/auth/login/`
2. Backend returns `access_token` (15 min) + `refresh_token` (7 days)
3. Frontend stores tokens in localStorage and Zustand store
4. All API requests include `Authorization: Bearer <access_token>`
5. Token refresh via `/api/v1/auth/token/refresh/`
**Token Payload:**
```json
{
"user_id": 123,
"account_id": 456,
"email": "user@example.com",
"exp": 1735123456,
"iat": 1735122456
}
```
### 2. Session Authentication (Admin/Fallback)
- Used by Django Admin interface
- Cookie-based session with CSRF protection
- Redis-backed sessions (prevents user swapping bug)
### 3. API Key Authentication (WordPress Bridge)
**Flow:**
1. Account generates API key in settings
2. WordPress plugin uses `Authorization: ApiKey <key>`
3. Backend validates key, sets `request.account` and `request.site`
**Use Cases:**
- WordPress content sync
- External integrations
- Headless CMS connections
---
## API Endpoints
| Method | Path | Handler | Purpose |
|--------|------|---------|---------|
| POST | `/api/v1/auth/register/` | `RegisterView` | Create new user + account |
| POST | `/api/v1/auth/login/` | `LoginView` | Authenticate, return tokens |
| POST | `/api/v1/auth/logout/` | `LogoutView` | Invalidate tokens |
| POST | `/api/v1/auth/token/refresh/` | `RefreshTokenView` | Refresh access token |
| POST | `/api/v1/auth/password/change/` | `ChangePasswordView` | Change password |
| POST | `/api/v1/auth/password/reset/` | `RequestPasswordResetView` | Request reset email |
| POST | `/api/v1/auth/password/reset/confirm/` | `ResetPasswordView` | Confirm reset with token |
---
## User Roles
| Role | Code | Permissions |
|------|------|-------------|
| **Developer** | `developer` | Full access across ALL accounts (superuser) |
| **Admin** | `admin` | Full access to own account |
| **Manager** | `manager` | Manage content, view billing |
| **Editor** | `editor` | AI content, manage clusters/tasks |
| **Viewer** | `viewer` | Read-only dashboards |
| **System Bot** | `system_bot` | System automation (internal) |
**Role Hierarchy:**
```
developer > admin > manager > editor > viewer
```
---
## Middleware: AccountContextMiddleware
**File:** `auth/middleware.py`
**Purpose:** Injects `request.account` on every request
**Flow:**
1. Check for JWT token → extract account_id
2. Check for session → get account from session
3. Check for API key → get account from key
4. Validate account exists and is active
5. Validate plan exists and is active
6. Set `request.account`, `request.user`
**Error Responses:**
- No account: 403 with JSON error
- Inactive plan: 402 with JSON error
---
## Frontend Auth Store
**File:** `store/authStore.ts`
**State:**
```typescript
{
user: User | null;
token: string | null;
refreshToken: string | null;
isAuthenticated: boolean;
}
```
**Actions:**
- `login(email, password)` - Authenticate and store tokens
- `register(data)` - Create account and store tokens
- `logout()` - Clear tokens and reset stores
- `refreshToken()` - Refresh access token
- `checkAuth()` - Verify current auth state
**Critical Implementation:**
```typescript
// Tokens are written synchronously to localStorage
// This prevents race conditions where API calls happen before persist
localStorage.setItem('auth-storage', JSON.stringify(authState));
```
---
## Session Security (Redis-Backed)
**Problem Solved:** User swapping / random logout issues
**Implementation:**
```python
# settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default' # Redis
# auth/backends.py
class NoCacheModelBackend(ModelBackend):
"""Authentication backend without user caching"""
pass
```
**Session Integrity:**
- Stores `account_id` and `user_id` in session
- Validates on every request
- Prevents cross-request contamination
---
## API Key Management
**Model:** `APIKey` in `auth/models.py`
| Field | Type | Purpose |
|-------|------|---------|
| key | CharField | Hashed API key |
| account | ForeignKey | Owner account |
| site | ForeignKey | Optional: specific site |
| name | CharField | Key name/description |
| is_active | Boolean | Enable/disable |
| created_at | DateTime | Creation time |
| last_used_at | DateTime | Last usage time |
**Generation:**
- 32-character random key
- Stored hashed (SHA-256)
- Shown once on creation
---
## Permission Checking
**In ViewSets:**
```python
class MyViewSet(AccountModelViewSet):
permission_classes = [IsAuthenticated]
def get_queryset(self):
# Automatically filtered by request.account
return super().get_queryset()
```
**Role Checks:**
```python
if request.user.is_admin_or_developer:
# Admin/developer access
pass
elif request.user.role == 'editor':
# Editor access
pass
```
---
## Logout Flow
**Backend:**
1. Blacklist refresh token (if using token blacklist)
2. Clear session
**Frontend (Critical):**
```typescript
logout: () => {
// NEVER use localStorage.clear() - breaks Zustand persist
const authKeys = ['auth-storage', 'site-storage', 'sector-storage', 'billing-storage'];
authKeys.forEach(key => localStorage.removeItem(key));
// Reset dependent stores
useSiteStore.setState({ activeSite: null });
useSectorStore.setState({ activeSector: null, sectors: [] });
set({ user: null, token: null, isAuthenticated: false });
}
```
---
## Common Issues
| Issue | Cause | Fix |
|-------|-------|-----|
| 403 after login | Tokens not persisted before API call | Write to localStorage synchronously |
| User swapping | DB-backed sessions with user caching | Redis sessions + NoCacheModelBackend |
| Token refresh loop | Refresh token expired | Redirect to login |
| API key not working | Missing site scope | Check API key has correct site assigned |
---
## Planned Changes
| Feature | Status | Description |
|---------|--------|-------------|
| Token blacklist | 🔜 Planned | Proper refresh token invalidation |
| 2FA | 🔜 Planned | Two-factor authentication |
| SSO | 🔜 Planned | Google/GitHub OAuth |

View File

@@ -1,73 +0,0 @@
# Identity and Authentication
## Purpose
Document how user identity, JWT handling, API keys, and session flows work, including middleware and validation rules.
## Code Locations (exact paths)
- JWT utilities: `backend/igny8_core/auth/utils.py`
- Account context middleware: `backend/igny8_core/auth/middleware.py`
- DRF authentication classes: `backend/igny8_core/api/authentication.py`
- DRF settings for auth/throttle: `backend/igny8_core/settings.py`
- User model and roles: `backend/igny8_core/auth/models.py`
- Auth URLs and views: `backend/igny8_core/auth/urls.py`, `backend/igny8_core/auth/views.py`
## High-Level Responsibilities
- Support multiple auth mechanisms: API key (WordPress bridge), JWT bearer tokens, session auth without CSRF for APIs, and basic auth fallback.
- Populate tenant/site context alongside user identity so downstream viewsets enforce isolation.
- Enforce active account/plan presence before serving protected endpoints (except admin/auth routes).
## Detailed Behavior
- Authentication order (DRF `DEFAULT_AUTHENTICATION_CLASSES` in `settings.py`): API key → JWT → CSRF-exempt session → basic auth. The first class that authenticates sets `request.user`; `request.account` may also be set by API key or JWT.
- API Key flow (`APIKeyAuthentication`): expects `Authorization: Bearer <api_key>` that is not JWT-like; finds an active `Site` with `wp_api_key`, loads its `account`, and selects an active user (owner preferred, else any active developer/owner/admin). Sets `request.account` and `request.site`. Rejects short/invalid keys; returns an auth failure if no active user exists.
- JWT flow (`JWTAuthentication`): expects `Authorization: Bearer <jwt>`; decodes via `auth.utils.decode_token`; only accepts tokens with `type == access`. Retrieves `User` by `user_id`; optional `account_id` is resolved to `Account` and set on `request.account`. Invalid/expired tokens fall through to other auth classes.
- Session flow (`CSRFExemptSessionAuthentication`): uses Django session cookies without CSRF enforcement for API calls.
- Basic auth: last resort; does not set tenant context.
- Token utilities (`auth/utils.py`) generate and decode access/refresh tokens using expiries from settings (`JWT_ACCESS_TOKEN_EXPIRY`, `JWT_REFRESH_TOKEN_EXPIRY`), embedding `user_id`, `account_id`, `email`, issued/expiry timestamps, and token `type`.
- Middleware (`AccountContextMiddleware`) runs on every request except admin/auth paths: refreshes session users from DB to pick up current account/plan, validates presence of an active plan, sets `request.account`, and logs out session users when invalid. For JWT-bearing requests it decodes the token directly and sets `request.account`. If account/plan is missing or inactive, it returns JSON with `success=false` and appropriate HTTP status.
## Data Structures / Models Involved (no code)
- `User` with `role` and `account` FKs in `auth/models.py`.
- `Account` with plan and billing fields; plan status is used for access gating.
- `Site` with `wp_api_key` for API key auth; `SiteUserAccess` for per-site grants.
- `PasswordResetToken` model for password reset flows.
- JWT payload fields: `user_id`, `account_id`, `email`, `exp`, `iat`, `type`.
## Execution Flow
- Middleware step: `AccountContextMiddleware` determines `request.account` (session or JWT) and validates plan status; skips admin/auth routes.
- DRF auth step: API key/JWT/session/basic authenticators run in order, potentially setting `request.account` (API key/JWT) and `request.site` (API key).
- Viewsets then apply role/permission checks and tenant/site/sector filtering via base classes in `api/base.py`.
## Cross-Module Interactions
- All module viewsets rely on `request.user` and `request.account` set by the auth stack. Site-aware modules can read `request.site` when API key auth is used.
- Role helpers (`is_admin_or_developer`, `is_system_account_user`) influence filtering bypass in base viewsets.
## State Transitions (if applicable)
- JWT lifetimes: access tokens default to 15 minutes; refresh tokens to 30 days (configurable in settings).
- Session users are refreshed on each request to pick up plan/account changes.
- Password reset tokens track expiry and usage via `expires_at` and `used` flags.
## Error Handling
- Middleware returns JSON errors for missing account or inactive plan and logs out session users in those cases.
- Invalid/expired JWTs cause the JWT authenticator to return `None`, allowing other auth methods; decoding errors raise `InvalidTokenError` in utilities.
- API key auth raises an auth failure when no active user is available for the resolved account.
## Tenancy Rules
- `request.account` is set early; base viewsets enforce account filtering unless user has admin/developer/system-account privileges.
- API key auth also sets `request.site` for integration contexts; site/sector filtering occurs in `SiteSectorModelViewSet`.
## Billing Rules (if applicable)
- Active plan is required for access (middleware enforces). Credit debits/charges are handled in billing modules, not in the auth layer.
## Background Tasks / Schedulers (if applicable)
- Token generation/validation is synchronous. Background tasks should receive explicit user/account identifiers in their payloads when invoked.
## Key Design Considerations
- Authentication stack is ordered to give integration API keys precedence, then JWT for app clients, then session for browser-based flows.
- Tenant context must be established before view logic; do not move or remove `AccountContextMiddleware`.
- Expiry durations and JWT secrets are centrally configured in `settings.py`.
## How Developers Should Work With This Module
- Use token helpers from `auth/utils.py` when issuing tokens; do not handcraft JWTs.
- Mount new auth-sensitive endpoints under existing routers and rely on DRF auth classes instead of custom header parsing.
- Ensure new features that require site context can work with API key auth by checking `request.site`.
- Keep plan enforcement in place; bypass only for admin/system routes when justified.

View File

@@ -1,85 +0,0 @@
# Data Flow Diagrams (Narrative)
## Purpose
Describe end-to-end data movement through the system based on current routing, middleware, and model conventions. No diagrams are embedded; flows are explained textually.
## Code Locations (exact paths)
- Request routing: `backend/igny8_core/urls.py`
- Middleware: `backend/igny8_core/middleware/request_id.py`, `backend/igny8_core/auth/middleware.py`, `backend/igny8_core/middleware/resource_tracker.py`
- DRF base viewsets: `backend/igny8_core/api/base.py`
- Authentication classes: `backend/igny8_core/api/authentication.py`
- Tenancy models: `backend/igny8_core/auth/models.py`
- Celery configuration: `backend/igny8_core/settings.py`
## High-Level Responsibilities
- Trace how HTTP requests are processed, tenant-scoped, authorized, and persisted.
- Show where async processing departs to Celery and where responses are shaped.
## Detailed Behavior
- Incoming request → Django middleware stack:
- Security/WhiteNoise/CORS/session/common/CSRF/Django auth.
- `RequestIDMiddleware` assigns `request.request_id` and returns it in `X-Request-ID`.
- `AccountContextMiddleware` resolves user/account (session or JWT) and enforces active plan; sets `request.account`.
- `ResourceTrackingMiddleware` optionally tracks resource usage for admin/developer users when the `X-Debug-Resource-Tracking` header is true; adds `X-Resource-Tracking-ID`.
- URL dispatch via `urls.py` routes to module routers (auth, account, planner, writer, system, billing, automation, linker, optimizer, publisher, integration) under `/api/v1/*`.
- DRF viewset pipeline:
- Authentication classes (API key → JWT → session → basic) establish `request.user` (and optionally `request.account`/`request.site`).
- Base viewsets (`AccountModelViewSet`, `SiteSectorModelViewSet`) filter querysets by `account`/`site`/`sector` and attach `account` on create.
- Serializers handle validation; responses are wrapped by unified helpers to standardize success/error payloads and pagination.
- Persistence:
- Tenant-scoped models inherit `AccountBaseModel` or `SiteSectorBaseModel`; save hooks enforce account/site/sector alignment.
- Soft deletion is used where models implement `soft_delete`, respecting retention windows from account settings.
- Async/Background:
- Celery uses Redis broker/backend; tasks inherit JSON payloads and time limits from `settings.py`.
- Automation, AI, publishing, and billing tasks enqueue via Celery; results return through database/state updates, not synchronous responses.
- Response:
- Unified response wrappers ensure `success`, `data`/`error`, and request ID are present; paginated responses include `count/next/previous/results`.
- Throttling headers apply per-scope (as configured in `REST_FRAMEWORK` throttles).
## Data Structures / Models Involved (no code)
- Tenancy bases: `AccountBaseModel`, `SiteSectorBaseModel`.
- Core entities: `Account`, `Plan`, `Site`, `Sector`, `User`, `SiteUserAccess`.
- Module-specific models follow the same tenancy bases (documented in module-specific files).
## Execution Flow
1) HTTP request hits middleware; IDs and tenant context are set.
2) DRF authentication authenticates and sets user/account/site.
3) Viewset filters data by tenant/site/sector and runs serializer validation.
4) DB operations persist data with enforced tenant alignment.
5) Optional Celery tasks are queued for long-running work.
6) Response returns unified JSON with request IDs and optional throttling/pagination headers.
## Cross-Module Interactions
- Auth context set in middleware is consumed by all module viewsets for scoping.
- API key auth provides site context for integration/publisher flows.
- Celery configuration is shared by automation/AI/publishing/billing task modules.
## State Transitions (if applicable)
- Entity lifecycle changes (create/update/delete/soft-delete) flow through base viewsets and tenancy bases, ensuring account/site/sector consistency.
- Request lifecycle includes request ID creation, optional resource tracking, and unified response wrapping.
## Error Handling
- Middleware can short-circuit with JSON errors for missing account/plan.
- Viewset overrides wrap validation and server errors into unified responses; missing objects return 404 payloads.
- Throttling (scope-based) returns standard DRF throttle responses with headers.
## Tenancy Rules
- All tenant-bound data flows require `request.account`; filtering and save hooks prevent cross-tenant access.
- Admin/developer/system-account users may bypass tenant filtering; system accounts are guarded against deletion.
- Site/sector alignment is validated on save for models inheriting `SiteSectorBaseModel`.
## Billing Rules (if applicable)
- Plan activation is validated in middleware. Credit debits and billing workflows occur in billing modules (covered elsewhere) after tenant resolution.
## Background Tasks / Schedulers (if applicable)
- Celery broker/backend configuration in `settings.py` governs async flow; tasks should include account/site identifiers to maintain scoping.
## Key Design Considerations
- Request ID and resource tracking enable traceability and performance debugging.
- Middleware ordering ensures tenant context precedes view logic.
- Unified response format keeps clients consistent across modules.
## How Developers Should Work With This Module
- Preserve middleware order; new middleware must not break request ID or tenant context.
- Ensure new viewsets inherit the base classes to pick up scoping and unified responses.
- When adding async tasks, include tenant/site identifiers and respect Celery limits from settings.

View File

@@ -1,79 +0,0 @@
# Multitenancy Model
## Purpose
Explain how tenant, site, and sector isolation is enforced across models, middleware, and viewsets, based on the current implementation.
## Code Locations (exact paths)
- Tenant base models: `backend/igny8_core/auth/models.py` (`AccountBaseModel`, `SiteSectorBaseModel`)
- Core entities: `backend/igny8_core/auth/models.py` (`Account`, `Plan`, `Site`, `Sector`, `Industry`, `IndustrySector`, `SeedKeyword`, `SiteUserAccess`, `User`)
- Middleware for context: `backend/igny8_core/auth/middleware.py`
- DRF base viewsets: `backend/igny8_core/api/base.py`
- Auth utilities and JWT: `backend/igny8_core/api/authentication.py`, `backend/igny8_core/auth/utils.py`
- URL routing (module mounting): `backend/igny8_core/urls.py`
## High-Level Responsibilities
- Enforce per-account isolation for all models carrying an `account` FK.
- Enforce per-site and per-sector scoping for content models via `SiteSectorBaseModel`.
- Inject tenant context on every request (session or JWT/API key), then apply scoping in base viewsets.
- Allow controlled overrides for admins, developers, and system accounts.
## Detailed Behavior
- `AccountBaseModel` adds an `account` FK plus timestamps and indexes; all tenant-scoped models inherit this to guarantee account linkage.
- `SiteSectorBaseModel` extends `AccountBaseModel` with `site` and `sector` FKs, indexes on `(account, site, sector)`, and a save hook that sets `account` from `site` and validates that `sector` belongs to the same `site`; raises validation errors on mismatch.
- `AccountContextMiddleware` sets `request.account` by refreshing the authenticated session user (with account and plan) or by decoding JWT tokens; it rejects requests when account is missing or plan is inactive, returning structured JSON and logging out session users. It skips admin and auth endpoints to avoid interference.
- JWT authentication (`api/authentication.py`) decodes tokens and sets `request.account` from the token payload; API key authentication sets both `request.account` and `request.site` for WordPress bridge calls.
- `AccountModelViewSet` auto-filters querysets by `account` when models expose that field. It bypasses filtering for admins/developers/system-account users; otherwise, it uses `request.account` or falls back to the authenticated users account. Creates set `account` on save when present.
- `SiteSectorModelViewSet` extends the above to filter by site/sector if those fields exist, using request query parameters and tenancy context.
- `User` role helpers (`is_admin_or_developer`, `is_system_account_user`) and account checks gate override behavior.
- `SiteUserAccess` provides explicit per-site access for non-admin roles; `User.get_accessible_sites` respects system account, developer, owner/admin, and granted access rules.
## Data Structures / Models Involved (no code)
- `Account`: tenant container with plan, credits, billing fields, status, and retention settings.
- `Plan`: defines limits (max users/sites/industries/author profiles), credit inclusion, Stripe IDs.
- `Site`: belongs to an account, optionally an industry; includes status, hosting type, legacy WP fields, and SEO metadata.
- `Sector`: belongs to a site and industry sector template; enforces account alignment and plan-based max-sector validation.
- `Industry`, `IndustrySector`, `SeedKeyword`: global reference data not bound to a single account.
- `SiteUserAccess`: explicit many-to-many grants between users and sites.
- `User`: links to account with role-based access flags.
## Execution Flow
- Request enters middleware; `AccountContextMiddleware` determines `request.account` (session or JWT/API key), validating plan status.
- Viewsets inheriting from `AccountModelViewSet`/`SiteSectorModelViewSet` filter querysets by `account` (and site/sector when present) before pagination/serialization.
- Object creation sets `account` automatically when the serializers model has that field; site/sector-based models validate alignment on save.
- Admin/developer/system-account users skip account filtering; other users remain constrained.
## Cross-Module Interactions
- All module viewsets depend on the base viewsets for scoping.
- Automation, planner, writer, billing, linker, optimizer, publisher, and integration models inherit from the tenancy bases to enforce account/site/sector ownership.
- API key flows for WordPress set `request.site`, enabling integration-specific logic to run in a site-aware context.
## State Transitions (if applicable)
- Account status changes (active, suspended, trial, cancelled) and plan activation directly affect access through middleware plan validation.
- Sector creation enforces plan-based limits for active sectors per site.
## Error Handling
- Middleware returns JSON errors for missing account or inactive plan, with HTTP 403 or 402 semantics and logs out session users.
- Base viewsets wrap CRUD operations in unified responses; validation failures or missing objects are returned in structured error payloads.
- Save-time validation on `SiteSectorBaseModel` and `Sector` raises validation errors when site/sector alignment or plan limits are violated.
## Tenancy Rules
- Every tenant-scoped model carries `account`; site/sector-aware models carry `site` and `sector` and must align to the same account.
- Middleware populates `request.account`; base viewsets enforce filtering unless the user is an admin/developer/system-account member.
- System accounts (`aws-admin`, `default-account`, `default`) and privileged roles can bypass scoping; protected from deletion via guard clauses.
## Billing Rules (if applicable)
- Middleware requires an active plan before allowing requests (except auth/admin paths). Credits, charges, and plan enforcement are handled in billing modules (documented elsewhere).
## Background Tasks / Schedulers (if applicable)
- Celery tasks inherit tenant context via payloads supplied by calling viewsets/services; the tenancy bases ensure stored records retain `account`/`site`/`sector`.
## Key Design Considerations
- Tenancy is enforced as early as middleware to avoid leakage in view logic.
- Base viewsets centralize scoping and unified responses to reduce duplication across modules.
- Role-based overrides exist for ops and system accounts; safeguards prevent system account deletion.
## How Developers Should Work With This Module
- Inherit from `AccountBaseModel` or `SiteSectorBaseModel` for any new tenant/site/sector data models.
- Inherit viewsets from `AccountModelViewSet` or `SiteSectorModelViewSet` to get automatic scoping and unified responses.
- Do not bypass `AccountContextMiddleware`; ensure new endpoints live under `/api/v1/*` and rely on the auth stack (API key → JWT → session).
- Validate that new background tasks carry account/site/sector identifiers so downstream saves remain scoped.

View File

@@ -1,61 +0,0 @@
# Tech Stack
## Purpose
Document the concrete technologies and dependencies in use across backend and frontend as defined in the repository.
## Code Locations (exact paths)
- Backend dependency manifest: `backend/requirements.txt`
- Backend settings (framework integration): `backend/igny8_core/settings.py`
- Frontend dependency manifest: `frontend/package.json`
- Frontend build tooling: `frontend/vite.config.ts`, `frontend/tsconfig*.json`
## High-Level Responsibilities
- Backend: Django 5.x with DRF for APIs, Celery for async tasks, Redis for broker/result, PostgreSQL or SQLite for data, drf-spectacular for OpenAPI, Stripe/PayPal configs for billing, WhiteNoise for static serving.
- Frontend: React 19 with Vite, TypeScript, TailwindCSS, Zustand state, React Router 7, ApexCharts and FullCalendar for UI widgets.
## Detailed Behavior
- Backend settings wire DRF pagination, filters, authentication (API key, JWT, session, basic), throttling, schema generation, CORS, Celery, logging, and Stripe/PayPal credentials. Static assets are served via WhiteNoise; admin uses Django contrib.
- `requirements.txt` enumerates runtime libs: Django, gunicorn, psycopg2-binary (PostgreSQL), redis, WhiteNoise, DRF, django-filter, django-cors-headers, PyJWT, requests, Celery, BeautifulSoup4, psutil, docker (for ops scripts), drf-spectacular, and stripe.
- Frontend `package.json` pins React 19, React Router 7, Zustand 5, Vite 6, TailwindCSS 4, ApexCharts 4, FullCalendar 6, react-dnd, dropzone, lucide/react-heroicons, and testing/tooling (Vitest, Testing Library, ESLint).
- Build scripts use Vite for dev and production builds, with separate marketing mode; tests via Vitest; lint via ESLint; type-check via `tsc -b`.
## Data Structures / Models Involved (no code)
- Not applicable; this file tracks technology components rather than domain models.
## Execution Flow
- Backend runs under Django/DRF with middleware and installed apps per `settings.py`. ASGI/WSGI entrypoints in `igny8_core/asgi.py` and `igny8_core/wsgi.py` (default WSGI via gunicorn).
- Celery worker/beat use Redis URLs from `settings.py` and respect JSON serialization/time limits.
- Frontend builds with Vite, consuming environment variables defined via Vite conventions.
## Cross-Module Interactions
- DRF auth classes depend on PyJWT and custom utilities for token handling.
- Celery tasks in AI/automation/publishing rely on Redis connectivity and the configured serializer/time limits.
- Stripe/PayPal keys in settings are consumed by billing modules.
- Frontend API calls rely on the DRF endpoints exposed in `igny8_core/urls.py`.
## State Transitions (if applicable)
- Dependency-driven: none beyond the build/runtime phases (install → build → serve).
## Error Handling
- Backend error handling configured via custom DRF exception handler (enabled by default) and logging setup in `settings.py`.
- Frontend build/test errors are surfaced through Vite/TypeScript/Vitest/ESLint tooling.
## Tenancy Rules
- Implemented at runtime by backend middleware and viewsets; the tech stack provides JWT, session, and API key support to carry tenant context.
## Billing Rules (if applicable)
- Stripe and PayPal keys in `settings.py` enable billing integrations; credit logic is implemented in billing modules (documented elsewhere).
## Background Tasks / Schedulers (if applicable)
- Celery configured in `settings.py` with Redis broker/backend, JSON serialization, and task limits; beat scheduling persists in `celerybeat-schedule`.
## Key Design Considerations
- Keep dependency manifests authoritative (`requirements.txt`, `package.json`).
- Redis is the default async backbone; Postgres is the default DB with SQLite fallback for local/dev.
- Vite + React 19 selected for fast dev/build; TailwindCSS 4 used for styling; Zustand for state.
## How Developers Should Work With This Module
- Add backend dependencies to `requirements.txt` and pin versions appropriately; update settings if new middleware/auth is added.
- Add frontend dependencies to `package.json`; run `npm install` and ensure Vite/TSC builds remain clean.
- Respect configured auth stack (API key → JWT → session) when adding API clients.
- Keep CORS and env vars aligned with the domains/ports in use for local and production.

299
docs/00-SYSTEM/TENANCY.md Normal file
View File

@@ -0,0 +1,299 @@
# Multi-Tenancy Architecture
**Last Verified:** December 25, 2025
**Backend Path:** `backend/igny8_core/auth/models.py`
---
## Data Hierarchy
```
Account (Tenant)
├── Users (team members)
├── Subscription (plan binding)
├── Sites
│ ├── Sectors
│ │ ├── Keywords
│ │ ├── Clusters
│ │ ├── ContentIdeas
│ │ ├── Tasks
│ │ ├── Content
│ │ └── Images
│ └── Integrations (WordPress)
└── Billing (credits, transactions)
```
---
## Core Models
### Account
| Field | Type | Purpose |
|-------|------|---------|
| name | CharField | Account/organization name |
| is_active | Boolean | Enable/disable account |
| credits | Decimal | Current credit balance |
| stripe_customer_id | CharField | Stripe integration |
| paypal_customer_id | CharField | PayPal integration |
| created_at | DateTime | Registration date |
### Plan
| Field | Type | Purpose |
|-------|------|---------|
| name | CharField | Plan name (Free, Starter, Growth, Scale) |
| included_credits | Integer | Monthly credit allocation |
| max_sites | Integer | Site limit |
| max_users | Integer | User limit |
| max_keywords | Integer | Keyword limit |
| max_clusters | Integer | Cluster limit |
| max_content_ideas | Integer | Monthly idea limit |
| max_content_words | Integer | Monthly word limit |
| max_images_basic | Integer | Monthly basic image limit |
| max_images_premium | Integer | Monthly premium image limit |
| is_active | Boolean | Available for purchase |
| is_internal | Boolean | Internal/test plan |
### Site
| Field | Type | Purpose |
|-------|------|---------|
| account | ForeignKey | Owner account |
| name | CharField | Site name |
| domain | CharField | Primary domain |
| is_active | Boolean | Enable/disable |
### Sector
| Field | Type | Purpose |
|-------|------|---------|
| site | ForeignKey | Parent site |
| account | ForeignKey | Owner account |
| industry | ForeignKey | Industry template |
| name | CharField | Sector name |
| is_active | Boolean | Enable/disable |
---
## Base Model Classes
### AccountBaseModel
**File:** `auth/models.py`
All account-scoped models inherit from this:
```python
class AccountBaseModel(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE)
class Meta:
abstract = True
```
**Used by:** Billing records, Settings, API keys
### SiteSectorBaseModel
**File:** `auth/models.py`
All content models inherit from this:
```python
class SiteSectorBaseModel(AccountBaseModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
sector = models.ForeignKey(Sector, on_delete=models.CASCADE)
class Meta:
abstract = True
```
**Used by:** Keywords, Clusters, Ideas, Tasks, Content, Images
---
## ViewSet Base Classes
### AccountModelViewSet
**File:** `api/base.py`
Automatically filters queryset by account:
```python
class AccountModelViewSet(ModelViewSet):
def get_queryset(self):
qs = super().get_queryset()
if not self.request.user.is_admin_or_developer:
qs = qs.filter(account=self.request.account)
return qs
```
### SiteSectorModelViewSet
**File:** `api/base.py`
Filters by account + site + sector:
```python
class SiteSectorModelViewSet(AccountModelViewSet):
def get_queryset(self):
qs = super().get_queryset()
site_id = self.request.query_params.get('site_id')
sector_id = self.request.query_params.get('sector_id')
if site_id:
qs = qs.filter(site_id=site_id)
if sector_id:
qs = qs.filter(sector_id=sector_id)
return qs
```
---
## Where Tenancy Applies
### Uses Site/Sector Filtering
| Module | Filter |
|--------|--------|
| Planner (Keywords, Clusters, Ideas) | site + sector |
| Writer (Tasks, Content, Images) | site + sector |
| Linker | site + sector |
| Optimizer | site + sector |
| Setup/Add Keywords | site + sector |
### Account-Level Only (No Site/Sector)
| Module | Filter |
|--------|--------|
| Billing/Plans | account only |
| Account Settings | account only |
| Team Management | account only |
| User Profile | user only |
| System Settings | account only |
---
## Frontend Implementation
### Site Selection
**Store:** `store/siteStore.ts`
```typescript
const useSiteStore = create({
activeSite: Site | null,
sites: Site[],
loadSites: () => Promise<void>,
setActiveSite: (site: Site) => void,
});
```
### Sector Selection
**Store:** `store/sectorStore.ts`
```typescript
const useSectorStore = create({
activeSector: Sector | null,
sectors: Sector[],
loadSectorsForSite: (siteId: number) => Promise<void>,
setActiveSector: (sector: Sector) => void,
});
```
### Sector Loading Pattern
Sectors are loaded by `PageHeader` component, not `AppLayout`:
```typescript
// PageHeader.tsx
useEffect(() => {
if (hideSiteSector) return; // Skip for account pages
if (activeSite?.id && activeSite?.is_active) {
loadSectorsForSite(activeSite.id);
}
}, [activeSite?.id, hideSiteSector]);
```
**Pages with `hideSiteSector={true}`:**
- `/account/*` (settings, team, billing)
- User profile pages
---
## Global Resources (No Tenancy)
These are system-wide, not tenant-specific:
| Model | Purpose |
|-------|---------|
| Industry | Global industry taxonomy |
| IndustrySector | Sub-categories within industries |
| SeedKeyword | Global keyword database |
| GlobalIntegrationSettings | Platform API keys |
| GlobalAIPrompt | Default prompt templates |
| GlobalAuthorProfile | Author persona templates |
---
## Tenant Data vs System Data
### System Data (KEEP on reset)
- Plans, CreditPackages
- Industries, IndustrySectors
- GlobalIntegrationSettings
- GlobalAIPrompt, GlobalAuthorProfile
- CreditCostConfig
- PaymentMethodConfig
### Tenant Data (CLEAN on reset)
- Accounts, Users, Sites, Sectors
- Keywords, Clusters, Ideas, Tasks, Content, Images
- Subscriptions, Invoices, Payments
- CreditTransactions, CreditUsageLogs
- SiteIntegrations, SyncEvents
- AutomationConfigs, AutomationRuns
---
## Admin/Developer Bypass
Admin and developer users can access all tenants:
```python
# In auth/models.py
class User:
@property
def is_admin_or_developer(self):
return self.role in ['admin', 'developer']
```
**Bypass Rules:**
- Admin: Full access to own account
- Developer: Full access to ALL accounts
- System accounts protected from deletion
---
## Common Issues
| Issue | Cause | Fix |
|-------|-------|-----|
| Data leak between accounts | Missing account filter | Use AccountModelViewSet |
| Wrong site data | Site not validated for account | Validate site.account == request.account |
| Sector not loading | Site changed but sector not reset | Clear activeSector when activeSite changes |
| Admin can't see all data | Incorrect role check | Check is_admin_or_developer |
---
## Planned Changes
| Feature | Status | Description |
|---------|--------|-------------|
| Account switching | 🔜 Planned | Allow users to switch between accounts |
| Sub-accounts | 🔜 Planned | Hierarchical account structure |