- Create AdminRoleEdit.tsx for /admin/roles/create and /admin/roles/:id/edit
- Create AdminPolicyEdit.tsx for /admin/policies/create and /admin/policies/:id/edit
- Remove create/edit modals from AdminRoles.tsx and AdminPolicies.tsx
- Add new lazy routes in App.tsx with proper permission gates
- Keep delete confirmation as small inline modal (appropriate for destructive action)
- Read policy role_id from top-level field instead of conditions object
- Use policy.role_name for display, remove conditions.role_id lookup
- Remove duplicate role_id injection into conditions payload on submit
- Fix PermissionGate strings: policies:* -> roles:* to match backend
- Remove redundant client-side status filtering in audit log (already
filtered server-side)
- Move RBAC translations from banSystem to admin namespace in all 4 locales
- Fix AccessPolicy type: action (string) -> actions (string[])
- Fix AuditLogEntry type to match backend: add status, request_method, request_path, resource_type, user info
- Fix AuditLogFilters: resource -> resource_type, add status filter
- Handle null details safely in audit log expanded view
- Fix policy form submission and display to use actions array
- Add global settings section (CHANNEL_IS_REQUIRED_SUB, trial/paid toggles)
at top of AdminChannelSubscriptions page
- Add per-channel disable_trial_on_leave and disable_paid_on_leave toggles
in each channel card with inline Toggle controls
- Update RequiredChannel type and API request interfaces with new fields
- Add translations for globalSettings and perChannel in en/ru/fa/zh locales
- Admin panel: added Edit button on channel cards with inline edit form
(title, link, sort order) using existing PATCH API
- Subscription screen: filter out already-subscribed channels so users
only see what they still need to subscribe to
- Updated channelIdHint to remove @username reference (numeric only)
- Added i18n keys: edit, editing, form.save (en/ru/fa/zh)
- Channel subscription blocking screen with per-channel status display
- Safe URL handling with protocol validation (no open redirect)
- Race-condition-safe subscription check with useRef guard
- Admin channel management page with create, toggle, delete
- Error handlers on all admin mutations with haptic feedback
- Proper type guard for channel subscription error detection
- Accessibility: aria-labels on icon-only buttons
- Localization: en, ru, zh, fa translations
memory.used from systeminformation includes disk cache/buffers,
showing ~91% when actual usage is ~18%. Now uses (total - available)
which matches htop/free output. Also added PB/EB units to formatBytes.
The old clearStaleSessionIfNeeded stored initData in sessionStorage for
comparison, but sessionStorage is cleared on tab close — so switching
Telegram accounts between separate Mini App openings was never detected.
Fix: store the Telegram user ID in localStorage (survives tab close).
On every launch, compare the fresh initData user ID with the stored one.
If they differ, clear all auth tokens and cached user data to force
re-authentication as the correct account.
Replace all bare useAuthStore(), useBlockingStore(), and
useSuccessNotification() calls with individual field selectors.
This prevents components from re-rendering when unrelated store
fields change.
- 21 useAuthStore usages across 20 files: individual selectors
for 1-2 fields, useShallow for 3+ fields
- 5 useBlockingStore usages: individual selectors
- 2 useSuccessNotification usages: individual selectors
- Remove isFullscreen from useBranding useEffect deps to prevent
re-render loop (requestFullscreen changes isFullscreen which
re-triggers the effect); use ref guard instead
- Make AdminBroadcasts 5s polling conditional: only poll when there
are active broadcasts (queued/in_progress/cancelling)
- Add URL.revokeObjectURL on unmount and media removal in
AdminBroadcastCreate, AdminPinnedMessageCreate, Support
- Track blob URLs via refs to revoke before creating new ones
- Add clearCreateAttachment/clearReplyAttachment helpers in Support
to properly revoke blob URLs when clearing attachments
- Cap adminTraffic cache at 20 entries with FIFO eviction to prevent
unbounded memory growth from different filter combinations
Prevent MutationObserver re-initialization on every render by moving
the options object out of JSX (was creating a new object reference
on each render of AppWithNavigator).
- Remove mix-blend-mode: overlay from body::before noise texture
(was forcing fullscreen GPU compositing on every paint)
- Disable noise texture on mobile via display:none media query
- Replace backdrop-filter animation in backdropFadeIn/Out keyframes
with opacity-only animation (backdrop-filter transition is expensive)
- Replace toast progress bar width animation with scaleX (GPU-composited)
- Remove redundant will-change: transform from .glass and .wave-blob
- Fix isWebglAvailable() leaking a full WebGL context on every check
by replacing OGL Renderer with raw canvas.getContext() + loseContext()
- Add visibilitychange listener to fully stop rAF loop when tab hidden
and restart on visibility restore (was running 24/7 in background)
- Properly destroy WebGL context on unmount via WEBGL_lose_context
extension and gl.deleteProgram() (was only nulling JS refs)
- Reduce target FPS from 20 to 10 (slow gradient, visually identical)
Previous fix used URLSearchParams.get() which already fully decodes,
then called decodeURIComponent() again causing double-decode of %
sequences. Parse raw query string pairs directly instead —
decodeURIComponent does not treat + as space, preserving base64.
URLSearchParams decodes + as space, breaking base64-encoded crypto
deep links (ss://, vless://, etc). Use raw param extraction with
manual decoding to preserve + characters.
Redesign applyPromoDiscount to apply promo offer on top of promo group
prices instead of treating them as mutually exclusive. Calculate combined
discount percent from the deepest original price. Show promo banner in
confirm step regardless of existing server discounts.
Add 60-second cooldown timer on resend verification email button to
prevent spam. Remove auth_type restriction on change email button so
OAuth users can also change their email.
Mirrors campaign.ts localStorage pattern to capture ?ref= from URL and pass
referral_code to backend during Telegram Widget, OAuth, email login, and
Mini App authentication. Fixes redirect loop when email auth is disabled.
servers_count already represents squad count on backend.
Renamed display labels to "locations" on tariff cards and
confirmation view. Server names still shown in subscription card.