2025-09-15 21:50:09 +03:30
|
|
|
|
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
|
2025-09-15 21:50:09 +03:30
|
|
|
|
from adapters.api.v1.auth import router as auth_router
|
2025-09-19 04:35:13 +03:30
|
|
|
|
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-15 21:50:09 +03:30
|
|
|
|
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
|
|
|
|
)
|
|
|
|
|
|
|
2025-09-15 21:50:09 +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"]:
|
|
|
|
|
|
# خواندن body درخواست
|
|
|
|
|
|
body = await request.body()
|
|
|
|
|
|
|
|
|
|
|
|
if body:
|
|
|
|
|
|
# تبدیل اعداد در JSON
|
|
|
|
|
|
normalized_body = smart_normalize_json(body)
|
|
|
|
|
|
if normalized_body != body:
|
|
|
|
|
|
# ایجاد request جدید با body تبدیل شده
|
|
|
|
|
|
request._body = normalized_body
|
|
|
|
|
|
|
|
|
|
|
|
response = await call_next(request)
|
|
|
|
|
|
return response
|
|
|
|
|
|
|
2025-09-15 21:50:09 +03:30
|
|
|
|
@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)
|
2025-09-15 21:50:09 +03:30
|
|
|
|
application.include_router(auth_router, prefix=settings.api_v1_prefix)
|
2025-09-19 04:35:13 +03:30
|
|
|
|
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-15 21:50:09 +03:30
|
|
|
|
|
|
|
|
|
|
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-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"] = {
|
|
|
|
|
|
"BearerAuth": {
|
|
|
|
|
|
"type": "http",
|
|
|
|
|
|
"scheme": "bearer",
|
|
|
|
|
|
"bearerFormat": "API Key",
|
|
|
|
|
|
"description": "کلید API برای احراز هویت. فرمت: Bearer sk_your_api_key_here"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# اضافه کردن security به endpoint های محافظت شده
|
|
|
|
|
|
for path, methods in openapi_schema["paths"].items():
|
|
|
|
|
|
for method, details in methods.items():
|
|
|
|
|
|
if method in ["get", "post", "put", "delete", "patch"]:
|
|
|
|
|
|
# تمام endpoint های auth و users نیاز به احراز هویت دارند
|
|
|
|
|
|
if "/auth/" in path or "/users" in path:
|
|
|
|
|
|
details["security"] = [{"BearerAuth": []}]
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
|
|
|
|
|
|
|