refactor: tighten backend, flows, frontend, tests; push flows to Langflow

This commit is contained in:
Jonas Miederer 2026-09-28 15:58:00 +02:00
parent 6688fb08c6
commit 06ab25fb47
31 changed files with 783 additions and 358 deletions

View file

@ -0,0 +1,18 @@
"""Shared SQLModel base: UTC timestamps, no copy-paste across models."""
from datetime import UTC, datetime
from sqlalchemy import Column, DateTime
from sqlmodel import Field
def utcnow() -> datetime:
return datetime.now(UTC)
def ts_column() -> Column:
return Column(DateTime(timezone=True))
def ts_field(*, default_factory=utcnow) -> datetime: # type: ignore[no-untyped-def]
return Field(default_factory=default_factory, sa_column=ts_column())

View file

@ -1,18 +1,12 @@
"""Flow engine domain models (SQLModel tables)."""
from datetime import UTC, datetime
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import JSON, Column, DateTime, UniqueConstraint
from sqlalchemy import JSON, Column, UniqueConstraint
from sqlmodel import Field, SQLModel
def utcnow() -> datetime:
return datetime.now(UTC)
def _ts_column() -> Column:
return Column(DateTime(timezone=True))
from .base import ts_column, utcnow
def _json_column() -> Column:
@ -34,7 +28,7 @@ class FlowDefinition(SQLModel, table=True):
source_path: str | None = Field(default=None, max_length=255)
# Flow to run after a human approval on this flow's runs (nullable).
continuation_flow_id: UUID | None = Field(default=None, foreign_key="flow_definitions.id")
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
class FlowVersion(SQLModel, table=True):
@ -49,9 +43,9 @@ class FlowVersion(SQLModel, table=True):
langflow_json: dict = Field(default_factory=dict, sa_column=_json_column())
# Flow id under which LFX serves this version (set on deploy).
lfx_flow_id: str | None = Field(default=None, max_length=200)
deployed_at: datetime | None = Field(default=None, sa_column=_ts_column())
deployed_at: datetime | None = Field(default=None, sa_column=ts_column())
author: str = Field(default="", max_length=200)
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
# Cached form schema for this exact JSON (see app/flows/input_schema.py).
# Computed at version creation; nullable for pre-Phase-3 rows.
input_schema: dict | None = Field(default=None, sa_column=_json_column())
@ -70,8 +64,8 @@ class FlowRun(SQLModel, table=True):
input: dict | None = Field(default=None, sa_column=_json_column())
output: dict | None = Field(default=None, sa_column=_json_column())
session_id: str | None = Field(default=None, max_length=200)
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
finished_at: datetime | None = Field(default=None, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
finished_at: datetime | None = Field(default=None, sa_column=ts_column())
class ApprovalTask(SQLModel, table=True):
@ -90,5 +84,5 @@ class ApprovalTask(SQLModel, table=True):
# open | approved | rejected
status: str = Field(default="open", index=True, max_length=20)
decision_note: str = Field(default="", max_length=2000)
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
decided_at: datetime | None = Field(default=None, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
decided_at: datetime | None = Field(default=None, sa_column=ts_column())

View file

@ -1,18 +1,11 @@
"""Access-management domain models (SQLModel tables)."""
from datetime import UTC, datetime
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import Column, DateTime
from sqlmodel import Field, SQLModel
def utcnow() -> datetime:
return datetime.now(UTC)
def _ts_column() -> Column:
return Column(DateTime(timezone=True))
from .base import ts_column, utcnow
class User(SQLModel, table=True):
@ -26,7 +19,7 @@ class User(SQLModel, table=True):
# member | admin
role: str = Field(default="member", max_length=20)
tenant_id: str = Field(default="default", max_length=200)
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
class AccessRequest(SQLModel, table=True):
@ -40,8 +33,8 @@ class AccessRequest(SQLModel, table=True):
reason: str = Field(default="", max_length=2000)
# pending | approved | rejected
status: str = Field(default="pending", index=True, max_length=20)
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
decided_at: datetime | None = Field(default=None, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
decided_at: datetime | None = Field(default=None, sa_column=ts_column())
decided_by: str | None = Field(default=None, max_length=320)
@ -53,11 +46,11 @@ class MagicLinkToken(SQLModel, table=True):
id: UUID = Field(default_factory=uuid4, primary_key=True)
user_id: UUID = Field(foreign_key="users.id", index=True)
token_hash: str = Field(index=True, unique=True, max_length=64)
expires_at: datetime = Field(sa_column=_ts_column())
expires_at: datetime = Field(sa_column=ts_column())
# Set before the session row is created, so a reused token can never
# mint a second session.
used_at: datetime | None = Field(default=None, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
used_at: datetime | None = Field(default=None, sa_column=ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
class UserSession(SQLModel, table=True):
@ -67,6 +60,6 @@ class UserSession(SQLModel, table=True):
id: str = Field(primary_key=True, max_length=128)
user_id: UUID = Field(foreign_key="users.id", index=True)
created_at: datetime = Field(default_factory=utcnow, sa_column=_ts_column())
expires_at: datetime = Field(sa_column=_ts_column())
revoked_at: datetime | None = Field(default=None, sa_column=_ts_column())
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
expires_at: datetime = Field(sa_column=ts_column())
revoked_at: datetime | None = Field(default=None, sa_column=ts_column())