156 Commits

Author SHA1 Message Date
Fringg
f300e07ce2 chore: ruff format 2026-02-25 06:34:22 +03:00
Fringg
bc7d0612f1 fix: specify foreign_keys on User.admin_roles_rel to resolve ambiguous join
UserRole has two FKs to users (user_id and assigned_by), causing
SQLAlchemy AmbiguousForeignKeysError on mapper initialization.
2026-02-25 03:23:41 +03:00
Fringg
3fee54f657 feat: add RBAC + ABAC permission system for admin cabinet
Backend:
- 4 new models: AdminRole, UserRole, AccessPolicy, AdminAuditLog
- Permission engine with RBAC wildcard matching + ABAC policy evaluation
- 26 permission sections (78 unique permissions) covering all admin routes
- require_permission() FastAPI dependency for route-level access control
- JWT tokens carry permissions, roles, role_level for frontend checks
- Admin roles CRUD with level-based hierarchy (viewers → superadmin)
- ABAC policies with time ranges and IP whitelist conditions
- Full audit log with CSV export
- Bootstrap service seeds 5 preset roles and assigns superadmins at startup
- Alembic migration 0011 for all RBAC tables
2026-02-25 03:02:40 +03:00
Fringg
3642462670 feat: add per-channel disable settings and fix CHANNEL_REQUIRED_FOR_ALL bug
- Fix critical bug: is_active_paid_subscription() guard was blocking
  CHANNEL_REQUIRED_FOR_ALL from disabling paid subscriptions
- Add disable_trial_on_leave and disable_paid_on_leave columns to
  RequiredChannel model with Alembic migration 0010
- Refactor enforcement logic in channel_member.py and channel_checker.py
  to use per-channel settings instead of global env vars
- Update CRUD, Pydantic schemas, and admin API routes for new fields
- Add should_disable_subscription() and get_channel_settings() to
  channel_subscription_service for per-channel decision logic
2026-02-25 00:24:31 +03:00
Fringg
a7db469fd7 fix: remove @username channel ID input, auto-prefix -100 for bare digits
@username resolution via bot.get_chat() was unreliable for subscription
checking. Now only numeric channel IDs are accepted with automatic -100
prefix when entering bare digits (e.g. 1234567890 -> -1001234567890).
2026-02-24 03:06:57 +03:00
Fringg
8375d7ecc5 feat: add multi-channel mandatory subscription system
- Multi-channel subscription enforcement via middleware, events, and cabinet API
- 3-layer cache architecture: Redis -> PostgreSQL -> rate-limited Telegram API
- ChatMemberUpdated event-driven tracking with automatic VPN access control
- Admin management via bot FSM handler and REST API with full CRUD
- Channel ID normalization: @username resolved to numeric ID at creation time
- Fail-closed error handling: API errors deny access (security-first)
- Background reconciliation with keyset pagination (100 per batch)
- Per-user rate limiting on subscription check button (5s cooldown)
- Redis connection pooling via cache singleton (no per-request connections)
- Database: channel_id index, multi-row upsert optimization
- Localization: en, ru, zh, fa, ua translations for all new strings
- Frontend blocking UI with channel list and subscription status
- Admin channel management page with toggle, delete, and create
2026-02-24 02:50:31 +03:00
Fringg
a7f3d652c5 fix: use AwareDateTime TypeDecorator for all datetime columns
TypeDecorator with process_result_value guarantees naive datetimes
from pre-TIMESTAMPTZ databases are converted to UTC-aware on every
load. Replaces unreliable event listener approach. All 175 DateTime
columns now use AwareDateTime.
2026-02-18 11:11:58 +03:00
Fringg
f7d33a7d2b fix: auto-convert naive datetimes to UTC-aware on model load
SQLAlchemy event listener on Base ensures all DateTime columns are
timezone-aware after loading from DB. Fixes TypeError crashes in
50+ comparison sites across handlers, services, and middlewares
for pre-TIMESTAMPTZ databases.
2026-02-18 11:01:04 +03:00
Fringg
bd11801467 fix: extend naive datetime guard to all model properties
Move _aware() to module level and apply to 4 more models:
- PromoCode.is_valid (valid_from, valid_until)
- TrafficPurchase.is_expired (expires_at)
- CabinetRefreshToken.is_expired (expires_at)
- Ticket.is_user_reply_blocked (user_reply_block_until)
2026-02-18 10:44:13 +03:00
Fringg
e512e5fe6e fix: handle naive datetimes in Subscription properties
Databases that haven't run the TIMESTAMPTZ migration return naive
datetimes from end_date. Comparing with datetime.now(UTC) raises
TypeError. Added _aware() helper to normalize naive→aware in
is_active, is_expired, should_be_expired, actual_status, days_left,
time_left_display, and extend_subscription.
2026-02-18 10:36:46 +03:00
Fringg
68499ee043 chore: ruff format 2026-02-18 09:51:56 +03:00
Fringg
0c07812ecc feat: add campaign_id to ReferralEarning for campaign attribution
Adds nullable FK campaign_id to referral_earnings table, enabling
direct campaign ROI analytics without JOINing through registrations.

- Model: campaign_id column + AdvertisingCampaign relationship
- CRUD: get_user_campaign_id() helper, campaign_id param in create_referral_earning
- Service: resolve campaign_id in all earning creation paths
- Cabinet API: campaign_name in earnings response
- Migration 0002: add column + deterministic backfill via DISTINCT ON
2026-02-18 09:12:01 +03:00
Fringg
10e231e52e feat: blocked user detection during broadcasts, filter blocked from all notifications
- Broadcast tri-state return: 'sent'/'blocked'/'failed' with blocked_count tracking
- Background cleanup: mark blocked users + disable their subscriptions + Remnawave
- blocked_count in BroadcastHistory model, schemas, API responses, admin UI
- Filter User.status==ACTIVE in subscription queries: get_expiring_subscriptions,
  get_expired_subscriptions, get_subscriptions_for_autopay,
  get_daily_subscriptions_for_charge, get_disabled_daily_subscriptions_for_resume
- Guard in notification_delivery_service.send_notification for BLOCKED/DELETED users
- Fix subscription tariff switch: preserve remaining days with total_seconds()
- Fix redundant local UTC imports across 16 files
- Fix test mocks: add **kwargs, correct assertion, remove dead expression
2026-02-17 18:37:25 +03:00
Fringg
88997492c3 fix: critical security and data integrity fixes for partner system
- Add SELECT FOR UPDATE locking on all financial state transitions
  (withdrawal approve/reject/complete/create, partner approve/reject)
- Add html.escape() on all user-controlled values in email templates
- Wrap sync SMTP send_email in asyncio.to_thread to avoid blocking event loop
- Add missing database indexes on referral_earnings(user_id, referral_id),
  users(referred_by_id, partner_status), withdrawal_requests(user_id, status),
  advertising_campaigns(partner_user_id)
2026-02-17 12:28:30 +03:00
Fringg
acc1323a54 fix: move PartnerStatus enum before User class to fix NameError
PartnerStatus was defined after the User class that references it,
causing a NameError on startup.
2026-02-17 09:56:11 +03:00
Fringg
58bfaeaddb feat: add partner system and withdrawal management to cabinet
- Partner application flow: user applies, admin reviews/approves/rejects
- Individual commission % per partner with admin management
- Campaign assignment/unassignment to partners
- Withdrawal system: balance check, create request, cancel
- Admin withdrawal management with risk scoring and fraud analysis
- Database migration: partner_applications table, user partner fields, campaign partner_user_id
- Pydantic schemas with proper validation bounds
- Batch user fetching to prevent N+1 queries
- Row locking on cancel to prevent race conditions
2026-02-17 09:51:36 +03:00
Fringg
eb18994b7d fix: complete datetime.utcnow() → datetime.now(UTC) migration
- Migrate 660+ datetime.utcnow() across 153 files to datetime.now(UTC)
- Migrate 30+ datetime.now() without UTC to datetime.now(UTC)
- Convert all 170 DateTime columns to DateTime(timezone=True)
- Add migrate_datetime_to_timestamptz() in universal_migration with SET LOCAL timezone='UTC' safety
- Remove 70+ .replace(tzinfo=None) workarounds
- Fix utcfromtimestamp → fromtimestamp(..., tz=UTC)
- Fix fromtimestamp() without tz= (system_logs, backup_service, referral_diagnostics)
- Fix fromisoformat/isoparse to ensure aware output (platega, yookassa, wata, miniapp, nalogo)
- Fix strptime() to add .replace(tzinfo=UTC) (backup_service, referral_diagnostics)
- Fix datetime.combine() to include tzinfo=UTC (remnawave_sync, traffic_monitoring)
- Fix datetime.max/datetime.min sentinels with .replace(tzinfo=UTC)
- Rename panel_datetime_to_naive_utc → panel_datetime_to_utc
- Remove DTZ003 from ruff ignore list
2026-02-17 04:45:40 +03:00
Fringg
4048aebb9f chore: format models.py 2026-02-12 21:08:05 +03:00
Fringg
bfd66c42c1 fix: add passive_deletes to Subscription relationships to prevent NOT NULL violation on cascade delete 2026-02-12 20:59:28 +03:00
Fringg
e94b93d0c1 fix: handle nullable traffic_limit_gb and end_date in subscription model
Add None-safety guards to Subscription model properties (is_active,
is_expired, should_be_expired, actual_status, days_left,
traffic_used_percent) and pricing handler comparisons to prevent
TypeError when nullable columns contain None values.
2026-02-10 20:35:42 +03:00
Fringg
184c52d4ea feat: webhook protection — prevent sync/monitoring from overwriting webhook data
Add last_webhook_update_at timestamp to Subscription model. When a webhook
handler modifies a subscription, it stamps this field. Auto-sync, monitoring,
and force-check services skip subscriptions updated by webhook within the
last 60 seconds, preventing stale panel data from overwriting fresh
real-time changes.

- Add last_webhook_update_at column + migration
- Stamp all 8 webhook handlers with commit in every code path
- Add is_recently_updated_by_webhook() guard in 12 sync/monitoring paths
- Add REMNAWAVE_WEBHOOK_* variables to .env.example
- Add webhook setup documentation to README with Caddy/nginx examples
- Fix pre-existing yookassa webhook test (mock AsyncSessionLocal)
2026-02-10 07:16:22 +03:00
Fringg
90d9df8f0e fix: preserve payment initiation time in transaction created_at
Transaction created_at and completed_at showed identical timestamps
because webhook handlers created transactions with is_completed=True
in a single step. Now all 10 payment providers pass payment.created_at
to the transaction so created_at reflects when the user initiated
the payment, not when the webhook processed it.

Also: remove duplicate datetime import in inline.py, upgrade button
stats DB error logging from debug to warning, add index on
button_click_logs.button_type for analytics queries.
2026-02-10 04:26:23 +03:00
Fringg
97be4afbff feat: add OAuth 2.0 authorization (Google, Yandex, Discord, VK)
- Add OAuth provider config vars and helpers to config.py
- Add google_id, yandex_id, discord_id, vk_id columns to User model
- Create OAuth provider service with state management and 4 providers
- Add CRUD functions for OAuth user lookup, linking, and creation
- Add 3 API endpoints: providers list, authorize URL, callback
- Add alembic migration and universal_migration support
- Fix trial disable logic to cover OAuth auth_types
2026-02-07 01:58:55 +03:00
Egor
4e9f01d439 Add files via upload 2026-01-31 17:45:19 +03:00
Egor
9afe370a98 Add files via upload 2026-01-30 16:05:36 +03:00
gy9vin
dd5ee45ab5 Keep local customs over main 2026-01-27 23:51:07 +03:00
gy9vin
95b7152c05 касса и прочее 2026-01-27 23:47:39 +03:00
Egor
441bc44a24 Add files via upload 2026-01-26 22:36:48 +03:00
Egor
658b48f154 Merge pull request #2412 from BEDOLAGA-DEV/email
Email
2026-01-25 11:55:22 +03:00
c0mrade
9a2aea038a chore: add uv package manager and ruff linter configuration
- Add pyproject.toml with uv and ruff configuration
- Pin Python version to 3.13 via .python-version
- Add Makefile commands: lint, format, fix
- Apply ruff formatting to entire codebase
- Remove unused imports (base64 in yookassa/simple_subscription)
- Update .gitignore for new config files
2026-01-24 17:45:27 +03:00
Egor
c27128aa30 Update models.py 2026-01-24 11:12:42 +03:00
Egor
0a0fc48463 Add files via upload 2026-01-23 11:23:51 +03:00
gy9vin
7aa64521d2 feat(payments): добавить KassaAI как отдельную платёжную систему
Новая платёжка KassaAI (api.fk.life) работает параллельно с Freekassa.

  Добавлено:
  - app/services/kassa_ai_service.py — API-сервис
  - app/database/crud/kassa_ai.py — CRUD-операции
  - app/services/payment/kassa_ai.py — KassaAiPaymentMixin
  - app/handlers/balance/kassa_ai.py — хендлеры пополнения

  Изменено:
  - config.py — настройки KASSA_AI_*
  - models.py — PaymentMethod.KASSA_AI, модель KassaAiPayment
  - payment_service.py — подключён KassaAiPaymentMixin
  - webserver/payments.py — webhook /kassa-ai-webhook
  - keyboards/inline.py — кнопка KassaAI
  - handlers/balance/main.py — регистрация хендлеров
  - universal_migration.py — миграция таблицы kassa_ai_payments
  - system_settings_service.py — настройки в админке
  - .env.example — примеры переменных

  Способы оплаты: 44=СБП, 36=Карты РФ, 43=SberPay
2026-01-20 19:09:27 +03:00
PEDZEO
67c3dba1cc feat(notifications): implement ticket notifications for users and admins
- Added a new TicketNotification model to handle notifications for ticket events.
- Implemented user and admin notifications for new tickets and replies in the cabinet.
- Introduced settings to enable or disable notifications for users and admins.
- Enhanced ticket settings to include notification preferences.
- Integrated WebSocket notifications for real-time updates.
2026-01-19 00:02:41 +03:00
libkit
ff45a3e28d feat(models): добавить тип DISCOUNT в PromoCodeType
Добавлен новый тип промокода для одноразовых скидок.
Использует существующие поля без изменения схемы БД:
- balance_bonus_kopeks для хранения процента скидки (1-100)
- subscription_days для хранения срока действия скидки в часах (0-8760)
2026-01-17 11:17:30 +05:00
Egor
c4cde5efa2 Add files via upload 2026-01-17 05:00:47 +03:00
Egor
1024a88d19 Add files via upload 2026-01-16 08:34:56 +03:00
Egor
a32e3fc582 Add files via upload 2026-01-16 06:01:23 +03:00
Egor
13770a01c6 Add files via upload 2026-01-15 17:31:15 +03:00
Egor
7f65d398bf Update models.py 2026-01-15 16:56:19 +03:00
PEDZEO
a686333603 Add support for custom days and traffic in tariffs
- Introduced fields for custom days and traffic in the tariff model, including enabling flags, pricing, and limits.
- Updated relevant routes and schemas to handle new tariff features.
- Implemented logic for purchasing and managing custom days and traffic in subscriptions.
- Added database migration scripts to accommodate new columns for tariffs and subscriptions.
2026-01-13 02:55:32 +03:00
PEDZEO
ac5850746e Merge pull request #2271 from BEDOLAGA-DEV/main
merge main
2026-01-12 22:25:49 +03:00
Egor
9e4fa9defe Add files via upload 2026-01-12 15:24:59 +03:00
PEDZEO
15f6108674 feat(tariffs): добавлена поддержка докупки трафика и улучшения тарифов
- Реализована возможность докупки трафика для тарифов с новыми параметрами: traffic_topup_enabled, traffic_topup_packages и max_topup_traffic_gb.
- Обновлены схемы и маршруты для управления тарифами и трафиком.
- Добавлены новые эндпоинты для работы с докупкой трафика в мини-приложении.
- Обновлены настройки и логика для проверки доступности докупки трафика в зависимости от тарифа.
- Внедрены улучшения в обработку платежей через Freekassa.

Обновлён .env.example с новыми параметрами для режима тарифов.
2026-01-12 07:47:35 +03:00
PEDZEO
0e24a5505c feat(subscription): добавлены новые функции для управления тарифами и трафиком
- Обновлены схемы и маршруты для поддержки покупки тарифов и управления трафиком.
- Реализована синхронизация тарифов и серверов из RemnaWave при запуске.
- Добавлены новые параметры в тарифы: server_traffic_limits и allow_traffic_topup.
- Обновлены настройки и логика для проверки доступности докупки трафика в зависимости от тарифа.
- Внедрены новые эндпоинты для работы с колесом удачи и обработка платежей через Stars.

Обновлён .env.example с новыми параметрами для режима продаж подписок.
2026-01-12 07:41:10 +03:00
Egor
c58faff2df Update models.py 2026-01-11 02:56:14 +03:00
Egor
90d090a048 Update models.py 2026-01-10 20:30:30 +03:00
gy9vin
3299d47b11 merge: resolve conflict in universal_migration.py 2026-01-07 15:05:14 +03:00
gy9vin
4afefcafa4 Добавлена система вывода реферального баланса
Новая функциональность вывода средств:
  - config.py: добавлены настройки вывода (минимальная сумма, кулдаун, анализ подозрительности, тестовый режим)
  - models.py: добавлена модель WithdrawalRequest с полями для заявок, анализа рисков и обработки админ
2026-01-07 14:54:50 +03:00
Egor
e32bf5f2c4 Add files via upload 2026-01-07 02:54:47 +03:00