mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Fix database
This commit is contained in:
@@ -1,25 +1,55 @@
|
||||
"""Application configuration"""
|
||||
from functools import lru_cache
|
||||
import os
|
||||
from pydantic_settings import BaseSettings
|
||||
from functools import lru_cache
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Application settings"""
|
||||
PROJECT_NAME: str = "Employee Request System"
|
||||
|
||||
# Database
|
||||
DATABASE_URL: str = "postgresql://postgres:postgres@localhost:5432/employee_requests"
|
||||
|
||||
# База данных
|
||||
POSTGRES_USER: str = "postgres"
|
||||
POSTGRES_PASSWORD: str = "postgres"
|
||||
POSTGRES_HOST: str = "postgres"
|
||||
POSTGRES_PORT: str = "5432"
|
||||
POSTGRES_DB: str = "app"
|
||||
POSTGRES_TEST_DB: str = "test_app"
|
||||
DATABASE_URL: str | None = None
|
||||
|
||||
# JWT
|
||||
SECRET_KEY: str = "your-secret-key"
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||
|
||||
|
||||
# Режим тестирования
|
||||
TESTING: bool = bool(os.getenv("TESTING"))
|
||||
|
||||
# Redis
|
||||
REDIS_HOST: str = "localhost"
|
||||
REDIS_HOST: str = "redis"
|
||||
REDIS_PORT: int = 6379
|
||||
|
||||
REDIS_DB: int = 0
|
||||
REDIS_TEST_DB: int = 1
|
||||
|
||||
def get_database_url(self) -> str:
|
||||
"""Get database URL"""
|
||||
if self.DATABASE_URL:
|
||||
return self.DATABASE_URL
|
||||
|
||||
if self.TESTING:
|
||||
return f"postgresql://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@localhost:5432/{self.POSTGRES_TEST_DB}"
|
||||
return f"postgresql://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
|
||||
|
||||
def get_redis_url(self) -> str:
|
||||
"""Get Redis URL"""
|
||||
db = self.REDIS_TEST_DB if self.TESTING else self.REDIS_DB
|
||||
host = "localhost" if self.TESTING else self.REDIS_HOST
|
||||
return f"redis://{host}:{self.REDIS_PORT}/{db}"
|
||||
|
||||
# Telegram
|
||||
TELEGRAM_BOT_TOKEN: str = os.getenv("TELEGRAM_BOT_TOKEN", "")
|
||||
TELEGRAM_CHAT_ID: str = os.getenv("TELEGRAM_CHAT_ID", "")
|
||||
|
||||
class Config:
|
||||
"""Pydantic config"""
|
||||
env_file = ".env"
|
||||
case_sensitive = True
|
||||
|
||||
@lru_cache()
|
||||
|
||||
@@ -6,7 +6,12 @@ class TestSettings(BaseSettings):
|
||||
PROJECT_NAME: str = "Employee Request System Test"
|
||||
|
||||
# Database
|
||||
DATABASE_URL: str = "sqlite:///:memory:"
|
||||
POSTGRES_USER: str = "postgres"
|
||||
POSTGRES_PASSWORD: str = "postgres"
|
||||
POSTGRES_HOST: str = "localhost"
|
||||
POSTGRES_PORT: str = "5432"
|
||||
POSTGRES_DB: str = "test_app"
|
||||
DATABASE_URL: str = "postgresql://postgres:postgres@localhost:5432/test_app"
|
||||
|
||||
# JWT
|
||||
SECRET_KEY: str = "test_secret_key"
|
||||
@@ -16,9 +21,14 @@ class TestSettings(BaseSettings):
|
||||
# Redis
|
||||
REDIS_HOST: str = "localhost"
|
||||
REDIS_PORT: int = 6379
|
||||
REDIS_DB: int = 1
|
||||
|
||||
# Testing
|
||||
TESTING: bool = True
|
||||
|
||||
class Config:
|
||||
"""Pydantic config"""
|
||||
case_sensitive = True
|
||||
env_file = ".env.test"
|
||||
|
||||
test_settings = TestSettings()
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Employee CRUD operations"""
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional, List
|
||||
from typing import List, Optional
|
||||
from ..models.employee import Employee
|
||||
from ..schemas.employee import EmployeeCreate, EmployeeUpdate
|
||||
from ..utils.loggers import auth_logger
|
||||
@@ -13,57 +13,42 @@ def get_employee(db: Session, employee_id: int) -> Optional[Employee]:
|
||||
"""Get employee by ID"""
|
||||
return db.query(Employee).filter(Employee.id == employee_id).first()
|
||||
|
||||
def get_employee_by_last_name(db: Session, last_name: str) -> Optional[Employee]:
|
||||
"""Get employee by last name"""
|
||||
return db.query(Employee).filter(Employee.last_name == last_name).first()
|
||||
def get_employee_by_credentials(db: Session, first_name: str, last_name: str) -> Optional[Employee]:
|
||||
"""Get employee by first name and last name"""
|
||||
return db.query(Employee).filter(
|
||||
Employee.first_name == first_name,
|
||||
Employee.last_name == last_name
|
||||
).first()
|
||||
|
||||
def create_employee(db: Session, employee: EmployeeCreate, hashed_password: str) -> Employee:
|
||||
"""Create new employee"""
|
||||
try:
|
||||
db_employee = Employee(
|
||||
first_name=employee.first_name,
|
||||
last_name=employee.last_name,
|
||||
department=employee.department,
|
||||
office=employee.office,
|
||||
hashed_password=hashed_password
|
||||
)
|
||||
db.add(db_employee)
|
||||
db.commit()
|
||||
db.refresh(db_employee)
|
||||
return db_employee
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Error creating employee: {e}")
|
||||
db.rollback()
|
||||
raise
|
||||
db_employee = Employee(
|
||||
first_name=employee.first_name,
|
||||
last_name=employee.last_name,
|
||||
department=employee.department,
|
||||
office=employee.office,
|
||||
hashed_password=hashed_password,
|
||||
is_admin=employee.is_admin
|
||||
)
|
||||
db.add(db_employee)
|
||||
db.commit()
|
||||
db.refresh(db_employee)
|
||||
return db_employee
|
||||
|
||||
def update_employee(db: Session, employee_id: int, employee: EmployeeUpdate) -> Optional[Employee]:
|
||||
"""Update employee"""
|
||||
"""Update employee data"""
|
||||
db_employee = get_employee(db, employee_id)
|
||||
if not db_employee:
|
||||
return None
|
||||
|
||||
for field, value in employee.model_dump(exclude_unset=True).items():
|
||||
setattr(db_employee, field, value)
|
||||
|
||||
try:
|
||||
if db_employee:
|
||||
for key, value in employee.dict(exclude_unset=True).items():
|
||||
setattr(db_employee, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_employee)
|
||||
return db_employee
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Error updating employee: {e}")
|
||||
db.rollback()
|
||||
raise
|
||||
return db_employee
|
||||
|
||||
def delete_employee(db: Session, employee_id: int) -> Optional[Employee]:
|
||||
"""Delete employee"""
|
||||
db_employee = get_employee(db, employee_id)
|
||||
if db_employee:
|
||||
try:
|
||||
db.delete(db_employee)
|
||||
db.commit()
|
||||
return db_employee
|
||||
except Exception as e:
|
||||
auth_logger.error(f"Error deleting employee: {e}")
|
||||
db.rollback()
|
||||
raise
|
||||
return None
|
||||
db.delete(db_employee)
|
||||
db.commit()
|
||||
return db_employee
|
||||
@@ -3,9 +3,9 @@ from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
from ..models.token import Token
|
||||
|
||||
def create_token(db: Session, token: str, user_id: int) -> Token:
|
||||
def create_token(db: Session, token: str, employee_id: int) -> Token:
|
||||
"""Create new token"""
|
||||
db_token = Token(token=token, user_id=user_id)
|
||||
db_token = Token(token=token, employee_id=employee_id)
|
||||
db.add(db_token)
|
||||
db.commit()
|
||||
db.refresh(db_token)
|
||||
@@ -24,8 +24,8 @@ def delete_token(db: Session, token: str) -> bool:
|
||||
return True
|
||||
return False
|
||||
|
||||
def delete_user_tokens(db: Session, user_id: int) -> bool:
|
||||
"""Delete all tokens for a user"""
|
||||
db.query(Token).filter(Token.user_id == user_id).delete()
|
||||
def delete_employee_tokens(db: Session, employee_id: int) -> bool:
|
||||
"""Delete all tokens for an employee"""
|
||||
db.query(Token).filter(Token.employee_id == employee_id).delete()
|
||||
db.commit()
|
||||
return True
|
||||
@@ -1,25 +1,24 @@
|
||||
"""Database module"""
|
||||
"""Database configuration"""
|
||||
import os
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from .core.config import settings
|
||||
# Определяем URL базы данных в зависимости от окружения
|
||||
if os.getenv("TESTING"):
|
||||
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URL,
|
||||
connect_args={"check_same_thread": False}
|
||||
)
|
||||
else:
|
||||
SQLALCHEMY_DATABASE_URL = "postgresql://postgres:postgres@postgres:5432/app"
|
||||
engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
||||
|
||||
# Определяем, используем ли тестовую базу данных
|
||||
TESTING = os.getenv("TESTING", "False") == "True"
|
||||
DATABASE_URL = "sqlite:///:memory:" if TESTING else settings.DATABASE_URL
|
||||
|
||||
# Создаем базовый класс для моделей
|
||||
Base = declarative_base()
|
||||
|
||||
# Создаем движок базы данных
|
||||
connect_args = {"check_same_thread": False} if TESTING else {}
|
||||
engine = create_engine(DATABASE_URL, connect_args=connect_args)
|
||||
|
||||
# Создаем фабрику сессий
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
def get_db():
|
||||
"""Get database session"""
|
||||
db = SessionLocal()
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
"""Database initialization script"""
|
||||
"""Database initialization"""
|
||||
from sqlalchemy.orm import Session
|
||||
from app.core.config import settings
|
||||
from app.models.employee import Employee
|
||||
from app.utils.auth import get_password_hash
|
||||
from ..models.employee import Employee
|
||||
from ..utils.auth import get_password_hash
|
||||
|
||||
def init_db(db: Session) -> None:
|
||||
"""Initialize database with default data"""
|
||||
# Создаем тестового сотрудника
|
||||
test_employee = db.query(Employee).filter(Employee.last_name == "User").first()
|
||||
if not test_employee:
|
||||
test_employee = Employee(
|
||||
first_name="Test",
|
||||
# Проверяем, есть ли уже админ в базе
|
||||
admin = db.query(Employee).filter(Employee.is_admin == True).first()
|
||||
if not admin:
|
||||
# Создаем админа по умолчанию
|
||||
admin = Employee(
|
||||
first_name="Admin",
|
||||
last_name="User",
|
||||
department="IT",
|
||||
office="101",
|
||||
hashed_password=get_password_hash("testpass123")
|
||||
office="102",
|
||||
hashed_password=get_password_hash("adminpass123"),
|
||||
is_admin=True
|
||||
)
|
||||
db.add(test_employee)
|
||||
db.add(admin)
|
||||
db.commit()
|
||||
db.refresh(test_employee)
|
||||
db.refresh(admin)
|
||||
63
backend/app/dependencies.py
Normal file
63
backend/app/dependencies.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Dependencies module"""
|
||||
from typing import Generator, Any
|
||||
from sqlalchemy.orm import Session
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
from .database import SessionLocal
|
||||
from .core.config import settings
|
||||
from .utils.jwt import verify_token
|
||||
from .models.employee import Employee
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth/login")
|
||||
|
||||
def get_db() -> Generator[Session, Any, None]:
|
||||
"""Get database session"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
async def get_current_employee(
|
||||
db: Session = Depends(get_db),
|
||||
token: str = Depends(oauth2_scheme)
|
||||
) -> Employee:
|
||||
"""Get current employee"""
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
employee_id = verify_token(token)
|
||||
if not employee_id:
|
||||
raise credentials_exception
|
||||
|
||||
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
||||
if not employee:
|
||||
raise credentials_exception
|
||||
|
||||
return employee
|
||||
|
||||
async def get_current_active_employee(
|
||||
current_employee: Employee = Depends(get_current_employee),
|
||||
) -> Employee:
|
||||
"""Get current active employee"""
|
||||
if not current_employee.is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Inactive employee"
|
||||
)
|
||||
return current_employee
|
||||
|
||||
async def get_current_admin(
|
||||
current_employee: Employee = Depends(get_current_employee),
|
||||
) -> Employee:
|
||||
"""Get current admin"""
|
||||
if not current_employee.is_admin:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="The employee doesn't have enough privileges"
|
||||
)
|
||||
return current_employee
|
||||
@@ -1,62 +1,44 @@
|
||||
"""Main application module"""
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic_settings import BaseSettings
|
||||
import logging
|
||||
|
||||
from .models.base import Base
|
||||
from .database import engine, SessionLocal
|
||||
from .routers import admin, employees, requests, auth, statistics
|
||||
from .routers import auth, employees, requests, admin
|
||||
from .database import engine, Base
|
||||
from .db.init_db import init_db
|
||||
from .core.config import settings
|
||||
from .database import get_db
|
||||
|
||||
def get_application(app_settings: BaseSettings = settings) -> FastAPI:
|
||||
"""Создание экземпляра приложения с заданными настройками."""
|
||||
# Создаем таблицы
|
||||
Base.metadata.create_all(bind=engine)
|
||||
# Настройка логирования
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Инициализируем базу данных
|
||||
db = SessionLocal()
|
||||
# Создаем таблицы
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
# Создаем приложение
|
||||
app = FastAPI(title="Employee Request System API")
|
||||
|
||||
# Настраиваем CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Подключаем роутеры
|
||||
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
|
||||
app.include_router(employees.router, prefix="/api/employees", tags=["employees"])
|
||||
app.include_router(requests.router, prefix="/api/requests", tags=["requests"])
|
||||
app.include_router(admin.router, prefix="/api/admin", tags=["admin"])
|
||||
|
||||
# Инициализируем базу данных
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
"""Initialize database on startup"""
|
||||
db = next(get_db())
|
||||
try:
|
||||
init_db(db)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
app = FastAPI(
|
||||
# Включаем автоматическое перенаправление со слэшем
|
||||
redirect_slashes=True,
|
||||
# Добавляем описание API
|
||||
title="Support System API",
|
||||
description="API для системы поддержки",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
# CORS configuration
|
||||
origins = [
|
||||
"http://localhost",
|
||||
"http://localhost:8080",
|
||||
"http://localhost:5173",
|
||||
"http://127.0.0.1:5173",
|
||||
"http://127.0.0.1:8080",
|
||||
"http://185.139.70.62", # Добавляем ваш production домен
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
expose_headers=["*"]
|
||||
)
|
||||
|
||||
# Include routers
|
||||
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
|
||||
app.include_router(employees.router, prefix="/api/employees", tags=["employees"])
|
||||
app.include_router(requests.router, prefix="/api/requests", tags=["requests"])
|
||||
app.include_router(admin.router, prefix="/api/admin", tags=["admin"])
|
||||
app.include_router(statistics.router, prefix="/api/statistics", tags=["statistics"])
|
||||
|
||||
return app
|
||||
|
||||
app = get_application()
|
||||
db.close()
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Token model"""
|
||||
from sqlalchemy import Column, Integer, String, DateTime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
|
||||
from ..database import Base
|
||||
@@ -10,5 +11,7 @@ class Token(Base):
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
token = Column(String, unique=True, index=True)
|
||||
employee_id = Column(Integer)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
employee_id = Column(Integer, ForeignKey("employees.id"), nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
employee = relationship("Employee", backref="tokens")
|
||||
@@ -6,7 +6,7 @@ from typing import Optional
|
||||
|
||||
from ..database import get_db
|
||||
from ..crud import employees
|
||||
from ..schemas.auth import Token
|
||||
from ..schemas.auth import Token, LoginCredentials
|
||||
from ..utils.auth import verify_password
|
||||
from ..utils.jwt import create_and_save_token
|
||||
|
||||
@@ -19,8 +19,18 @@ async def login_for_access_token(
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Авторизация сотрудника"""
|
||||
# Разделяем username на имя и фамилию
|
||||
try:
|
||||
first_name, last_name = form_data.username.split()
|
||||
except ValueError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Username should be in format: 'First Last'",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Проверяем учетные данные сотрудника
|
||||
employee = employees.get_employee_by_last_name(db, form_data.username)
|
||||
employee = employees.get_employee_by_credentials(db, first_name, last_name)
|
||||
if not employee or not verify_password(form_data.password, employee.hashed_password):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
@@ -42,8 +52,18 @@ async def admin_login(
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Авторизация администратора"""
|
||||
# Разделяем username на имя и фамилию
|
||||
try:
|
||||
first_name, last_name = form_data.username.split()
|
||||
except ValueError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Username should be in format: 'First Last'",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Проверяем учетные данные администратора
|
||||
employee = employees.get_employee_by_last_name(db, form_data.username)
|
||||
employee = employees.get_employee_by_credentials(db, first_name, last_name)
|
||||
if not employee or not employee.is_admin or not verify_password(form_data.password, employee.hashed_password):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"""Schemas package"""
|
||||
from .employee import Employee, EmployeeCreate, EmployeeUpdate
|
||||
from .request import Request, RequestCreate, RequestUpdate
|
||||
from .auth import Token, TokenData
|
||||
from .auth import Token, TokenData, LoginCredentials
|
||||
|
||||
__all__ = [
|
||||
'Employee', 'EmployeeCreate', 'EmployeeUpdate',
|
||||
'Request', 'RequestCreate', 'RequestUpdate',
|
||||
'Token', 'TokenData'
|
||||
'Token', 'TokenData', 'LoginCredentials'
|
||||
]
|
||||
@@ -1,32 +1,18 @@
|
||||
"""Authentication schemas"""
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
class AdminLogin(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class EmployeeLogin(BaseModel):
|
||||
last_name: str
|
||||
password: str
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class EmployeeResponse(BaseModel):
|
||||
id: int
|
||||
first_name: str
|
||||
last_name: str
|
||||
department: str
|
||||
office: str
|
||||
access_token: str
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
class Token(BaseModel):
|
||||
"""Token schema"""
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
class TokenData(BaseModel):
|
||||
employee_id: int | None = None
|
||||
"""Token data schema"""
|
||||
employee_id: Optional[int] = None
|
||||
is_admin: bool = False
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
class LoginCredentials(BaseModel):
|
||||
"""Login credentials schema"""
|
||||
username: str # В формате "Имя Фамилия"
|
||||
password: str
|
||||
@@ -1,31 +1,33 @@
|
||||
"""Employee schemas"""
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
class EmployeeBase(BaseModel):
|
||||
"""Base employee schema"""
|
||||
first_name: str
|
||||
last_name: str
|
||||
department: str
|
||||
office: str
|
||||
is_admin: bool = False
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class EmployeeCreate(EmployeeBase):
|
||||
"""Employee creation schema"""
|
||||
password: str
|
||||
|
||||
class EmployeeUpdate(BaseModel):
|
||||
"""Employee update schema"""
|
||||
first_name: Optional[str] = None
|
||||
last_name: Optional[str] = None
|
||||
department: Optional[str] = None
|
||||
office: Optional[str] = None
|
||||
is_admin: Optional[bool] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class Employee(EmployeeBase):
|
||||
"""Employee schema"""
|
||||
id: int
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
class Config:
|
||||
"""Pydantic config"""
|
||||
from_attributes = True
|
||||
@@ -8,6 +8,6 @@ class Token(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class TokenData(BaseModel):
|
||||
user_id: int | None = None
|
||||
employee_id: int | None = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -3,11 +3,11 @@ from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from passlib.context import CryptContext
|
||||
from sqlalchemy.orm import Session
|
||||
import re
|
||||
|
||||
from .jwt import verify_token
|
||||
from ..database import get_db
|
||||
from ..crud import employees
|
||||
from ..models.employee import Employee
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
security = HTTPBearer(auto_error=False)
|
||||
@@ -23,7 +23,7 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
def get_current_admin(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||
db: Session = Depends(get_db)
|
||||
) -> dict:
|
||||
) -> Employee:
|
||||
"""Get current admin from token"""
|
||||
if not credentials:
|
||||
raise HTTPException(
|
||||
@@ -34,11 +34,16 @@ def get_current_admin(
|
||||
|
||||
try:
|
||||
token = credentials.credentials
|
||||
payload = verify_token(token, db)
|
||||
employee_id = int(payload.get("sub"))
|
||||
token_data = verify_token(token, db)
|
||||
if not token_data:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid authentication credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Проверяем, что это админ
|
||||
employee = employees.get_employee(db, employee_id)
|
||||
employee = employees.get_employee(db, token_data.employee_id)
|
||||
if not employee or not employee.is_admin:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
@@ -47,7 +52,7 @@ def get_current_admin(
|
||||
)
|
||||
|
||||
return employee
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid authentication credentials",
|
||||
@@ -57,7 +62,7 @@ def get_current_admin(
|
||||
def get_current_employee(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||
db: Session = Depends(get_db)
|
||||
) -> dict:
|
||||
) -> Employee:
|
||||
"""Get current employee from token"""
|
||||
if not credentials:
|
||||
raise HTTPException(
|
||||
@@ -68,11 +73,16 @@ def get_current_employee(
|
||||
|
||||
try:
|
||||
token = credentials.credentials
|
||||
payload = verify_token(token, db)
|
||||
employee_id = int(payload.get("sub"))
|
||||
token_data = verify_token(token, db)
|
||||
if not token_data:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid authentication credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Проверяем существование сотрудника
|
||||
employee = employees.get_employee(db, employee_id)
|
||||
employee = employees.get_employee(db, token_data.employee_id)
|
||||
if not employee:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
from datetime import datetime, timedelta
|
||||
from jose import JWTError, jwt
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
|
||||
from ..core.config import settings
|
||||
from ..models.token import Token
|
||||
from ..schemas.auth import TokenData
|
||||
|
||||
def create_access_token(data: dict) -> str:
|
||||
"""Create access token"""
|
||||
@@ -14,13 +16,22 @@ def create_access_token(data: dict) -> str:
|
||||
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
def verify_token(token: str, db: Session) -> dict:
|
||||
def verify_token(token: str, db: Session) -> Optional[TokenData]:
|
||||
"""Verify token"""
|
||||
try:
|
||||
# Проверяем, что токен действителен
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||
return payload
|
||||
except JWTError:
|
||||
employee_id = int(payload.get("sub"))
|
||||
if employee_id is None:
|
||||
return None
|
||||
|
||||
# Проверяем, что токен существует в базе
|
||||
db_token = db.query(Token).filter(Token.token == token).first()
|
||||
if not db_token:
|
||||
return None
|
||||
|
||||
return TokenData(employee_id=employee_id)
|
||||
except (JWTError, ValueError):
|
||||
return None
|
||||
|
||||
def create_and_save_token(employee_id: int, db: Session) -> str:
|
||||
|
||||
@@ -3,20 +3,17 @@ from aiogram import Bot
|
||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
import os
|
||||
from logging import getLogger
|
||||
from ..models.request import RequestStatus, RequestPriority
|
||||
from ..crud import requests
|
||||
from ..database import get_db
|
||||
from ..core.config import settings
|
||||
|
||||
# Initialize logger
|
||||
logger = getLogger(__name__)
|
||||
|
||||
# Initialize bot with token
|
||||
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "7677506032:AAHduD5EePz3bE23DKlo35KoOp2_9lZuS34")
|
||||
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "5057752127")
|
||||
|
||||
bot = Bot(token=TELEGRAM_BOT_TOKEN)
|
||||
# Initialize bot with token from settings
|
||||
bot = Bot(token=settings.TELEGRAM_BOT_TOKEN)
|
||||
|
||||
def format_priority(priority: str) -> str:
|
||||
"""Format priority with emoji"""
|
||||
@@ -59,7 +56,7 @@ async def send_request_notification(request_id: int):
|
||||
)
|
||||
|
||||
await bot.send_message(
|
||||
chat_id=TELEGRAM_CHAT_ID,
|
||||
chat_id=settings.TELEGRAM_CHAT_ID,
|
||||
text=message,
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user