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
This commit is contained in:
Dxnil
2026-04-15 03:29:54 +03:00
committed by GitHub
parent 32dba93f1a
commit c80e1aff75
6 changed files with 198 additions and 36 deletions

View File

@@ -8,6 +8,31 @@ export type { WSMessage } from './WebSocketContext';
const isDev = import.meta.env.DEV;
function buildWebSocketUrl(accessToken: string): string {
const apiUrl = String(import.meta.env.VITE_API_URL || '/api').trim();
const windowWsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const withToken = (base: string) => `${base}?token=${encodeURIComponent(accessToken)}`;
if (apiUrl.startsWith('http://') || apiUrl.startsWith('https://')) {
try {
const api = new URL(apiUrl);
const wsProtocol = api.protocol === 'https:' ? 'wss:' : 'ws:';
const basePath = api.pathname.replace(/\/+$/, '');
const wsPath = basePath.endsWith('/cabinet') ? `${basePath}/ws` : `${basePath}/cabinet/ws`;
return withToken(`${wsProtocol}//${api.host}${wsPath}`);
} catch {
// fall through to relative-path handling
}
}
const normalizedBasePath = `/${apiUrl}`.replace(/\/{2,}/g, '/').replace(/\/+$/, '');
const wsPath = normalizedBasePath.endsWith('/cabinet')
? `${normalizedBasePath}/ws`
: `${normalizedBasePath}/cabinet/ws`;
return withToken(`${windowWsProtocol}//${window.location.host}${wsPath}`);
}
export function WebSocketProvider({ children }: { children: React.ReactNode }) {
const accessToken = useAuthStore((state) => state.accessToken);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
@@ -48,21 +73,7 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
cleanup();
// Build WebSocket URL
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
let host = window.location.host;
// Handle VITE_API_URL - can be absolute URL or relative path
const apiUrl = import.meta.env.VITE_API_URL;
if (apiUrl && (apiUrl.startsWith('http://') || apiUrl.startsWith('https://'))) {
try {
host = new URL(apiUrl).host;
} catch {
// If URL parsing fails, use window.location.host
}
}
const wsUrl = `${protocol}//${host}/cabinet/ws?token=${accessToken}`;
const wsUrl = buildWebSocketUrl(accessToken);
try {
const ws = new WebSocket(wsUrl);