Обновлены модели аккаунтов для хранения имени, фамилии и телефона. Добавлены функции для получения и обновления профиля аккаунта, а также изменения пароля. Улучшена валидация данных с использованием Pydantic.
This commit is contained in:
parent
fa0161710e
commit
161e0b3ec4
@ -95,10 +95,15 @@ def fill_db():
|
|||||||
# 1. Accounts
|
# 1. Accounts
|
||||||
accounts = []
|
accounts = []
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
|
name_parts = NAMES[i % len(NAMES)].split()
|
||||||
|
first_name = name_parts[0]
|
||||||
|
surname = name_parts[1] if len(name_parts) > 1 else 'Тестов'
|
||||||
acc = Account(
|
acc = Account(
|
||||||
login=f"user{i+1}",
|
login=f"user{i+1}",
|
||||||
password_hash=get_password_hash("password123"),
|
password_hash=get_password_hash("password123"),
|
||||||
name=NAMES[i % len(NAMES)],
|
firstName=first_name,
|
||||||
|
surname=surname,
|
||||||
|
phone=PHONES[i % len(PHONES)],
|
||||||
email=f"user{i+1}@example.com",
|
email=f"user{i+1}@example.com",
|
||||||
company_id=company.id
|
company_id=company.id
|
||||||
)
|
)
|
||||||
|
|||||||
112
main.py
112
main.py
@ -10,6 +10,8 @@ from fastapi.responses import JSONResponse
|
|||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
import jwt
|
import jwt
|
||||||
|
from jwt.exceptions import InvalidTokenError
|
||||||
|
from pydantic import BaseModel, EmailStr
|
||||||
|
|
||||||
# Конфигурация
|
# Конфигурация
|
||||||
AUTH_DATABASE_ADDRESS = "sqlite:///partner.db"
|
AUTH_DATABASE_ADDRESS = "sqlite:///partner.db"
|
||||||
@ -67,12 +69,24 @@ class Account(SQLModel, table=True):
|
|||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
login: str = Field(index=True, unique=True)
|
login: str = Field(index=True, unique=True)
|
||||||
password_hash: str # теперь хранится hash пароля
|
password_hash: str # теперь хранится hash пароля
|
||||||
name: Optional[str] = None
|
firstName: Optional[str] = None
|
||||||
|
surname: Optional[str] = None
|
||||||
|
phone: Optional[str] = None
|
||||||
email: Optional[str] = None
|
email: Optional[str] = None
|
||||||
company_id: int = Field(foreign_key="company.id")
|
company_id: int = Field(foreign_key="company.id")
|
||||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
|
||||||
|
class AccountProfileUpdateRequest(BaseModel):
|
||||||
|
firstName: str
|
||||||
|
surname: str
|
||||||
|
email: EmailStr
|
||||||
|
phone: str
|
||||||
|
|
||||||
|
class AccountPasswordChangeRequest(BaseModel):
|
||||||
|
currentPassword: str
|
||||||
|
newPassword: str
|
||||||
|
|
||||||
# Создание движка базы данных
|
# Создание движка базы данных
|
||||||
AUTH_DB_ENGINE = create_engine(AUTH_DATABASE_ADDRESS, echo=True)
|
AUTH_DB_ENGINE = create_engine(AUTH_DATABASE_ADDRESS, echo=True)
|
||||||
SQLModel.metadata.create_all(AUTH_DB_ENGINE)
|
SQLModel.metadata.create_all(AUTH_DB_ENGINE)
|
||||||
@ -498,18 +512,7 @@ def get_billing_chart_pie(db: Session = Depends(get_db)):
|
|||||||
]
|
]
|
||||||
return JSONResponse(content=data)
|
return JSONResponse(content=data)
|
||||||
|
|
||||||
@app.get("/account", tags=["bff"])
|
|
||||||
def get_account(db: Session = Depends(get_db)):
|
|
||||||
account = db.exec(select(Account)).first()
|
|
||||||
if not account:
|
|
||||||
raise HTTPException(status_code=404, detail="Account not found")
|
|
||||||
return {
|
|
||||||
"id": account.id,
|
|
||||||
"login": account.login,
|
|
||||||
"name": account.name,
|
|
||||||
"email": account.email,
|
|
||||||
"balance": account.balance
|
|
||||||
}
|
|
||||||
|
|
||||||
@app.post("/tg_auth", tags=["partner-tg"])
|
@app.post("/tg_auth", tags=["partner-tg"])
|
||||||
def tg_auth(hash: str = Body(..., embed=True), db: Session = Depends(get_db)):
|
def tg_auth(hash: str = Body(..., embed=True), db: Session = Depends(get_db)):
|
||||||
@ -527,3 +530,86 @@ def verify_password(plain_password, hashed_password):
|
|||||||
def get_account_by_login(db: Session, login: str) -> Optional[Account]:
|
def get_account_by_login(db: Session, login: str) -> Optional[Account]:
|
||||||
statement = select(Account).where(Account.login == login)
|
statement = select(Account).where(Account.login == login)
|
||||||
return db.exec(statement).first()
|
return db.exec(statement).first()
|
||||||
|
|
||||||
|
def get_current_account(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
|
||||||
|
credentials_exception = HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Could not validate credentials",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
|
login: str = payload.get("sub")
|
||||||
|
if login is None:
|
||||||
|
raise credentials_exception
|
||||||
|
except InvalidTokenError:
|
||||||
|
raise credentials_exception
|
||||||
|
account = get_account_by_login(db, login)
|
||||||
|
if account is None:
|
||||||
|
raise credentials_exception
|
||||||
|
return account
|
||||||
|
|
||||||
|
@app.get("/account", tags=["bff"])
|
||||||
|
def get_account(current_account: Account = Depends(get_current_account)):
|
||||||
|
return {
|
||||||
|
"firstName": current_account.firstName,
|
||||||
|
"surname": current_account.surname
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.get("/account/profile", tags=["bff"])
|
||||||
|
def get_account_profile(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
|
company = db.exec(select(Company).where(Company.id == current_account.company_id)).first()
|
||||||
|
if not company:
|
||||||
|
raise HTTPException(status_code=404, detail="Компания не найдена")
|
||||||
|
return {
|
||||||
|
"firstName": current_account.firstName,
|
||||||
|
"surname": current_account.surname,
|
||||||
|
"phone": current_account.phone,
|
||||||
|
"email": current_account.email,
|
||||||
|
"create_dttm": current_account.create_dttm,
|
||||||
|
"company": {
|
||||||
|
"name": company.name,
|
||||||
|
"key": company.key,
|
||||||
|
"commission": company.commission
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.post("/account/profile", tags=["bff"])
|
||||||
|
def update_account_profile(
|
||||||
|
req: AccountProfileUpdateRequest,
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
# Проверка, что все поля заполнены (Pydantic уже валидирует email и обязательность)
|
||||||
|
if not req.firstName.strip() or not req.surname.strip() or not req.email or not req.phone.strip():
|
||||||
|
raise HTTPException(status_code=400, detail="Все поля должны быть заполнены")
|
||||||
|
# Обновляем поля
|
||||||
|
current_account.firstName = req.firstName.strip()
|
||||||
|
current_account.surname = req.surname.strip()
|
||||||
|
current_account.email = req.email
|
||||||
|
current_account.phone = req.phone.strip()
|
||||||
|
db.add(current_account)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(current_account)
|
||||||
|
return {"msg": "Профиль обновлён успешно"}
|
||||||
|
|
||||||
|
@app.post("/account/password", tags=["bff"])
|
||||||
|
def change_account_password(
|
||||||
|
req: AccountPasswordChangeRequest,
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
# Проверяем текущий пароль
|
||||||
|
if not verify_password(req.currentPassword, current_account.password_hash):
|
||||||
|
raise HTTPException(status_code=400, detail="Текущий пароль неверный")
|
||||||
|
# Проверяем, что новый пароль не пустой и отличается от текущего
|
||||||
|
if not req.newPassword.strip():
|
||||||
|
raise HTTPException(status_code=400, detail="Новый пароль не может быть пустым")
|
||||||
|
if verify_password(req.newPassword, current_account.password_hash):
|
||||||
|
raise HTTPException(status_code=400, detail="Новый пароль не должен совпадать с текущим")
|
||||||
|
# Хешируем и сохраняем новый пароль
|
||||||
|
current_account.password_hash = pwd_context.hash(req.newPassword)
|
||||||
|
db.add(current_account)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(current_account)
|
||||||
|
return {"msg": "Пароль успешно изменён"}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user