FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
37 lines
1.4 KiB
Python
37 lines
1.4 KiB
Python
"""Every distinct IP an account has ever signed in from - one row per
|
|
(user, ip) pair, first_seen_at set once and last_seen_at touched on every
|
|
subsequent sign-in from that same IP. Pure data capture for now (see
|
|
app/services/auth_service.py's sign-in paths, both real login and the
|
|
local-dev bypass) - nothing currently reads this table, but it's the
|
|
foundation a later "new device/location" security feature would query
|
|
against without needing to scan/dedupe the much larger, append-only
|
|
user_security_events log."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
from typing import TYPE_CHECKING
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, String, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.db.base import Base, TimestampMixin, UUIDPrimaryKeyMixin
|
|
|
|
if TYPE_CHECKING:
|
|
from app.models.user import User
|
|
|
|
|
|
class UserKnownIp(Base, UUIDPrimaryKeyMixin, TimestampMixin):
|
|
__tablename__ = "user_known_ips"
|
|
__table_args__ = (UniqueConstraint("user_id", "ip_address", name="uq_user_known_ips_user_ip"),)
|
|
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey("users.id", ondelete="CASCADE"), index=True
|
|
)
|
|
ip_address: Mapped[str] = mapped_column(String(45))
|
|
first_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
last_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
|
|
user: Mapped[User] = relationship()
|