feat(backend): subscription state columns on users table
Adds 4 nullable columns to the users table so deployments that wire an external billing system (e.g. velxio.dev with Odoo) have a place to cache subscription status. Self-hosters never write these — defaults are safe (no paid features unlock). - models/user.py: is_paid_subscriber (bool, default false), subscription_status (str|None), subscription_period_end (datetime|None), odoo_partner_id (int|None, indexed). - main.py: 4 ALTER TABLE statements appended to the legacy_migrations list so existing deployments auto-migrate on next boot. - schemas/auth.py: extend UserResponse with the 3 user-visible fields (is_paid_subscriber, subscription_status, subscription_period_end). The frontend useAuthStore already persists the whole UserResponse, so these surface automatically without any frontend changes upstream. odoo_partner_id stays internal — clients don't need it. Zero behavioural change for existing OSS deployments.
This commit is contained in:
parent
a5b5e57aae
commit
5f0e7523ed
|
|
@ -74,6 +74,13 @@ async def lifespan(_app: FastAPI):
|
|||
"ALTER TABLE usage_events ADD COLUMN country VARCHAR(2)",
|
||||
# Multi-board persistence (replaces single board_type as the source of truth)
|
||||
"ALTER TABLE projects ADD COLUMN boards_json TEXT NOT NULL DEFAULT '[]'",
|
||||
# Subscription state. Self-hosters never write these; deployments
|
||||
# that wire an external billing system (e.g. velxio.dev → Odoo)
|
||||
# populate them via webhooks + periodic resync.
|
||||
"ALTER TABLE users ADD COLUMN is_paid_subscriber BOOLEAN NOT NULL DEFAULT 0",
|
||||
"ALTER TABLE users ADD COLUMN subscription_status VARCHAR(20)",
|
||||
"ALTER TABLE users ADD COLUMN subscription_period_end DATETIME",
|
||||
"ALTER TABLE users ADD COLUMN odoo_partner_id INTEGER",
|
||||
]
|
||||
for stmt in legacy_migrations:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -31,4 +31,12 @@ class User(Base):
|
|||
signup_country: Mapped[str | None] = mapped_column(String(2), nullable=True, index=True)
|
||||
last_country: Mapped[str | None] = mapped_column(String(2), nullable=True, index=True)
|
||||
|
||||
# Subscription state. Self-hosters never set these — defaults are safe
|
||||
# (no paid features unlock). Production velxio.dev syncs them from an
|
||||
# external billing system (Odoo) via webhook + periodic resync.
|
||||
is_paid_subscriber: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
subscription_status: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
||||
subscription_period_end: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
odoo_partner_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
|
||||
|
||||
projects: Mapped[list["Project"]] = relationship("Project", back_populates="owner", lazy="select") # noqa: F821
|
||||
|
|
|
|||
|
|
@ -41,4 +41,12 @@ class UserResponse(BaseModel):
|
|||
is_admin: bool = False
|
||||
created_at: datetime
|
||||
|
||||
# Subscription state. Always present in the response shape so the frontend
|
||||
# gating logic doesn't have to deal with `undefined`. Self-hosters get
|
||||
# the safe defaults (no paid features); production overlays sync these
|
||||
# from an external billing system.
|
||||
is_paid_subscriber: bool = False
|
||||
subscription_status: str | None = None
|
||||
subscription_period_end: datetime | None = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
|
|
|||
Loading…
Reference in New Issue