from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean, BigInteger from sqlalchemy.dialects.mysql import JSON from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker, declarative_base from sqlalchemy.sql import func from passlib.context import CryptContext from dotenv import load_dotenv from sqlalchemy.future import select import os # Загружаем переменные окружения load_dotenv() # Правильная строка подключения DATABASE_URL = os.getenv('DATABASE_URL') # Должно быть "mysql+aiomysql://..." или "postgresql+asyncpg://..." # Создаём асинхронный движок async_engine = create_async_engine(DATABASE_URL, echo=True) # Создаём фабрику сессий async_session = sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False) # Базовый класс для моделей Base = declarative_base() # Контекст хеширования паролей pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # Функция для инициализации базы данных async def init_db(): async with async_engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) async with async_session() as session: # Проверяем, есть ли уже данные в таблице пользователей result = await session.execute(select(User).limit(1)) user_exists = result.scalars().first() if not user_exists: new_user = User( username="admin", hashed_password=hash_password("admin123"), # Пароль хешируем role="admin", is_active=True ) session.add(new_user) await session.commit() # Зависимость FastAPI для работы с БД async def get_async_session() -> AsyncSession: async with async_session() as session: yield session # Функции для хеширования паролей def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) # async def get_db(): # async with SessionLocal() as session: # yield session # Модель пользователя class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String(55), unique=True, index=True, nullable=False) # ✅ Правильно hashed_password = Column(String(255), nullable=False) role = Column(String(55), default="user") is_active = Column(Boolean, default=True) # Модель профиля class Profile(Base): __tablename__ = "profile" id = Column(Integer, primary_key=True, index=True) link = Column(String(255), unique=True, nullable=True) first_name = Column(String(255), nullable=False) last_name = Column(String(255), nullable=False) pronouns = Column(String(50), nullable=True) email = Column(String(255), nullable=True) telephone = Column(String(20), nullable=True) location = Column(String(255), nullable=True) statement = Column(Text, nullable=True) skills = Column(JSON, nullable=True) # JSON-поле для списка навыков websites = Column(JSON, nullable=True) # JSON-поле для списка сайтов languages = Column(JSON, nullable=True) # JSON-поле для языков date = Column(DateTime, server_default=func.now()) # Модель вакансий class Job(Base): __tablename__ = "jobs" job_id = Column(BigInteger, primary_key=True, index=True) job_title = Column(String(255), nullable=False) job_company = Column(String(255), nullable=True) minimum_annual_salary = Column(Float, nullable=True) salary_currency = Column(String(10), nullable=True) location_type = Column(String(50), nullable=True) # Remote, On-site, Hybrid location = Column(String(255), nullable=True) job_level = Column(String(50), nullable=True) # Entry-level, Junior, Mid job_type = Column(String(50), nullable=True) # Full-time, Part-time days_posted = Column(Integer, nullable=True) hourly_rate = Column(Float, nullable=True) link = Column(String(2083), nullable=True) # URL вакансии link_company = Column(String(2083), nullable=True) # URL компании active = Column(Boolean, default=True) # Вакансия активна?