FastAPI + HTMX Web-App zum Speichern von Links mit automatischer KI-Kategorisierung, Zusammenfassung und semantischer Suche (OpenAI). Mehrbenutzer mit Login, SQLite-Persistenz, Docker/Docker-Compose-Setup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
50 lines
1.7 KiB
Python
50 lines
1.7 KiB
Python
import json
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, String, Text
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from .database import Base
|
|
|
|
|
|
def utcnow() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
|
|
password_hash: Mapped[str] = mapped_column(String(255))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=utcnow)
|
|
|
|
links: Mapped[list["Link"]] = relationship(
|
|
back_populates="user", cascade="all, delete-orphan"
|
|
)
|
|
|
|
|
|
class Link(Base):
|
|
__tablename__ = "links"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True)
|
|
url: Mapped[str] = mapped_column(Text)
|
|
title: Mapped[str] = mapped_column(Text, default="")
|
|
summary: Mapped[str] = mapped_column(Text, default="")
|
|
category: Mapped[str] = mapped_column(String(120), default="", index=True)
|
|
manufacturer: Mapped[str] = mapped_column(String(120), default="", index=True)
|
|
tags: Mapped[str] = mapped_column(Text, default="[]") # JSON-Liste
|
|
embedding: Mapped[str] = mapped_column(Text, default="") # JSON-Liste (Vektor)
|
|
status: Mapped[str] = mapped_column(String(20), default="ok")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=utcnow, index=True)
|
|
|
|
user: Mapped["User"] = relationship(back_populates="links")
|
|
|
|
@property
|
|
def tag_list(self) -> list[str]:
|
|
try:
|
|
return json.loads(self.tags)
|
|
except (ValueError, TypeError):
|
|
return []
|