From 05914f0b23cf913435d2648ba2bf031222f93407 Mon Sep 17 00:00:00 2001 From: hexdev Date: Mon, 17 Aug 2026 21:33:08 +0700 Subject: [PATCH] feat: added duration_days to /users/subscriptions --- ...f4ece5936e3d_subscription_duration_days.py | 32 +++++++++++++++++++ db/models/subscriptions.py | 1 + routes/users.py | 2 ++ schemas/user.py | 3 +- services/subscriptions.py | 5 +-- 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 alembic/versions/f4ece5936e3d_subscription_duration_days.py diff --git a/alembic/versions/f4ece5936e3d_subscription_duration_days.py b/alembic/versions/f4ece5936e3d_subscription_duration_days.py new file mode 100644 index 0000000..947b070 --- /dev/null +++ b/alembic/versions/f4ece5936e3d_subscription_duration_days.py @@ -0,0 +1,32 @@ +"""+subscription.duration_days + +Revision ID: f4ece5936e3d +Revises: 135672cdf14a +Create Date: 2026-08-17 21:23:17.900314 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'f4ece5936e3d' +down_revision: Union[str, Sequence[str], None] = '135672cdf14a' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('subscriptions', sa.Column('duration_days', sa.INTEGER(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('subscriptions', 'duration_days') + # ### end Alembic commands ### diff --git a/db/models/subscriptions.py b/db/models/subscriptions.py index 44a0b8c..b060228 100644 --- a/db/models/subscriptions.py +++ b/db/models/subscriptions.py @@ -20,6 +20,7 @@ class Subscription(Base): user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False, unique=True) devices: Mapped[int] = mapped_column(INTEGER, nullable=False) + duration_days: Mapped[int] = mapped_column(INTEGER, default=30, nullable=True) status: Mapped[SubscriptionStatus] = mapped_column( Enum(SubscriptionStatus, name="subscriptionstatus"), nullable=False, diff --git a/routes/users.py b/routes/users.py index 2064576..fa6241d 100644 --- a/routes/users.py +++ b/routes/users.py @@ -30,6 +30,7 @@ async def get_subscription(ctx: AuthContext = Depends(get_auth_context)): expires_at=None, addon_ids=[], subscription_link=None, + duration_days=None, ) rw_user = await get_rw_user(get_sdk(), ctx.user.telegram_id, ctx.user.username) @@ -39,6 +40,7 @@ async def get_subscription(ctx: AuthContext = Depends(get_auth_context)): expires_at=sub.expires_at, addon_ids=[a.addon_id for a in sub.addons], subscription_link=build_subscription_link(rw_user.short_uuid), + duration_days=sub.duration_days, ) diff --git a/schemas/user.py b/schemas/user.py index 7b1c255..5d4800b 100644 --- a/schemas/user.py +++ b/schemas/user.py @@ -4,11 +4,12 @@ from pydantic import BaseModel, Field class SubscriptionData(BaseModel): + has_subscription: bool = Field() devices: int | None = Field(None) expires_at: datetime | None = Field(None) addon_ids: list[str] = Field(default_factory=list) subscription_link: str | None = Field(None) - has_subscription: bool = Field() + duration_days: int | None = Field(None) class UserInfo(BaseModel): diff --git a/services/subscriptions.py b/services/subscriptions.py index 2c009a4..04a368e 100644 --- a/services/subscriptions.py +++ b/services/subscriptions.py @@ -72,7 +72,7 @@ async def replace_subscription_addons( async def ensure_subscription( - user: User, session: AsyncSession, starts_at: datetime + user: User, session: AsyncSession, starts_at: datetime, duration_days: int ) -> Subscription: subscription = user.subscription if subscription is not None: @@ -83,6 +83,7 @@ async def ensure_subscription( devices=0, status=SubscriptionStatus.EXPIRED, expires_at=starts_at, + duration_days=duration_days, ) session.add(subscription) await session.flush() @@ -121,7 +122,7 @@ async def apply_order_now( pricing: PricingPlans, now: datetime, ) -> Subscription: - subscription = await ensure_subscription(user, session, now) + subscription = await ensure_subscription(user, session, now, order.duration_days) addon_ids = [addon.addon_id for addon in order.addons] current_addons = [] if subscription.devices == 0 else get_subscription_addon_ids(subscription)