From 57188186c003a50608f8bbca11f34c87ff24669b Mon Sep 17 00:00:00 2001 From: Redsandyg Date: Mon, 9 Jun 2025 12:52:49 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=20integration=5Fapi.py=20=D0=B4=D0=BB=D1=8F=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=BE=D0=BD?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20API,=20=D1=80=D0=B5=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20=D1=82=D0=BE=D0=BA=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2,=20=D0=B0=20=D1=82=D0=B0=D0=BA=D0=B6=D0=B5?= =?UTF-8?q?=20=D1=8D=D0=BD=D0=B4=D0=BF=D0=BE=D0=B8=D0=BD=D1=82=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BE=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0=D0=B6=D0=B0=D1=85.=20=D0=9E=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BC=D0=BE=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B8=20=D0=B8=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20=D0=B4=D0=B0?= =?UTF-8?q?=D1=82=D0=B0=D0=BC=D0=B8=20=D0=B2=20fill=5Fdb.py=20=D0=B8=20mai?= =?UTF-8?q?n.py=20=D0=B4=D0=BB=D1=8F=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8F=20sale=5Fdate=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=20create=5Fdttm.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fill_db.py | 1 + integration_api.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++ main.py | 10 +++---- sql_models.py | 1 + 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 integration_api.py diff --git a/fill_db.py b/fill_db.py index 5e70beb..7669157 100644 --- a/fill_db.py +++ b/fill_db.py @@ -165,6 +165,7 @@ def fill_db(): ref=ref.id, sale_id=str(uuid4()), company_id=company.id, + sale_date=dt, create_dttm=dt, update_dttm=dt ) diff --git a/integration_api.py b/integration_api.py new file mode 100644 index 0000000..dd9f7be --- /dev/null +++ b/integration_api.py @@ -0,0 +1,75 @@ +from fastapi import FastAPI, HTTPException, status, Depends, Request +from sqlmodel import Session, select +from typing import Optional +from datetime import datetime, timedelta +import jwt +from jwt.exceptions import InvalidTokenError +from pydantic import BaseModel + +from sql_models import Sale +from helpers_bff import get_db, AUTH_DB_ENGINE # Assuming these are the correct imports + +app = FastAPI() + +# Конфигурация для интеграционного API токена +INTEGRATION_SECRET_KEY = "your-integration-super-secret-key" # Смените это на безопасный ключ! +INTEGRATION_ALGORITHM = "HS256" +INTEGRATION_TOKEN_EXPIRE_MINUTES = 60 * 24 # 24 часа + +class IntegrationTokenData(BaseModel): + client_id: str + +class SaleCreate(BaseModel): + cost: float + crediting: float + ref_id: int + sale_id: str + company_id: int + sale_date: datetime = datetime.utcnow() + +def create_integration_access_token(data: dict, expires_delta: timedelta = None): + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=INTEGRATION_TOKEN_EXPIRE_MINUTES) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, INTEGRATION_SECRET_KEY, algorithm=INTEGRATION_ALGORITHM) + return encoded_jwt + +async def verify_integration_token(request: Request): + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate integration credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + auth_header = request.headers.get("Authorization") + if not auth_header or not auth_header.startswith("Bearer "): + raise credentials_exception + token = auth_header.replace("Bearer ", "").strip() + try: + payload = jwt.decode(token, INTEGRATION_SECRET_KEY, algorithms=[INTEGRATION_ALGORITHM]) + client_id: str = payload.get("client_id") + if client_id is None: + raise credentials_exception + # Здесь вы можете добавить логику для проверки client_id, например, из базы данных + except InvalidTokenError: + raise credentials_exception + return True # Токен действителен + +@app.post("/sale", status_code=status.HTTP_201_CREATED) +async def upload_sale(sale_data: SaleCreate, db: Session = Depends(get_db), verified: bool = Depends(verify_integration_token)): + if not verified: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authorized") + + db_sale = Sale(cost=sale_data.cost, crediting=sale_data.crediting, ref=sale_data.ref_id, sale_id=sale_data.sale_id, company_id=sale_data.company_id, sale_date=sale_data.sale_date) + db.add(db_sale) + db.commit() + db.refresh(db_sale) + return {"message": "Sale uploaded successfully", "sale_id": db_sale.id} + +@app.get("/generate-integration-token") +async def generate_token_endpoint(client_id: str): + token_data = {"client_id": client_id} + token = create_integration_access_token(token_data) + return {"access_token": token, "token_type": "bearer"} \ No newline at end of file diff --git a/main.py b/main.py index f33dc48..9818a57 100644 --- a/main.py +++ b/main.py @@ -236,11 +236,11 @@ def get_dashboard_chart_total(current_account: Account = Depends(get_current_acc # Группируем продажи по дате (день) result = db.exec( select( - func.strftime('%Y-%m-%d', Sale.create_dttm).label('date'), + func.strftime('%Y-%m-%d', Sale.sale_date).label('date'), func.sum(Sale.cost).label('revenue'), func.count(Sale.id).label('sales') - ).where(Sale.company_id == current_account.company_id).group_by(func.strftime('%Y-%m-%d', Sale.create_dttm)) - .order_by(func.strftime('%Y-%m-%d', Sale.create_dttm)) + ).where(Sale.company_id == current_account.company_id).group_by(func.strftime('%Y-%m-%d', Sale.sale_date)) + .order_by(func.strftime('%Y-%m-%d', Sale.sale_date)) ).all() # Преобразуем результат в нужный формат data = [ @@ -368,9 +368,9 @@ def get_sales_stat( """ sales_query = select(Sale).where(Sale.company_id == current_account.company_id) if date_start: - sales_query = sales_query.where(Sale.create_dttm >= date_start) + sales_query = sales_query.where(Sale.sale_date >= date_start) if date_end: - sales_query = sales_query.where(Sale.create_dttm <= date_end) + sales_query = sales_query.where(Sale.sale_date <= date_end) sales = db.exec(sales_query).all() ref_ids = list(set(sale.ref for sale in sales)) refs = db.exec(select(Ref).where(Ref.id.in_(ref_ids))).all() if ref_ids else [] diff --git a/sql_models.py b/sql_models.py index f9da77a..e4ba7e6 100644 --- a/sql_models.py +++ b/sql_models.py @@ -39,6 +39,7 @@ class Sale(SQLModel, table=True): ref: int = Field(foreign_key="ref.id") sale_id: str company_id: int = Field(foreign_key="company.id") + sale_dttm: datetime = Field(default_factory=datetime.utcnow) create_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow)