hesabixArc/hesabixAPI/app/main.py

377 lines
16 KiB
Python
Raw Normal View History

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
2025-09-15 13:53:54 +03:30
from app.core.settings import get_settings
from app.core.logging import configure_logging
from adapters.api.v1.health import router as health_router
from adapters.api.v1.auth import router as auth_router
from adapters.api.v1.users import router as users_router
2025-09-20 01:17:27 +03:30
from adapters.api.v1.businesses import router as businesses_router
2025-09-28 23:06:53 +03:30
from adapters.api.v1.currencies import router as currencies_router
from adapters.api.v1.business_dashboard import router as business_dashboard_router
2025-09-25 01:01:27 +03:30
from adapters.api.v1.business_users import router as business_users_router
2025-09-27 21:19:00 +03:30
from adapters.api.v1.accounts import router as accounts_router
2025-09-30 17:12:53 +03:30
from adapters.api.v1.categories import router as categories_router
from adapters.api.v1.product_attributes import router as product_attributes_router
from adapters.api.v1.products import router as products_router
from adapters.api.v1.price_lists import router as price_lists_router
2025-10-11 02:13:18 +03:30
from adapters.api.v1.invoices import router as invoices_router
2025-09-25 22:36:08 +03:30
from adapters.api.v1.persons import router as persons_router
2025-10-05 02:33:08 +03:30
from adapters.api.v1.customers import router as customers_router
2025-10-03 02:25:35 +03:30
from adapters.api.v1.bank_accounts import router as bank_accounts_router
2025-10-03 17:02:07 +03:30
from adapters.api.v1.cash_registers import router as cash_registers_router
2025-10-04 17:17:53 +03:30
from adapters.api.v1.petty_cash import router as petty_cash_router
2025-09-30 17:12:53 +03:30
from adapters.api.v1.tax_units import router as tax_units_router
from adapters.api.v1.tax_types import router as tax_types_router
2025-09-20 22:46:06 +03:30
from adapters.api.v1.support.tickets import router as support_tickets_router
from adapters.api.v1.support.operator import router as support_operator_router
from adapters.api.v1.support.categories import router as support_categories_router
from adapters.api.v1.support.priorities import router as support_priorities_router
from adapters.api.v1.support.statuses import router as support_statuses_router
2025-09-21 19:53:21 +03:30
from adapters.api.v1.admin.file_storage import router as admin_file_storage_router
from adapters.api.v1.admin.email_config import router as admin_email_config_router
2025-10-14 23:16:28 +03:30
from adapters.api.v1.receipts_payments import router as receipts_payments_router
2025-10-16 13:02:03 +03:30
from adapters.api.v1.fiscal_years import router as fiscal_years_router
from app.core.i18n import negotiate_locale, Translator
from app.core.error_handlers import register_error_handlers
2025-09-18 10:44:23 +03:30
from app.core.smart_normalizer import smart_normalize_json, SmartNormalizerConfig
from app.core.calendar_middleware import add_calendar_type
2025-09-15 13:53:54 +03:30
def create_app() -> FastAPI:
settings = get_settings()
configure_logging(settings)
application = FastAPI(
title=settings.app_name,
version=settings.app_version,
debug=settings.debug,
2025-09-20 01:17:27 +03:30
description="""
# Hesabix API
API جامع برای مدیریت کاربران، احراز هویت و سیستم معرفی
## ویژگی‌های اصلی:
- **احراز هویت**: ثبتنام، ورود، فراموشی رمز عبور
- **مدیریت کاربران**: لیست، جستجو، فیلتر و آمار کاربران
- **سیستم معرفی**: آمار و مدیریت معرفیها
- **خروجی**: PDF و Excel برای گزارشها
- **امنیت**: کپچا، کلیدهای API، رمزگذاری
## 🔐 احراز هویت (Authentication)
### کلیدهای API
تمام endpoint های محافظت شده نیاز به کلید API دارند که در header `Authorization` ارسال میشود:
```
Authorization: Bearer sk_your_api_key_here
```
### نحوه دریافت کلید API:
1. **ثبتنام**: با ثبتنام، یک کلید session دریافت میکنید
2. **ورود**: با ورود موفق، کلید session دریافت میکنید
3. **کلیدهای شخصی**: از endpoint `/api/v1/auth/api-keys` میتوانید کلیدهای شخصی ایجاد کنید
### انواع کلیدهای API:
- **Session Keys**: کلیدهای موقت که با ورود ایجاد میشوند
- **Personal Keys**: کلیدهای دائمی که خودتان ایجاد میکنید
### مثال درخواست با احراز هویت:
```bash
curl -X GET "http://localhost:8000/api/v1/auth/me" \\
-H "Authorization: Bearer sk_1234567890abcdef" \\
-H "Accept: application/json"
```
## 🛡️ مجوزهای دسترسی (Permissions)
برخی endpoint ها نیاز به مجوزهای خاص دارند:
### مجوزهای اپلیکیشن (App-Level Permissions):
- `user_management`: دسترسی به مدیریت کاربران
- `superadmin`: دسترسی کامل به سیستم
- `business_management`: مدیریت کسب و کارها
- `system_settings`: دسترسی به تنظیمات سیستم
### مثال مجوزها در JSON:
```json
{
"user_management": true,
"superadmin": false,
"business_management": true,
"system_settings": false
}
```
### endpoint های محافظت شده:
- تمام endpoint های `/api/v1/users/*` نیاز به مجوز `user_management` دارند
- endpoint های `/api/v1/auth/me` و `/api/v1/auth/api-keys/*` نیاز به احراز هویت دارند
## 🌍 چندزبانه (Internationalization)
API از چندزبانه پشتیبانی میکند:
### هدر زبان:
```
Accept-Language: fa
Accept-Language: en
Accept-Language: fa-IR
Accept-Language: en-US
```
### زبان‌های پشتیبانی شده:
- **فارسی (fa)**: پیشفرض
- **انگلیسی (en)**
### مثال درخواست با زبان فارسی:
```bash
curl -X GET "http://localhost:8000/api/v1/auth/me" \\
-H "Authorization: Bearer sk_1234567890abcdef" \\
-H "Accept-Language: fa" \\
-H "Accept: application/json"
```
## 📅 تقویم (Calendar)
API از تقویم شمسی (جلالی) پشتیبانی میکند:
### هدر تقویم:
```
X-Calendar-Type: jalali
X-Calendar-Type: gregorian
```
### انواع تقویم:
- **جلالی (jalali)**: تقویم شمسی - پیشفرض
- **میلادی (gregorian)**: تقویم میلادی
### مثال درخواست با تقویم شمسی:
```bash
curl -X GET "http://localhost:8000/api/v1/users" \\
-H "Authorization: Bearer sk_1234567890abcdef" \\
-H "X-Calendar-Type: jalali" \\
-H "Accept: application/json"
```
## 📊 فرمت پاسخ‌ها (Response Format)
تمام پاسخها در فرمت زیر هستند:
```json
{
"success": true,
"message": "پیام توضیحی",
"data": {
// دادههای اصلی
}
}
```
### کدهای خطا:
- **200**: موفقیت
- **400**: خطا در اعتبارسنجی دادهها
- **401**: احراز هویت نشده
- **403**: دسترسی غیرمجاز
- **404**: منبع یافت نشد
- **422**: خطا در اعتبارسنجی
- **500**: خطای سرور
## 🔒 امنیت (Security)
### کپچا:
برای عملیات حساس از کپچا استفاده میشود:
- دریافت کپچا: `POST /api/v1/auth/captcha`
- استفاده در ثبتنام، ورود، فراموشی رمز عبور
### رمزگذاری:
- رمزهای عبور با bcrypt رمزگذاری میشوند
- کلیدهای API با SHA-256 هش میشوند
## 📝 مثال کامل درخواست:
```bash
# 1. دریافت کپچا
curl -X POST "http://localhost:8000/api/v1/auth/captcha"
# 2. ورود
curl -X POST "http://localhost:8000/api/v1/auth/login" \\
-H "Content-Type: application/json" \\
-H "Accept-Language: fa" \\
-H "X-Calendar-Type: jalali" \\
-d '{
"identifier": "user@example.com",
"password": "password123",
"captcha_id": "captcha_id_from_step_1",
"captcha_code": "12345"
}'
# 3. استفاده از API با کلید دریافتی
curl -X GET "http://localhost:8000/api/v1/users" \\
-H "Authorization: Bearer sk_1234567890abcdef" \\
-H "Accept-Language: fa" \\
-H "X-Calendar-Type: jalali" \\
-H "Accept: application/json"
```
## 🚀 شروع سریع:
1. **ثبتنام**: `POST /api/v1/auth/register`
2. **ورود**: `POST /api/v1/auth/login`
3. **دریافت اطلاعات کاربر**: `GET /api/v1/auth/me`
4. **مدیریت کاربران**: `GET /api/v1/users` (نیاز به مجوز usermanager)
## 📞 پشتیبانی:
- **ایمیل**: support@hesabix.ir
- **مستندات**: `/docs` (Swagger UI)
- **ReDoc**: `/redoc`
""",
contact={
"name": "Hesabix Team",
"email": "support@hesabix.ir",
"url": "https://hesabix.ir",
},
license_info={
"name": "GNU GPLv3 License",
"url": "https://opensource.org/licenses/GPL-3.0",
},
servers=[
{
"url": "http://localhost:8000",
"description": "Development server"
},
{
"url": "https://agent.hesabix.ir",
"description": "Production server"
}
],
2025-09-15 13:53:54 +03:30
)
application.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_allowed_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
2025-09-18 10:44:23 +03:30
@application.middleware("http")
async def smart_number_normalizer(request: Request, call_next):
"""Middleware هوشمند برای تبدیل اعداد فارسی/عربی به انگلیسی"""
if SmartNormalizerConfig.ENABLED and request.method in ["POST", "PUT", "PATCH"]:
2025-09-29 19:19:24 +03:30
# فقط برای درخواست‌های JSON اعمال شود تا فایل‌های باینری/چندبخشی خراب نشوند
content_type = request.headers.get("Content-Type", "").lower()
if content_type.startswith("application/json"):
# خواندن body درخواست
body = await request.body()
if body:
# تبدیل اعداد در JSON
normalized_body = smart_normalize_json(body)
if normalized_body != body:
# ایجاد request جدید با body تبدیل شده
request._body = normalized_body
2025-09-18 10:44:23 +03:30
response = await call_next(request)
return response
@application.middleware("http")
async def add_locale(request: Request, call_next):
lang = negotiate_locale(request.headers.get("Accept-Language"))
request.state.locale = lang
request.state.translator = Translator(lang)
response = await call_next(request)
return response
2025-09-18 10:44:23 +03:30
@application.middleware("http")
async def add_calendar_middleware(request: Request, call_next):
return await add_calendar_type(request, call_next)
2025-09-15 13:53:54 +03:30
application.include_router(health_router, prefix=settings.api_v1_prefix)
application.include_router(auth_router, prefix=settings.api_v1_prefix)
application.include_router(users_router, prefix=settings.api_v1_prefix)
2025-09-20 01:17:27 +03:30
application.include_router(businesses_router, prefix=settings.api_v1_prefix)
2025-09-28 23:06:53 +03:30
application.include_router(currencies_router, prefix=settings.api_v1_prefix)
application.include_router(business_dashboard_router, prefix=settings.api_v1_prefix)
2025-09-25 01:01:27 +03:30
application.include_router(business_users_router, prefix=settings.api_v1_prefix)
2025-09-27 21:19:00 +03:30
application.include_router(accounts_router, prefix=settings.api_v1_prefix)
2025-09-30 17:12:53 +03:30
application.include_router(categories_router, prefix=settings.api_v1_prefix)
application.include_router(product_attributes_router, prefix=settings.api_v1_prefix)
application.include_router(products_router, prefix=settings.api_v1_prefix)
application.include_router(price_lists_router, prefix=settings.api_v1_prefix)
2025-10-11 02:13:18 +03:30
application.include_router(invoices_router, prefix=settings.api_v1_prefix)
2025-09-25 22:36:08 +03:30
application.include_router(persons_router, prefix=settings.api_v1_prefix)
2025-10-05 02:33:08 +03:30
application.include_router(customers_router, prefix=settings.api_v1_prefix)
2025-10-03 02:25:35 +03:30
application.include_router(bank_accounts_router, prefix=settings.api_v1_prefix)
2025-10-14 23:16:28 +03:30
from adapters.api.v1.checks import router as checks_router
application.include_router(checks_router, prefix=settings.api_v1_prefix)
2025-10-03 17:02:07 +03:30
application.include_router(cash_registers_router, prefix=settings.api_v1_prefix)
2025-10-04 17:17:53 +03:30
application.include_router(petty_cash_router, prefix=settings.api_v1_prefix)
2025-09-30 17:12:53 +03:30
application.include_router(tax_units_router, prefix=settings.api_v1_prefix)
application.include_router(tax_types_router, prefix=settings.api_v1_prefix)
2025-10-14 23:16:28 +03:30
application.include_router(receipts_payments_router, prefix=settings.api_v1_prefix)
2025-10-16 13:02:03 +03:30
application.include_router(fiscal_years_router, prefix=settings.api_v1_prefix)
2025-09-20 22:46:06 +03:30
# Support endpoints
application.include_router(support_tickets_router, prefix=f"{settings.api_v1_prefix}/support")
application.include_router(support_operator_router, prefix=f"{settings.api_v1_prefix}/support/operator")
application.include_router(support_categories_router, prefix=f"{settings.api_v1_prefix}/metadata/categories")
application.include_router(support_priorities_router, prefix=f"{settings.api_v1_prefix}/metadata/priorities")
application.include_router(support_statuses_router, prefix=f"{settings.api_v1_prefix}/metadata/statuses")
2025-09-21 19:53:21 +03:30
# Admin endpoints
application.include_router(admin_file_storage_router, prefix=settings.api_v1_prefix)
application.include_router(admin_email_config_router, prefix=settings.api_v1_prefix)
register_error_handlers(application)
2025-09-15 13:53:54 +03:30
2025-09-20 01:17:27 +03:30
@application.get("/",
summary="اطلاعات سرویس",
description="دریافت اطلاعات کلی سرویس و نسخه",
tags=["general"]
)
2025-09-15 13:53:54 +03:30
def read_root() -> dict[str, str]:
return {"service": settings.app_name, "version": settings.app_version}
2025-09-25 01:01:27 +03:30
2025-09-20 01:17:27 +03:30
# اضافه کردن security schemes
from fastapi.openapi.utils import get_openapi
def custom_openapi():
if application.openapi_schema:
return application.openapi_schema
openapi_schema = get_openapi(
title=application.title,
version=application.version,
description=application.description,
routes=application.routes,
)
# اضافه کردن security schemes
openapi_schema["components"]["securitySchemes"] = {
2025-09-20 22:46:06 +03:30
"ApiKeyAuth": {
2025-10-03 02:25:35 +03:30
"type": "apiKey",
"in": "header",
"name": "Authorization",
2025-09-20 22:46:06 +03:30
"description": "کلید API برای احراز هویت. فرمت: ApiKey sk_your_api_key_here"
2025-09-20 01:17:27 +03:30
}
}
# اضافه کردن security به endpoint های محافظت شده
for path, methods in openapi_schema["paths"].items():
for method, details in methods.items():
if method in ["get", "post", "put", "delete", "patch"]:
2025-10-03 02:25:35 +03:30
# تمام endpoint های auth، users، support و bank-accounts نیاز به احراز هویت دارند
if "/auth/" in path or "/users" in path or "/support" in path or "/bank-accounts" in path:
2025-09-20 22:46:06 +03:30
details["security"] = [{"ApiKeyAuth": []}]
2025-09-20 01:17:27 +03:30
application.openapi_schema = openapi_schema
return application.openapi_schema
application.openapi = custom_openapi
2025-09-15 13:53:54 +03:30
return application
app = create_app()