Files
gcontext/api/app/models.py
T
bernatsamperaandClaude Fable 5 aca7523754 Add workflows API, the marketplace backend (marketplace task 3b)
FastAPI + Postgres service that stores workflow templates as file
bundles with indexed manifest fields, submit-for-review publishing,
and moderation endpoints behind an admin token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 13:15:47 +02:00

65 lines
2.1 KiB
Python

from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Index, Integer, Text, func
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
class Base(DeclarativeBase):
pass
class Template(Base):
__tablename__ = "templates"
pk: Mapped[int] = mapped_column(Integer, primary_key=True)
id: Mapped[str] = mapped_column(Text, nullable=False)
name: Mapped[str] = mapped_column(Text, nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False)
tags: Mapped[list[str]] = mapped_column(ARRAY(Text), nullable=False, default=list)
status: Mapped[str] = mapped_column(Text, nullable=False, default=PENDING)
submitted_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
reviewed_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
downloads: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
votes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
files: Mapped[list["TemplateFile"]] = relationship(
back_populates="template", cascade="all, delete-orphan", order_by="TemplateFile.path"
)
# One approved and at most one pending entry per workflow id.
Index(
"uq_templates_id_approved",
Template.id,
unique=True,
postgresql_where=Template.status == APPROVED,
)
Index(
"uq_templates_id_pending",
Template.id,
unique=True,
postgresql_where=Template.status == PENDING,
)
class TemplateFile(Base):
__tablename__ = "template_files"
pk: Mapped[int] = mapped_column(Integer, primary_key=True)
template_pk: Mapped[int] = mapped_column(
ForeignKey("templates.pk", ondelete="CASCADE"), nullable=False, index=True
)
path: Mapped[str] = mapped_column(Text, nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
template: Mapped[Template] = relationship(back_populates="files")