Metadata-Version: 2.4
Name: apex-saas-framework
Version: 0.1.10
Summary: A pure Python SDK for multi-tenant SaaS (users, auth, RBAC, payments, email, files), database-agnostic and framework-agnostic.
Author-email: ApexNeural <example@apexneural.com>
Maintainer-email: ApexNeural <example@apexneural.com>
License: MIT
Project-URL: Homepage, https://github.com/apexneural/apex-saas-framework
Project-URL: Documentation, https://github.com/apexneural/apex-saas-framework#readme
Project-URL: Repository, https://github.com/apexneural/apex-saas-framework
Project-URL: Issues, https://github.com/apexneural/apex-saas-framework/issues
Project-URL: Bug Tracker, https://github.com/apexneural/apex-saas-framework/issues
Project-URL: Changelog, https://github.com/apexneural/apex-saas-framework/releases
Keywords: saas,sdk,multi-tenant,authentication,rbac,payments,database-agnostic,async
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sqlalchemy>=2.0.0
Requires-Dist: alembic>=1.12.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: pydantic-settings>=2.1.0
Requires-Dist: python-jose[cryptography]>=3.3.0
Requires-Dist: passlib[bcrypt]>=1.7.4
Requires-Dist: typer>=0.9.0
Requires-Dist: psycopg2-binary>=2.9.9
Requires-Dist: asyncpg>=0.29.0
Requires-Dist: httpx>=0.25.0
Requires-Dist: sendgrid>=6.11.0
Requires-Dist: bcrypt==4.0.1
Provides-Extra: dev
Requires-Dist: pytest>=7.4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: httpx>=0.25.0; extra == "dev"
Requires-Dist: black>=23.11.0; extra == "dev"
Requires-Dist: ruff>=0.1.6; extra == "dev"
Requires-Dist: mypy>=1.7.0; extra == "dev"
Dynamic: license-file

# Apex SaaS Framework

**A pure Python library - No APIs provided. You write your own APIs using the features you need.**

- ✅ **No API endpoints** - You write your own APIs (FastAPI, Flask, Django, etc.)
- ✅ **Use only what you need** - Pick the features you want
- ✅ **Your database choice** - SQLite, PostgreSQL, MySQL, or any SQLAlchemy-supported database
- ✅ **Minimal code** - Just database URL and you're ready

## ⚡ Super Quick Start

```python
# my_app.py - YOU CREATE THIS FILE
from apex import Client
from apex.core.base import Base
from sqlalchemy import Column, Integer, String

# Define your models (your choice - primary keys, foreign keys, fields!)
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)  # Your primary key choice
    email = Column(String(255), unique=True)
    password_hash = Column(String(255))

# Initialize - Just database URL (your choice!)
client = Client(
    database_url="sqlite+aiosqlite:///./mydb.db",  # SQLite (easiest)
    # or "postgresql+asyncpg://..." for PostgreSQL
    # or "mysql+aiomysql://..." for MySQL
    user_model=User
)

# Use it in your code (you write your own APIs!)
async with client:
    await client.init_database()
    user = await client.users.create(email="user@example.com", password="pass123")
    # Use in your FastAPI/Flask/Django endpoints
```

**That's it!** You write your own APIs using the features you need! 🎉

## 🎯 Key Features

- ✅ **Pure Python Library** - No APIs provided, you write your own
- ✅ **Use Only What You Need** - Pick features: users, auth, payments, email, etc.
- ✅ **Your Database Choice** - SQLite, PostgreSQL, MySQL, or any SQLAlchemy-supported database
- ✅ **Flexible Models** - Use all default fields, disable unwanted ones, or add custom fields
- ✅ **Minimal Code** - Just database URL and you're ready
- ✅ **Framework Agnostic** - Works with FastAPI, Flask, Django, or standalone

## 📝 Complete Model Control

### Option 1: Define Everything Yourself (Full Control)
```python
from apex import Client
from apex.core.base import Base
from sqlalchemy import Column, Integer, String, ForeignKey

# You define primary key, foreign keys, fields - everything!
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)  # Your primary key choice
    email = Column(String(255), unique=True)
    password_hash = Column(String(255))
    company_id = Column(Integer, ForeignKey("companies.id"))  # Your foreign key

class Company(Base):
    __tablename__ = "companies"
    id = Column(Integer, primary_key=True)
    name = Column(String(255))
```

### Option 2: Use Flexible Helpers (Optional)
```python
from apex import FlexibleUser, IntegerPKMixin

class User(FlexibleUser, IntegerPKMixin):  # Integer primary key
    __tablename__ = "users"
    # Add your own fields, foreign keys, etc.
```

### Option 3: Mix and Match
```python
from apex.core.base import Base, TimestampMixin
from sqlalchemy import Column, String

class User(Base, TimestampMixin):  # Use timestamps mixin
    __tablename__ = "users"
    user_id = Column(String(50), primary_key=True)  # Your custom primary key
    email = Column(String(255))
```

**Key Points:**
- ✅ **You choose primary key type** (Integer, String, UUID, or any type)
- ✅ **You define foreign keys** (any name, any reference)
- ✅ **You add any fields** (any fields, any names)
- ✅ **Package adapts** to your structure automatically

See [CLERK_STYLE_GUIDE.md](CLERK_STYLE_GUIDE.md) and [DYNAMIC_MODELS_GUIDE.md](DYNAMIC_MODELS_GUIDE.md) for complete documentation.

## 🚀 Use It Anywhere (no bundled APIs)

The package provides **functions only**. You can call them from any Python environment—scripts, CLI tools, background workers, or any web framework you choose.

**Available Features (use only what you need):**
- ✅ User management
- ✅ Authentication (JWT tokens)
- ✅ Organization management
- ✅ Role-based permissions (RBAC)
- ✅ PayPal payment integration
- ✅ Email sending
- ✅ File storage
- ✅ Settings management
- ✅ Feature flags (modules)

## 📦 Installation

### From PyPI (Production - Recommended)

**Simple installation:**

```bash
pip install apex-saas-framework
```

That's it! No special flags needed! ✅

### From Test PyPI (For Testing)

If you want to test pre-release versions:

```bash
pip install --index-url https://pypi.org/simple/ --extra-index-url https://test.pypi.org/simple/ apex-saas-framework
```

## 🚀 Features

- 🎯 **Simple API** - Minimal imports, maximum functionality
- 🏢 **Multi-tenant Architecture** - Organizations with row-level isolation
- 👥 **User Management** - Complete user system with JWT authentication
- 🔐 **Authentication** - Signup, login, forgot/reset password, change password
- 🛡️ **RBAC** - Role-based access control with permissions
- 📦 **Extensible Models** - Extend base models using ORM inheritance
- 🗄️ **PostgreSQL** - SQLAlchemy 2.0 with async support
- 💳 **PayPal Integration** - Subscription management and webhooks
- ⚙️ **JSONB Settings** - Flexible user and organization settings
- 🎨 **Feature Flags** - Module system for enabling/disabling features
- 📧 **Email Integration** - SendGrid adapter with templates
- 🛠️ **CLI Tools** - Command-line utilities

## 💡 Simple Examples

### Minimal Usage
```python
from apex import Client
from apex.core.base import Base
from sqlalchemy import Column, Integer, String

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True)
    password_hash = Column(String(255))

client = Client(database_url="sqlite+aiosqlite:///./mydb.db", user_model=User)

async with client:
    await client.init_database()
    user = await client.users.create(email="user@example.com", password="pass123")
```

## 🎯 Quick Start Guide

### 1. Create Your Models

Start by re-exporting the Apex defaults so each table is only registered once:

```python
# app/models.py
from apex.app.models.default import (
    Organization,
    OrganizationLocation,
    Permission,
    Role,
    User,
)

__all__ = ["Organization", "OrganizationLocation", "Permission", "Role", "User"]
```

> Need custom columns? Create **new subclasses** (e.g. `class CustomUser(User): ...`)
> in a separate module and update the dependency overrides (`get_user_service`,
> `get_organization_model`, etc.) to point at them. Avoid redefining the stock
> tables in place—SQLAlchemy will raise “Table … already defined” otherwise.

### 2. Configure Settings

Copy `.env.example` to `.env` and update with your values:

```bash
cp .env.example .env
```

**Minimum required configuration:**

```env
# Database (Required)
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

# Security (Required)
SECRET_KEY=your-secret-key-here-minimum-32-characters
```

**Generate a secure secret key:**

```bash
python -c "import secrets; print(secrets.token_urlsafe(32))"
```

See [Configuration Reference](#configuration) below for all available options.

### 3. Set Up Database

```python
# app/database.py
from apex.infrastructure.database import Base, engine
from app.models import User, Organization, Role, Permission

# Create tables
async def init_db():
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
```

### 4. Create Your FastAPI App

```python
# app.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from apex.api.v1.auth.router import router as auth_router
from apex.core.authentication.dependencies import get_auth_service
from apex.core.config import get_settings
from app.dependencies import get_auth_service as project_get_auth_service

settings = get_settings()

app = FastAPI(
    title=settings.APP_NAME,
    version=settings.APP_VERSION,
    debug=settings.DEBUG,
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=settings.CORS_ALLOW_CREDENTIALS,
    allow_methods=settings.CORS_ALLOW_METHODS,
    allow_headers=settings.CORS_ALLOW_HEADERS,
)

app.dependency_overrides[get_auth_service] = project_get_auth_service
app.include_router(auth_router, prefix=settings.API_V1_PREFIX)

@app.get("/")
async def root():
    return {"message": "Apex"}
```

### 5. Override Auth Service

```python
# app/services.py
from apex.domain.services.auth import AuthService
from app.models import User
from sqlalchemy.ext.asyncio import AsyncSession

def get_auth_service(db: AsyncSession) -> AuthService:
    return AuthService(session=db, user_model=User)
```

### 6. Run Migrations

Set up Alembic:

```bash
alembic init migrations
```

Copy `apex/migrations/env.py` to your project and modify it to import your models:

```python
# migrations/env.py
from apex.infrastructure.database.base import Base
from app.models import User, Organization, Role, Permission

target_metadata = Base.metadata
```

Create and run migrations:

```bash
alembic revision --autogenerate -m "Initial migration"
alembic upgrade head
```

## Architecture

### Project Structure

```
apex/
├── core/                    # Core functionality
│   ├── config/              # Settings (Pydantic BaseSettings)
│   ├── security/            # Password hashing, JWT utilities
│   ├── authentication/      # Auth dependencies (get_current_user, etc.)
│   ├── permissions/         # RBAC dependencies (require_role, require_permission)
│   └── utils/               # Utility functions
├── domain/                  # Business logic layer
│   ├── models/              # Base domain models (abstract)
│   │   ├── user.py          # BaseUser model
│   │   ├── organization.py  # BaseOrganization, BaseOrganizationLocation
│   │   ├── role.py          # BaseRole model
│   │   └── permission.py    # BasePermission model
│   ├── repositories/        # Repository pattern (BaseRepository)
│   └── services/            # Business services (AuthService, UserService)
├── infrastructure/          # External adapters
│   ├── database/            # Database setup (SQLAlchemy async, session mgmt)
│   ├── email/               # Email adapters (SMTP, abstract interface)
│   ├── storage/             # File storage adapters (Local, S3-ready)
│   └── payments/            # Payment scaffolding (Stripe-ready)
├── api/                     # API layer
│   └── v1/                  # API version 1
│       ├── auth/            # Authentication routes
│       │   └── router.py    # Login, logout, refresh, me endpoints
│       └── schemas/         # Pydantic request/response schemas
│           ├── auth.py      # Auth schemas
│           └── user.py      # User schemas
├── migrations/              # Alembic templates
│   ├── env.py               # Alembic environment (copy to user project)
│   └── script.py.mako       # Migration script template
└── cli/                     # CLI utilities (Typer)
    └── main.py              # CLI commands (version, check, init-db, etc.)
```

### Design Principles

1. **Clean Architecture** - Separation of concerns with clear layers
2. **Extensibility** - Extend base classes, don't modify them
3. **Type Safety** - Full type hints throughout
4. **Async First** - Built for async/await patterns
5. **Framework Agnostic Core** - Business logic independent of FastAPI

## Usage Examples

### Authentication

```python
from apex.domain.services.auth import AuthService
from app.models import User

# Login
auth_service = AuthService(session=db, user_model=User)
user = await auth_service.authenticate_user("user@example.com", "password")
tokens = await auth_service.create_tokens(user)
```

### User Management

```python
from apex.domain.services.user import UserService

user_service = UserService(session=db, user_model=User)

# Create user
user = await user_service.create_user(
    email="user@example.com",
    password="securepassword",
    full_name="John Doe",
)

# Update user
await user_service.update_user(user, full_name="Jane Doe")

# Change password
await user_service.change_password(user, "oldpass", "newpass")
```

### Permissions

```python
from apex.core.permissions import require_permission, require_role

# In your router
@router.post("/users")
async def create_user(
    current_user: dict = Depends(require_permission("users", "create"))
):
    # User has "users:create" permission
    pass

@router.delete("/users/{id}")
async def delete_user(
    id: str,
    current_user: dict = Depends(require_role("admin"))
):
    # User has "admin" role
    pass
```

### File Storage

```python
from apex.infrastructure.storage import LocalStorageAdapter

storage = LocalStorageAdapter()

# Upload file
file_path = await storage.upload_file(
    file=uploaded_file,
    filename="document.pdf",
    folder="documents"
)

# Get file URL
url = await storage.get_file_url(file_path)
```

### Email

```python
from apex.infrastructure.email import SMTPEmailAdapter

email = SMTPEmailAdapter()

await email.send_email(
    to="user@example.com",
    subject="Welcome!",
    body="Welcome to our platform",
    html="<h1>Welcome!</h1>"
)
```

## Configuration

All configuration is done via environment variables or `.env` file. Copy `.env.example` to `.env` and customize.

### Required Settings

| Variable | Description | Example |
|----------|-------------|---------|
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@localhost:5432/db` |
| `SECRET_KEY` | JWT secret key (min 32 chars) | Generate with `secrets.token_urlsafe(32)` |

### Application Settings

| Variable | Description | Default |
|----------|-------------|---------|
| `APP_NAME` | Application name | `"Apex Backend"` |
| `DEBUG` | Debug mode | `False` |
| `API_V1_PREFIX` | API version 1 prefix | `"/api/v1"` |

### Database Settings

| Variable | Description | Default |
|----------|-------------|---------|
| `DB_ECHO` | Log SQL queries | `False` |
| `DB_POOL_SIZE` | Connection pool size | `5` |
| `DB_MAX_OVERFLOW` | Max overflow connections | `10` |

### JWT/Auth Settings

| Variable | Description | Default |
|----------|-------------|---------|
| `ALGORITHM` | JWT algorithm | `"HS256"` |
| `ACCESS_TOKEN_EXPIRE_MINUTES` | Access token lifetime | `30` |
| `REFRESH_TOKEN_EXPIRE_DAYS` | Refresh token lifetime | `7` |

### CORS Settings

| Variable | Description | Default |
|----------|-------------|---------|
| `CORS_ORIGINS` | Allowed origins (comma-separated) | `"http://localhost:3000"` |
| `CORS_ALLOW_CREDENTIALS` | Allow credentials | `True` |
| `CORS_ALLOW_METHODS` | Allowed HTTP methods | `"GET,POST,PUT,DELETE,PATCH,OPTIONS"` |
| `CORS_ALLOW_HEADERS` | Allowed headers | `"*"` |

### Email Settings (Optional)

| Variable | Description | Example |
|----------|-------------|---------|
| `SMTP_HOST` | SMTP server | `smtp.gmail.com` |
| `SMTP_PORT` | SMTP port | `587` |
| `SMTP_USER` | SMTP username | `your-email@gmail.com` |
| `SMTP_PASSWORD` | SMTP password | Your app password |
| `SMTP_FROM_EMAIL` | From email address | `noreply@yourdomain.com` |
| `SMTP_USE_TLS` | Use TLS | `True` |

### Storage Settings

| Variable | Description | Default |
|----------|-------------|---------|
| `STORAGE_TYPE` | Storage type (`local` or `s3`) | `"local"` |
| `STORAGE_LOCAL_PATH` | Local storage path | `"./uploads"` |
| `AWS_ACCESS_KEY_ID` | AWS access key (if using S3) | - |
| `AWS_SECRET_ACCESS_KEY` | AWS secret key (if using S3) | - |
| `AWS_REGION` | AWS region (if using S3) | `"us-east-1"` |
| `AWS_S3_BUCKET` | S3 bucket name (if using S3) | - |

### Payment Settings (Optional)

| Variable | Description | Example |
|----------|-------------|---------|
| `STRIPE_SECRET_KEY` | Stripe secret key | `sk_test_...` |
| `STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | `pk_test_...` |
| `STRIPE_WEBHOOK_SECRET` | Stripe webhook secret | `whsec_...` |

### Multi-tenancy

| Variable | Description | Default |
|----------|-------------|---------|
| `MULTI_TENANT_MODE` | Enable multi-tenancy | `True` |

## CLI Commands

The `apex` command-line tool provides utilities for managing your application:

```bash
# Show package version
apex version

# Check configuration and database connection
apex check

# Initialize database (base implementation - extend in your app)
apex init-db

# Create superuser (base implementation - extend in your app)
apex create-superuser --email admin@example.com --password
```

**Note:** `init-db` and `create-superuser` are base implementations. You should extend these commands in your application to work with your specific User model.

## Extending the Framework

### Custom User Model

```python
class User(BaseUser):
    __tablename__ = "users"
    
    # Add custom fields
    avatar_url: Mapped[str | None] = None
    bio: Mapped[str | None] = None
```

### Custom Service

```python
from apex.domain.services.user import UserService

class CustomUserService(UserService):
    async def get_users_by_organization(self, org_id: str):
        # Custom logic
        pass
```

### Custom Repository

```python
from apex.domain.repositories.base import BaseRepository

class UserRepository(BaseRepository[User]):
    async def get_by_email(self, email: str) -> User | None:
        # Custom query
        pass
```

## Testing

```python
from fastapi.testclient import TestClient
from apex.infrastructure.database import get_db

# Override database dependency
app.dependency_overrides[get_db] = get_test_db

client = TestClient(app)
response = client.post("/api/v1/auth/login", json={
    "email": "test@example.com",
    "password": "password"
})
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

MIT License - see LICENSE file for details.

## Project Files

- `pyproject.toml` - Package configuration and dependencies
- `.env.example` - Template for environment variables (copy to `.env`)
- `.gitignore` - Git ignore patterns
- `.cursor/rules/cursorrules.mdc` - Cursor AI editing rules
- `README.md` - This file

## Support

- 📖 [Documentation](https://github.com/yourusername/apex#readme)
- 🐛 [Issue Tracker](https://github.com/yourusername/apex/issues)
- 💬 [Discussions](https://github.com/yourusername/apex/discussions)

## Roadmap

- [ ] S3 storage adapter implementation
- [ ] Stripe payment integration
- [ ] Audit logging system
- [ ] Module/feature flag system
- [ ] WebSocket support
- [ ] GraphQL support
- [ ] More comprehensive tests
- [ ] Docker deployment examples

---

Built with ❤️ using FastAPI, SQLAlchemy, and modern Python practices.

