""" User and authentication models. """ import uuid from datetime import datetime from enum import Enum from sqlalchemy import Boolean, DateTime, String, Text, func from sqlalchemy.dialects.postgresql import JSONB, UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base class AuthProvider(str, Enum): LOCAL = "local" GOOGLE = "google" GITHUB = "github" class UserRole(str, Enum): CANDIDATE = "candidate" EMPLOYER = "employer" ADMIN = "admin" class User(Base): __tablename__ = "users" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) email: Mapped[str] = mapped_column(String(320), unique=True, index=True, nullable=False) hashed_password: Mapped[str | None] = mapped_column(String(256), nullable=True) full_name: Mapped[str] = mapped_column(String(256), nullable=False) avatar_url: Mapped[str | None] = mapped_column(String(512), nullable=True) role: Mapped[str] = mapped_column(String(20), default=UserRole.CANDIDATE, nullable=False) auth_provider: Mapped[str] = mapped_column(String(20), default=AuthProvider.LOCAL, nullable=False) auth_provider_id: Mapped[str | None] = mapped_column(String(256), nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, default=True) is_verified: Mapped[bool] = mapped_column(Boolean, default=False) is_superuser: Mapped[bool] = mapped_column(Boolean, default=False) llm_preferences: Mapped[dict | None] = mapped_column(JSONB, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) last_login_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) profile: Mapped["Profile"] = relationship(back_populates="user", uselist=False) applications: Mapped[list["Application"]] = relationship(back_populates="user") resume_versions: Mapped[list["ResumeVersion"]] = relationship(back_populates="user") saved_jobs: Mapped[list["SavedJob"]] = relationship(back_populates="user") api_keys: Mapped[list["UserAPIKey"]] = relationship(back_populates="user") class UserAPIKey(Base): """Encrypted API keys for LLM providers. key_prefix/key_last4 stored for display without decryption.""" __tablename__ = "user_api_keys" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False, index=True) provider: Mapped[str] = mapped_column(String(50), nullable=False) encrypted_key: Mapped[str] = mapped_column(Text, nullable=False) key_prefix: Mapped[str | None] = mapped_column(String(8), nullable=True) # e.g., "sk-p" key_last4: Mapped[str | None] = mapped_column(String(8), nullable=True) # e.g., "ab4f" label: Mapped[str | None] = mapped_column(String(100), nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) user: Mapped["User"] = relationship(back_populates="api_keys") class Profile(Base): __tablename__ = "profiles" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), unique=True, nullable=False) phone: Mapped[str | None] = mapped_column(String(30), nullable=True) location: Mapped[str | None] = mapped_column(String(200), nullable=True) linkedin_url: Mapped[str | None] = mapped_column(String(512), nullable=True) github_url: Mapped[str | None] = mapped_column(String(512), nullable=True) portfolio_url: Mapped[str | None] = mapped_column(String(512), nullable=True) website_url: Mapped[str | None] = mapped_column(String(512), nullable=True) headline: Mapped[str | None] = mapped_column(String(256), nullable=True) summary: Mapped[str | None] = mapped_column(Text, nullable=True) experience: Mapped[list | None] = mapped_column(JSONB, nullable=True) education: Mapped[list | None] = mapped_column(JSONB, nullable=True) skills: Mapped[list | None] = mapped_column(JSONB, nullable=True) certifications: Mapped[list | None] = mapped_column(JSONB, nullable=True) projects: Mapped[list | None] = mapped_column(JSONB, nullable=True) languages: Mapped[list | None] = mapped_column(JSONB, nullable=True) desired_roles: Mapped[list | None] = mapped_column(JSONB, nullable=True) desired_locations: Mapped[list | None] = mapped_column(JSONB, nullable=True) remote_preference: Mapped[str | None] = mapped_column(String(20), nullable=True) salary_min: Mapped[int | None] = mapped_column(nullable=True) salary_max: Mapped[int | None] = mapped_column(nullable=True) salary_currency: Mapped[str | None] = mapped_column(String(3), nullable=True) visa_required: Mapped[bool | None] = mapped_column(Boolean, nullable=True) open_to_work: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) user: Mapped["User"] = relationship(back_populates="profile")