Commit Graph

7134 Commits

Author SHA1 Message Date
Fringg f4eeb9a503 fix: multiple payment and notification bugs
- CloudPayments: add missing process_referral_topup, has_made_first_topup flag, and admin notification (matching other adapters)
- Promocode: handle TelegramBadRequest for broadcast messages without text (fallback to answer())
- Devices: unify price prorating to day-based calculation (matching cabinet behavior)
- Auth: pass Bot instance to process_referral_registration for campaign referral notifications
- Wata: remove WATA_TERMINAL_PUBLIC_ID from is_wata_enabled() (not used in API calls), make type field conditional, add transactionId webhook fallback
2026-03-08 14:17:44 +03:00
Fringg 1f664a9083 fix: remove is_active_paid_subscription guard from admin deactivation
The guard silently blocked admins from deactivating active paid
subscriptions, returning a generic error with no explanation.
Admin deactivation is intentional (with confirmation step) and
should not be prevented. The guard remains in automated processes
(monitoring, broadcast, user_service) where it makes sense.
2026-03-08 13:05:52 +03:00
Fringg 330d1cb6fe fix: gift purchase notification and activation flow
- Refresh purchase.user relationship after setting user_id to fix
  stale None value that prevented Telegram gift notifications
- Route gift purchases with expired subscriptions through
  PENDING_ACTIVATION instead of auto-activating
- Hide subscription URL from gift buyer in API response
2026-03-08 12:47:25 +03:00
Fringg 9ba61a0879 feat: add telegram gift notification with inline activation button
- New gift_activation handler for gift_activate:{id} callback buttons
- Send Telegram notification to gift recipients with activate button
- Add skip_notification param to activate_purchase to prevent duplicates
- Fix telegram username regex minimum length (4→5 chars) in landing routes
- Add BOT_TOKEN guard in telegram gift notification sender
- Pre-resolve notification params before commit to avoid DetachedInstanceError
2026-03-07 20:39:04 +03:00
Egor d7f05ae409 Merge pull request #2696 from BEDOLAGA-DEV/main
w
2026-03-07 17:42:20 +03:00
Egor bf2d5e48e5 Merge pull request #2694 from BEDOLAGA-DEV/release-please--branches--main
chore(main): release 3.25.0
v3.25.0
2026-03-07 17:22:38 +03:00
github-actions[bot] 93bf21e55b chore(main): release 3.25.0 2026-03-07 14:21:25 +00:00
Egor fea44f5ad4 Merge pull request #2693 from BEDOLAGA-DEV/dev
Dev
2026-03-07 17:20:52 +03:00
Fringg 923b36a8b9 fix: use --frozen instead of --locked in Dockerfile to avoid version mismatch 2026-03-07 17:17:35 +03:00
Fringg a7fea86c99 chore: update uv.lock 2026-03-07 17:15:49 +03:00
Fringg fbe56c15ac style: ruff format 2026-03-07 17:13:56 +03:00
Egor 213f82b9a4 Merge pull request #2692 from BEDOLAGA-DEV/main
w
2026-03-07 17:13:03 +03:00
Fringg d72ea6b7f9 fix: remaining context_vars/SAMPLE_CONTEXTS mismatches found by agents
- traffic_reset: traffic_limit → reset_gb, current_limit_gb
- payment_received: amount → formatted_amount
- guest_activation_required: added missing is_gift to context_vars
- daily_debit SAMPLE_CONTEXTS: added missing amount_rubles, new_balance_rubles
- traffic_reset SAMPLE_CONTEXTS: aligned with runtime keys
- payment_received SAMPLE_CONTEXTS: added amount_rubles
2026-03-07 17:11:57 +03:00
Fringg c507634398 fix: align subscription_renewed/activated context_vars with runtime keys
- subscription_renewed: new_end_date → new_expires_at
- subscription_activated: end_date → expires_at
- Added traffic_limit_gb and device_limit to both types
- Fixed SAMPLE_CONTEXTS keys to match
2026-03-07 17:11:57 +03:00
Fringg c9ea2b15e9 fix: strip newlines from subject substitution, fix subscription notification context
- Defense-in-depth: strip \r\n from context values in subject line
  substitution to prevent email header injection at composition layer
- Add missing tariff_name to classic mode subscription notification context
  (prevents literal {tariff_name} in DB override templates)
- Remove SQLAlchemy model object from notification context dict
  (str() on model produces garbage in DB override templates)
2026-03-07 17:11:57 +03:00
Fringg ab5313a381 fix: align context_vars and SAMPLE_CONTEXTS with actual runtime context keys
TEMPLATE_TYPES context_vars showed wrong placeholder names to admins
(e.g. 'amount' instead of 'formatted_amount', 'balance' instead of
'formatted_balance'). SAMPLE_CONTEXTS had mismatched keys causing
test emails to render with empty values.

Fixed types: balance_topup, balance_change, autopay_success,
autopay_insufficient_funds, daily_debit, daily_insufficient_funds,
referral_bonus.
2026-03-07 17:11:57 +03:00
Fringg 351d714f2d fix: substitute sample context in admin test email for template overrides
Same bug as notification_delivery_service — send_test_email used
get_template_override (raw template) instead of get_rendered_override.
Admins testing custom templates saw literal {days_left} placeholders
instead of sample values.
2026-03-07 16:48:18 +03:00
Fringg d52c87b2b7 fix: substitute context variables in email template overrides
The send_notification method was calling get_template_override which
returns raw template HTML without variable substitution. Placeholders
like {days_left} and {expires_at} were sent to users as literal text.

Switched to get_rendered_override which properly substitutes context
variables via str.replace before wrapping in the base email template.
2026-03-07 16:40:35 +03:00
Fringg d9f9f3dca1 fix: add or [] guard to remaining connected_squads call site in fulfill_purchase
Missed the create_paid_subscription branch (line 275) in the previous
commit — now all 4 call sites consistently use `or []` to guard against
None from tariff.allowed_squads JSON column.
2026-03-07 16:34:15 +03:00
Fringg 44d46feb0a fix: correct device_limit and connected_squads in guest purchase fulfillment
- Remove `or settings.DEFAULT_DEVICE_LIMIT` from fulfill_purchase expired
  subscription branch — tariff.device_limit is NOT NULL so the `or` pattern
  would incorrectly convert 0 (unlimited) to DEFAULT_DEVICE_LIMIT
- Add `or []` guard to connected_squads in activate_purchase to match
  replace_subscription's list[str] type contract (tariff.allowed_squads
  can be None from JSON column)
2026-03-07 16:29:04 +03:00
Fringg 9e78509284 fix: handle expired subscription in guest purchase fulfillment
When a user with an expired subscription makes a landing page purchase,
the code tried to INSERT a new subscription, violating the user_id
unique constraint. Now uses replace_subscription for expired/inactive
subscriptions instead of create_paid_subscription.
2026-03-07 16:02:06 +03:00
Fringg f4ab174d32 fix: support {total_amount} placeholder in cart notification templates
Add total_amount as an alias for cart_total in format() calls so custom
locale overrides using {total_amount} are properly substituted instead
of appearing as literal text in user messages.
2026-03-07 15:50:42 +03:00
Fringg fc65e2de4c fix: use information_schema for constraint existence checks in migrations
Replace pg_class lookup with information_schema.table_constraints query
that is schema-qualified and consistent with migration 0028 pattern.
Fixes constraint detection on fresh installs where create_all() creates
constraints that pg_class lookups could miss.
2026-03-07 15:50:31 +03:00
Fringg ba335fe784 fix: use pg_class lookup for constraint existence checks in migrations
inspector.get_unique_constraints() fails to detect constraints created
by Base.metadata.create_all() on fresh installs, causing
DuplicateTableError. Query pg_class directly for reliable detection.
2026-03-07 15:38:11 +03:00
Fringg 5214f55f46 fix: drop legacy prize_days column from contest_templates
The column was left over from the old schema before prize_type/prize_value
refactoring. Its NOT NULL constraint caused INSERT failures since the
SQLAlchemy model no longer includes it.
2026-03-07 15:34:54 +03:00
Fringg 9d5329d9d1 fix: resolve NameError in YooKassa successful payment processing
event_object was referenced in _process_successful_yookassa_payment
but never passed to the method, causing all YooKassa webhook payments
to fail. Use payment.amount_kopeks from the database model instead.
2026-03-07 13:28:25 +03:00
Fringg bbd353ff38 fix: resolve alembic migration failures on fresh database install
Migration 0001 uses Base.metadata.create_all() which creates ALL tables
from current models.py, causing subsequent migrations (0015+) to fail
with "already exists" errors when they try to re-create constraints,
indexes, columns, and tables.

Three-layer fix:

1. migrations.py: detect fresh DB (no tables) and bootstrap via
   create_all() + stamp head, bypassing all migrations entirely.

2. models.py: add EmailTemplate model, CheckConstraints to LandingPage,
   and indexes to GuestPurchase so create_all() produces a complete
   schema identical to running all 30 migrations sequentially.

3. Idempotency guards in migrations 0015-0030: _has_unique_constraint,
   _has_table, _has_index, _has_column, _has_check_constraint checks
   before DDL operations, protecting against re-runs via make migrate.
2026-03-07 13:17:04 +03:00
Fringg 11d3e637c1 feat: add configurable animated background for landing pages
Add background_config JSON field to LandingPage model, enabling
per-landing animated backgrounds (aurora, sparkles, vortex, etc).

- Add background_config column (JSON, nullable) with Alembic migration
- Add validated background_config to create/update/detail/public schemas
- Reuse ALLOWED_BG_TYPES and _validate_settings from branding module
- Strip unknown keys via whitelist, validate all fields including reducedOnMobile
2026-03-07 12:46:02 +03:00
Fringg 0ba1127469 feat: add paginated purchases list endpoint for landing pages
Add GET /admin/landings/{id}/purchases with offset/limit pagination,
optional status filter (validated against GuestPurchaseStatus enum),
tariff name join, and truncated token display.
2026-03-07 09:49:57 +03:00
Fringg 25478ced20 feat: add landing page statistics endpoint with charts data
Add GET /admin/landings/{id}/stats endpoint returning:
- Summary stats (purchases, revenue, gifts, conversion rate)
- Daily breakdown for last 30 days (purchases, revenue, gifts per day)
- Tariff distribution (purchases and revenue per tariff)

Uses case() expressions for SQLite compatibility, timezone-safe
date grouping via func.timezone('UTC', ...), and composite index
(landing_id, status, paid_at) for query performance.
2026-03-07 09:35:21 +03:00
Egor 57b95671ea Merge pull request #2686 from BEDOLAGA-DEV/release-please--branches--main
chore(main): release 3.24.0
v3.24.0
2026-03-07 07:31:21 +03:00
github-actions[bot] eecf2b4183 chore(main): release 3.24.0 2026-03-07 04:30:05 +00:00
Egor 5a97fc2fa1 Merge pull request #2685 from BEDOLAGA-DEV/dev
Dev
2026-03-07 07:26:29 +03:00
Fringg 26b486cdd9 chore: sync uv.lock with pyproject.toml version bump 2026-03-07 07:18:43 +03:00
Egor ba05c5ce92 Merge pull request #2684 from BEDOLAGA-DEV/main
w
2026-03-07 07:16:05 +03:00
Fringg 372d628908 fix: remove executable bit from email_service.py 2026-03-07 07:14:52 +03:00
Fringg ceac29d5e3 style: format admin_landings.py 2026-03-07 07:13:01 +03:00
Egor 3ee108fce8 Merge pull request #2658 from thegrayfoxxx/fix_logo_from_show_qr
fix: reset QR photo when returning to referral
2026-03-07 07:09:38 +03:00
Egor de541ea1c3 Merge pull request #2682 from smediainfo/fix/email-message-id-headers
fix: add Message-ID and Date headers to outgoing emails
2026-03-07 07:08:37 +03:00
Fringg 6d65e15266 fix: read discount overrides from landing model instead of response DTO
After removing overrides from the public LandingDiscountInfo response,
_load_landing_tariffs still referenced discount.overrides which no
longer exists. Read from landing.discount_overrides directly.
2026-03-07 07:05:38 +03:00
Fringg aa7d98630d feat: add discount system for landing pages
Add time-bounded percentage discounts with per-tariff overrides
and countdown timer support for landing pages.

- Add 5 discount columns to LandingPage model (percent, overrides,
  starts_at, ends_at, badge_text) with Alembic migrations 0027-0028
- Add DB CHECK constraints for discount_percent range and date ordering
- Add discount price calculation in validate_and_calculate() and
  public landing config endpoint with consistent formula
- Add admin CRUD with Pydantic validation, cascade-clear on removal,
  merged date validation on partial updates, size bounds
- Remove discount_overrides from public API (baked into prices)
- Add size limits for allowed_tariff_ids and allowed_periods
2026-03-07 07:01:35 +03:00
Fringg 8b77cdae2c feat: add GET /admin/rbac/users endpoint for listing all RBAC users 2026-03-07 06:23:14 +03:00
Fringg c93dbec7a0 feat: add landings to permission registry
Register landings:read, landings:create, landings:edit, landings:delete
permissions so they appear in the role editor permission matrix.
2026-03-07 06:05:03 +03:00
Fringg fa21549cac fix: add activate hint to gift pending activation email link
Append ?activate=1 to success page URL in recipient notification email
for gift purchases with pending_activation status, so the frontend can
distinguish buyer from recipient and show the activate button only to
the recipient.
2026-03-07 06:00:29 +03:00
Fringg c10d6780ba feat: add external squad support for tariffs
- Add external_squad_uuid column to Tariff model with Alembic migration
- Add external_squad_uuid parameter to RemnaWave API create_user/update_user
- Pass external squad from tariff to RemnaWave on subscription creation/update
- Sync external squad in monitoring service, sync service, admin user management
- Clear external squad when tariff has none (consistent across all call sites)
- Add GET /available-external-squads endpoint with UUID validation and response model
- Update tariff schemas with UUID pattern validation
- Fix db.refresh to include tariff relationship for async safety
2026-03-07 05:44:19 +03:00
Fringg 770f19e846 fix: address review findings for guest purchase admin notifications
- Remove double html.escape on payment_method (already escaped in helper)
- Use attribute_names=['landing'] keyword arg in db.refresh for consistency
- Use async with Bot() for guaranteed session cleanup
2026-03-07 04:48:52 +03:00
Fringg dbb9757a3c feat: add admin topic notifications for landing page purchases
- New send_guest_purchase_notification() method in AdminNotificationService
  with blockquote for payment details, landing slug, buyer/recipient info
- Called from fulfill_purchase() for both DELIVERED and PENDING_ACTIVATION
- Called from activate_purchase() when pending purchase is activated
- Different titles: regular purchase, gift purchase, pending activation
- Properly typed GuestPurchase, html.escape on all user data
- Refresh purchase with ['landing'] relationship after commit
- html.escape fallback in _get_payment_method_display
- ruff formatting fixes
2026-03-07 04:44:02 +03:00
Fringg 77456efb75 fix: add X-CSRF-Token and X-Telegram-Init-Data to CORS allow_headers
The security hardening commit changed allow_headers from ['*'] to
['Authorization', 'Content-Type'], but the frontend sends X-CSRF-Token
on all POST/PUT/DELETE/PATCH requests and X-Telegram-Init-Data on all
requests. The missing headers caused preflight OPTIONS requests to fail
with 400 "Disallowed CORS origin".
2026-03-07 04:30:29 +03:00
Fringg c165cca323 fix: use get_rendered_override for proper variable substitution in guest email overrides
Admin-created email template overrides were not substituting {tariff_name},
{period_days}, {cabinet_url} etc. because get_template_override returns raw
body_html. Switched to get_rendered_override which performs variable
substitution with html.escape. Also removed dead is_existing_user from
sample context.
2026-03-07 04:26:09 +03:00
Fringg 6970340e62 feat: add quick purchase email templates to admin panel
- Register 4 guest purchase template types in admin email templates:
  guest_subscription_delivered, guest_activation_required,
  guest_gift_received, guest_cabinet_credentials
- Add sample contexts with placeholders for preview/test
- Add DB override support to send_guest_notification for all 4 types
2026-03-07 04:13:41 +03:00