Files
verify/backend/app/routers/orgs.py
Mohamed Mathar Irfan ed6610d5d8 Initial project upload
2026-07-28 17:57:02 +05:30

160 lines
5.8 KiB
Python

import re
from fastapi import APIRouter, Depends, HTTPException, status
from app.core.deps import current_principal, current_user, require_client_roles, Principal
from app.models import Organization, OrgMember, Plan, Subscription, StorageUsage, AuditLog
from app.models.enums import ClientRole
from app.schemas.common import OrgCreate
router = APIRouter(tags=["organization"])
@router.get("/me")
async def me(p: Principal = Depends(current_principal)):
"""Who am I — used by the frontend to route admins vs clients."""
return {
"id": str(p.user.id),
"name": p.user.name,
"email": p.user.email,
"email_verified": p.user.email_verified,
"is_product_admin": p.user.is_product_admin,
"product_admin_role": p.admin_role,
"client_role": p.client_role,
"has_org": p.org is not None,
"org_name": p.org.name if p.org else None,
}
def _slug(name: str) -> str:
base = re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-")
return base or "org"
@router.post("/organization", status_code=201)
async def create_org(body: OrgCreate, user=Depends(current_user)):
existing = await OrgMember.find_one(OrgMember.user_id == str(user.id))
if existing:
raise HTTPException(status.HTTP_409_CONFLICT, "User already belongs to an organization")
free = await Plan.find_one(Plan.name == "Free")
slug = _slug(body.name)
if await Organization.find_one(Organization.slug == slug):
slug = f"{slug}-{str(user.id)[-4:]}"
org = Organization(name=body.name, slug=slug, gstin=body.gstin, pan=body.pan,
contact_email=body.contact_email or user.email,address=body.address,
phone=body.phone, website=body.website,
plan_id=str(free.id) if free else None)
await org.insert()
await OrgMember(org_id=str(org.id), user_id=str(user.id),
role=ClientRole.admin, status="active").insert()
await StorageUsage(org_id=str(org.id),
total_bytes=(free.storage_gb if free else 5) * 1024 ** 3).insert()
if free:
await Subscription(org_id=str(org.id), plan_id=str(free.id),
plan_name=free.name).insert()
await AuditLog(org_id=str(org.id), user_id=str(user.id), user_name=user.name,
action="org.create", module="organization",
details=f"Created org {org.name}").insert()
return {"id": str(org.id), "name": org.name, "slug": org.slug}
@router.get("/organization")
async def get_org(p: Principal = Depends(current_principal)):
if not p.org:
raise HTTPException(status.HTTP_404_NOT_FOUND, "No organization")
plan = await Plan.get(p.org.plan_id) if p.org.plan_id else None
return {
"id": str(p.org.id), "name": p.org.name, "slug": p.org.slug,
"logo_url": p.org.logo_url, "gstin": p.org.gstin, "pan": p.org.pan,
"timezone": p.org.timezone, "language": p.org.language,
"subscription_status": p.org.subscription_status,"address": p.org.address,
"phone": p.org.phone,
"website": p.org.website,
"contact_email": p.org.contact_email,
"address":p.org.address,"country": p.org.country,
"product_count": p.org.product_count, "user_count": p.org.user_count,
"plan": {"name": plan.name, "product_limit": plan.product_limit,
"user_limit": plan.user_limit, "price": plan.price} if plan else None,
"your_role": p.client_role,
}
@router.put("/organization")
async def update_org(body: dict, p: Principal = Depends(require_client_roles(ClientRole.admin))):
allowed = {"name", "logo_url", "gstin", "pan", "address", "contact_email","country",
"phone", "website", "timezone", "language"}
for k, v in body.items():
if k in allowed:
setattr(p.org, k, v)
await p.org.save()
await AuditLog(
org_id=str(p.org.id),
user_id=str(p.user.id),
user_name=p.user.name,
user_role=p.client_role,
action="organization.update",
module="organization",
details=f"Updated organization {p.org.name}",
).insert()
return {"message": "updated"}
from datetime import datetime
from typing import Optional
@router.get("/audit-logs")
async def org_audit_logs(
p: Principal = Depends(require_client_roles(ClientRole.admin)),
limit: int = 50,
module: Optional[str] = None,
user: Optional[str] = None,
from_date: Optional[str] = None,
to_date: Optional[str] = None,
):
query = AuditLog.find(AuditLog.org_id == str(p.org.id))
if module:
query = query.find(AuditLog.module == module)
if user:
query = query.find(AuditLog.user_name == user)
if from_date:
query = query.find(
AuditLog.created_at >= datetime.fromisoformat(from_date)
)
if to_date:
query = query.find(
AuditLog.created_at <= datetime.fromisoformat(to_date + "T23:59:59")
)
logs = await query.sort(-AuditLog.created_at).limit(limit).to_list()
return [
{
"at": l.created_at,
"user": l.user_name,
"role": l.user_role,
"action": l.action,
"module": l.module,
"details": l.details,
"ip": l.ip,
}
for l in logs
]
@router.get("/roles")
async def list_roles():
return [
{"role": "admin", "label": "Admin",
"description": "Full access within the organization: products, QRs, batches, compliance, users, billing."},
{"role": "finance", "label": "Finance",
"description": "Billing, invoices, payment methods, subscription within the org only."},
{"role": "user", "label": "User",
"description": "Create products, generate QRs, manage batches, view analytics. No billing/invites."},
]