Commit Graph

25 Commits

Author SHA1 Message Date
c0mrade
574da67ca0 feat(yandex-conv): forward CID through remaining paid API methods
Pair with the bot's #558449 follow-up. Pass getYandexCid() through
the four cabinet API methods that previously omitted it:

  - switchTraffic (PUT /traffic)
  - activateTrial (POST /trial — body added, was previously empty POST)
  - updateCountries (POST /countries)
  - switchTariff (POST /tariff/switch)

Without these the backend now-typed yandex_cid field would silently
stay None for these paid flows even when the user had a CID cached.
2026-05-28 16:43:22 +03:00
c0mrade
1b82280853 feat(yandex-conv): forward CID through subscription purchase requests
Pair-commit with the bot's #558449 fix. On the user's first cabinet
visit, the separate POST /yandex-cid that Metrika init fires may not
finish before the user clicks Buy. Pass the locally-cached CID
directly in every purchase request body — renewSubscription,
purchaseTraffic, purchaseDevices, submitPurchase, purchaseTariff —
so the backend can persist it synchronously and the offline-conversion
event fires reliably.
2026-05-28 15:51:20 +03:00
Fringg
e44a09312e fix(subscription): forward subscription_id to purchaseTariff to fix renew race
Companion to bedolaga-bot a527df23. Bot's purchase-tariff endpoint
now accepts optional subscription_id to resolve the EXACT target row
by ID instead of doing a (user_id, tariff_id) re-lookup that races
with concurrent panel webhooks (produces 'Тариф уже активен' +
refund + no extension).

Frontend changes:
* subscriptionApi.purchaseTariff() — new optional subscriptionId arg
  appended after trafficGb. Forwarded as subscription_id in POST body.
* SubscriptionPurchase.tsx — forwards URL searchParam subscriptionId
  (set when user lands here from 'Renew this subscription').
* SubscriptionCardExpired.tsx — passes subscription.id when renewing
  an expired daily tariff (the .purchaseTariff(tariff_id, 1) call).
2026-05-16 16:10:32 +03:00
Fringg
321c65b68b feat(devices): inline rename UI for connected HWID devices
Pairs with the bot commit that adds the user_device_aliases table.
Surfaces the new local_name field on the existing devices list in
both the user-facing subscription page and the admin user-detail
page, with the same inline-edit pattern in both places.

User-side (src/pages/Subscription.tsx):
- pencil button next to each device row toggles edit mode
- input is focused automatically, capped at 64 chars (matches the
  backend ALIAS_MAX_LENGTH and DB column width)
- Enter saves, Escape cancels, empty input clears the alias
- display priority: local_name → device_model → platform
- works identically in classic / single-tariff / multi-tariff —
  subscriptionId is forwarded as a query param like every other
  device-management endpoint already does

Admin-side (src/pages/AdminUserDetail.tsx):
- same pencil + inline input pattern, admin acts on behalf of the
  user. notify.success on save, loadDevices() refresh.

API (src/api/subscription.ts + src/api/adminUsers.ts):
- new renameDevice(hwid, name, subscriptionId?) on subscriptionApi
- new renameUserDevice(userId, hwid, name) on adminUsersApi
- existing getDevices/getUserDevices contracts widened with
  local_name?: string | null on the returned device shape

Locales (src/locales/ru.json):
- subscription.renameDevice / .renameDeviceSave / .renameDeviceCancel
  / .renameDevicePlaceholder / .deviceRenamed
- admin.users.detail.devices.rename / .renameSave / .renamed
2026-05-16 03:02:45 +03:00
Fringg
f7cc445127 feat: add subscription reissue button with cooldown timer
- Add revokeSubscription API method
- Add reissue button in Additional Options with amber styling,
  destructive confirm dialog, 15-min cooldown with countdown timer,
  localStorage persistence across page reloads
- Add i18n keys in all 4 locales (en, ru, zh, fa)
2026-05-04 17:22:05 +03:00
Dxnil
c80e1aff75 fix(connection): happ cryptolink flow + ui fixes (#385)
* fix(connection): respect happ_cryptolink mode

- read connect_mode from connection-link endpoint
- auto-redirect to happ cryptolink flow when mode is HAPP_CRYPTOLINK
- keep installation guide behavior for other modes

* docs(readme): fix source build step 2

- use compose service name for build/create commands
- copy static files from created compose container
- remove compose container via docker compose rm

* fix(connection): handle mode and ws path

- treat any non-guide connect mode as direct happ redirect
- wait for connection-link response before rendering installation guide
- build websocket URL from VITE_API_URL (supports /api and absolute URLs)

* docs(readme): fix docker cp static path

- copy /usr/share/nginx/html contents via html/.
- prevent nested /srv/cabinet/html deployment and 404 on root

* fix(connection): keep guide, use happ links

- restore guide page behavior for /connection (no auto redirect)
- use happ cryptolink URL in /connection/qr when happ crypt mode is active
- replace subscription page connection URL with happ cryptolink from connection-link API

* fix(connection): avoid wrong redirect flow

- force happ scheme deeplink in HAPP_CRYPTOLINK mode for guide connect buttons
- use cabinet redirect page only inside Telegram Mini App
- open deeplink directly in regular browsers

* fix(connection): enforce happ cryptolink

- prioritize backend crypt deeplink fields in HAPP_CRYPTOLINK mode
- fallback to local crypt4/crypt3 generation from subscription URL when backend returns plain happ://sub...
- apply same resolution for guide open action, qr page source and subscription page link display/copy

* fix(subscription): truncate long connect link

- render connection URL in a single line with ellipsis on /subscriptions
- preserve full URL in tooltip for easier manual copy
- keep copy button behavior unchanged

* fix(connection): build cryptolink from happ sub

- allow cryptolink fallback generation from happ://sub... URLs in addition to http(s)
- prevent plain happ://sub links from leaking into UI in HAPP_CRYPTOLINK mode
2026-04-15 03:29:54 +03:00
Fringg
9546e0ffe6 fix: add missing balance cache invalidation after mutations
- Subscription.tsx: devicePurchase + trafficPurchase onSuccess → invalidate ['balance']
- RenewSubscription.tsx: renewMutation onSuccess → invalidate ['balance']
- SubscriptionPurchase.tsx: purchaseMutation onSuccess → invalidate ['balance'] + ['subscriptions-list']
- subscription.ts: refreshTraffic uses withSubId (already fixed, formatting)
2026-03-26 10:44:00 +03:00
c0mrade
fe75fa45f0 feat: delete expired subscriptions with confirmation dialog
- API: deleteSubscription() calls DELETE /subscriptions/:id
- Subscription detail: "Удалить подписку" button for expired subs
  in multi-tariff mode
- Radix Dialog confirmation with warning text, cancel/confirm buttons
- Navigates to /subscriptions after successful deletion
2026-03-24 15:31:10 +03:00
c0mrade
348d654892 fix: send subscription_id as query param in ALL POST/PATCH endpoints
All backend endpoints accept subscription_id via Query(...) parameter,
but frontend was sending it in the request body via bodyWithSubId helper.
This caused multi-tariff operations (pause, autopay, renew, traffic,
devices, servers, tariff switch) to silently fail — backend couldn't
find subscription_id in query params and fell back to first subscription.

Changed bodyWithSubId to return [body, config] tuple with subscription_id
in params, spread into axios calls as separate arguments.
2026-03-24 12:45:47 +03:00
c0mrade
b0421b94a6 fix: send subscription_id as query param in autopay PATCH, not in body 2026-03-24 12:05:48 +03:00
c0mrade
820ba46cc4 feat: multi-subscription frontend support
- Add SubscriptionListItem and SubscriptionsListResponse types
- Add getSubscriptions/getSubscriptionById API methods
- Add optional subscriptionId parameter to all 30+ API methods
- Create /subscriptions page with subscription list cards
- Add /subscription/:subscriptionId route for per-subscription management
- Dashboard: show multi-tariff navigation block when user has 2+ subscriptions
- Subscription page: read subscriptionId from URL params, pass to all queries
- SubscriptionPurchase: accept subscriptionId from query params
- SubscriptionCardExpired: pass subscription.id to API calls
- Full backward compatibility: without subscriptionId, works as before
2026-03-23 18:57:05 +03:00
c0mrade
2dab25c5a0 fix: resolve telegram auth token expiration and clean up codebase
- Fix stale initData comparison in clearStaleSessionIfNeeded that destroyed
  valid refresh tokens on mobile WebView reopens
- Restrict X-Telegram-Init-Data header to auth endpoints only
- Close Mini App on auth retry to force fresh initData from Telegram
- Merge Connection page error/not-configured states for better UX
- Remove unnecessary comments across 40+ files (section dividers,
  restating comments, noise catch block comments)
- Configure ESLint allowEmptyCatch to properly handle intentional
  empty catch blocks (62 warnings resolved)
2026-03-13 17:50:49 +03:00
Fringg
4a68347ae8 revert: remove user-facing reset traffic toggle
Reset traffic on tariff switch is an admin-only setting.
Backend handles it via admin settings system (data-driven, no frontend code needed).
Reverted all user-facing toggle UI, API params, types, and translations.
2026-02-27 09:50:13 +03:00
Fringg
49fff8e855 feat: add reset traffic toggle on tariff switch
- Add reset_traffic parameter to previewTariffSwitch and switchTariff API calls
- Add reset_traffic_available/reset_traffic_default fields to TariffSwitchPreview type
- Add toggle switch in tariff switch preview modal (shown when backend enables it)
- Initialize toggle from server default (reset_traffic_default)
- Pass reset_traffic flag on switch execution
- Add translations for all 4 locales (ru, en, zh, fa)
2026-02-27 09:48:28 +03:00
Fringg
46b93ef098 fix(subscription): display promo group discounts in device/traffic purchase and tariff switch
- Add discount fields to TrafficPackage and TariffSwitchPreview types
- Show strikethrough original price and discount badge for device purchase
- Show discount badge and original price for traffic packages
- Show discount info and free label for tariff switch with 100% discount
2026-02-05 06:26:27 +03:00
Egor
92905770e4 Update subscription.ts 2026-02-01 17:21:03 +03:00
Egor
b1eea1860c Update subscription.ts 2026-01-31 20:55:41 +03:00
Egor
391c42ec60 Update subscription.ts 2026-01-31 20:48:36 +03:00
Egor
01e3c7b042 Update subscription.ts 2026-01-31 20:31:01 +03:00
Egor
660f2295ba Update subscription.ts 2026-01-29 01:03:46 +03:00
Egor
2722fca22e Update subscription.ts 2026-01-28 20:34:46 +03:00
c0mrade
bc90ba3779 refactor: migrate to eslint flat config and format codebase with prettier
- Remove legacy .eslintrc.cjs and .eslintignore
- Add eslint.config.js with flat config, security rules (no-eval, no-implied-eval, no-new-func, no-script-url)
- Add .prettierrc and .prettierignore
- Format entire codebase with prettier
2026-01-27 17:37:31 +03:00
Egor
fe9e28dbb7 Update subscription.ts 2026-01-20 06:38:25 +03:00
Egor
2dad001591 Update subscription.ts 2026-01-20 05:40:50 +03:00
Egor
7be6b5c0ae Add files via upload 2026-01-15 19:18:17 +03:00