system-prompts-and-models-o.../salesflow-saas/backend/app/api/v1/users.py
Claude f1852c1121
Add SalesMatic AI Sales SaaS Platform - Complete Foundation
Full-stack AI-powered sales automation platform for Saudi SMEs:

Backend (FastAPI + PostgreSQL):
- Multi-tenant architecture with row-level isolation
- JWT auth with RBAC (owner/manager/agent/admin)
- Lead, Customer, Deal, Pipeline, Activity, Message, Proposal models
- Dashboard analytics API (overview, pipeline, revenue)
- WhatsApp Business API, Email (SMTP/SendGrid), SMS (Unifonic) integrations
- Celery + Redis workers for automated follow-ups and scheduled messages
- Property model for Real Estate module (Riyadh districts)
- Hijri date utilities, Arabic/English localization

Frontend (Next.js + Tailwind):
- Professional Arabic RTL landing page with 10 sections
- Brand identity: SalesMatic (سيلزماتك) with custom SVG logo
- Color system: Trust Blue #0F4C81, Growth Teal #00BFA6, CTA Orange #FF6B35
- IBM Plex Sans Arabic + Inter typography
- Responsive design, dark hero section, pricing table, FAQ

Industry Templates:
- Healthcare/Clinics: pipeline stages, WhatsApp message templates, auto-workflows
- Real Estate Riyadh: 20 districts, property tours, payment plans, matching

Infrastructure:
- Docker Compose (PostgreSQL, Redis, Backend, Celery, Frontend, Nginx)
- Nginx reverse proxy config
- Makefile for common operations

https://claude.ai/code/session_01LLR7jzpyNRwDA9kojtT3CW
2026-03-28 03:06:53 +00:00

117 lines
3.5 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel
from typing import Optional
from uuid import UUID
from datetime import datetime
from app.database import get_db
from app.api.deps import get_current_user, require_role
from app.models.user import User
from app.utils.security import hash_password
router = APIRouter()
class UserResponse(BaseModel):
id: UUID
tenant_id: UUID
email: str
full_name: Optional[str]
full_name_ar: Optional[str]
role: str
phone: Optional[str]
is_active: bool
last_login: Optional[datetime]
created_at: datetime
model_config = {"from_attributes": True}
class UserCreate(BaseModel):
email: str
password: str
full_name: str
full_name_ar: Optional[str] = None
role: str = "agent"
phone: Optional[str] = None
class UserUpdate(BaseModel):
full_name: Optional[str] = None
full_name_ar: Optional[str] = None
role: Optional[str] = None
phone: Optional[str] = None
is_active: Optional[bool] = None
@router.get("", response_model=list[UserResponse])
async def list_users(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(User).where(User.tenant_id == current_user.tenant_id).order_by(User.created_at))
return [UserResponse.model_validate(u) for u in result.scalars().all()]
@router.post("", response_model=UserResponse, status_code=201)
async def create_user(
data: UserCreate,
current_user: User = Depends(require_role("owner", "admin", "manager")),
db: AsyncSession = Depends(get_db),
):
existing = await db.execute(select(User).where(User.email == data.email, User.tenant_id == current_user.tenant_id))
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="Email already exists in this company")
user = User(
tenant_id=current_user.tenant_id,
email=data.email,
password_hash=hash_password(data.password),
full_name=data.full_name,
full_name_ar=data.full_name_ar,
role=data.role,
phone=data.phone,
)
db.add(user)
await db.flush()
await db.refresh(user)
return UserResponse.model_validate(user)
@router.put("/{user_id}", response_model=UserResponse)
async def update_user(
user_id: UUID,
data: UserUpdate,
current_user: User = Depends(require_role("owner", "admin")),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(User).where(User.id == user_id, User.tenant_id == current_user.tenant_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="User not found")
for field, value in data.model_dump(exclude_none=True).items():
setattr(user, field, value)
await db.flush()
await db.refresh(user)
return UserResponse.model_validate(user)
@router.delete("/{user_id}", status_code=204)
async def delete_user(
user_id: UUID,
current_user: User = Depends(require_role("owner", "admin")),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(User).where(User.id == user_id, User.tenant_id == current_user.tenant_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="User not found")
if user.id == current_user.id:
raise HTTPException(status_code=400, detail="Cannot delete yourself")
user.is_active = False
await db.flush()