diff --git a/proxy.conf.json b/proxy.conf.json
index fcdce66..26f4cf2 100644
--- a/proxy.conf.json
+++ b/proxy.conf.json
@@ -19,5 +19,12 @@
"changeOrigin": true,
"pathRewrite": { "^/proxy/api-vitanova": "" },
"logLevel": "debug"
+ },
+ "/proxy/users-vitanova": {
+ "target": "https://users.vitanova.network:456",
+ "secure": true,
+ "changeOrigin": true,
+ "pathRewrite": { "^/proxy/users-vitanova": "" },
+ "logLevel": "debug"
}
}
diff --git a/src/app/api.ts b/src/app/api.ts
index 7d4cc78..94a5220 100644
--- a/src/app/api.ts
+++ b/src/app/api.ts
@@ -10,6 +10,14 @@ export const FASTCHECK_API = isDevMode()
? '/proxy/fastcheck-store/api'
: 'https://fastcheck.store/api';
+/**
+ * Base URL for Telegram/websession authorization.
+ * Auth routes: POST /users/sessions, GET/DELETE /users/sessions/:webSessionID.
+ */
+export const USERS_VITANOVA_API = isDevMode()
+ ? '/proxy/users-vitanova'
+ : 'https://users.vitanova.network:456';
+
/**
* Base URL for the QR/SBP backend (qr.vitanova.network).
* Used for the /api/settings endpoint that may return active check data on load.
diff --git a/src/app/auth-dialog/auth-dialog.html b/src/app/auth-dialog/auth-dialog.html
index 56ed28c..f8f4203 100644
--- a/src/app/auth-dialog/auth-dialog.html
+++ b/src/app/auth-dialog/auth-dialog.html
@@ -30,6 +30,10 @@
{{ 'auth.telegram_btn' | translate }}
+
+ {{ telegramLink() }}
+
+
{{ 'auth.qr_hint' | translate }}
diff --git a/src/app/auth-dialog/auth-dialog.scss b/src/app/auth-dialog/auth-dialog.scss
index 578eeb8..002dbda 100644
--- a/src/app/auth-dialog/auth-dialog.scss
+++ b/src/app/auth-dialog/auth-dialog.scss
@@ -118,6 +118,20 @@ h2 {
flex-shrink: 0;
}
+.telegram-link {
+ display: block;
+ margin-top: 10px;
+ color: var(--telegram-hover);
+ font-size: 12px;
+ line-height: 1.4;
+ overflow-wrap: anywhere;
+ text-decoration: none;
+}
+
+.telegram-link:hover {
+ text-decoration: underline;
+}
+
.qr-section {
margin-top: 20px;
}
diff --git a/src/app/auth-dialog/auth-dialog.ts b/src/app/auth-dialog/auth-dialog.ts
index 735819b..ad40147 100644
--- a/src/app/auth-dialog/auth-dialog.ts
+++ b/src/app/auth-dialog/auth-dialog.ts
@@ -1,14 +1,20 @@
import { HttpClient } from '@angular/common/http';
import { Component, computed, effect, inject, input, output, signal } from '@angular/core';
-import { FASTCHECK_API } from '../api';
+import { USERS_VITANOVA_API } from '../api';
import { TranslatePipe } from '../translate/translate.pipe';
interface WebSessionResponse {
- sessionId: string;
- userId: string;
- expires: string;
- userSessionId: string;
- Status: boolean;
+ sessionId?: string;
+ webSessionID?: string;
+ webSessionId?: string;
+ userId?: string;
+ userID?: string;
+ telegramID?: string;
+ expires?: string;
+ userSessionId?: string;
+ userSessionID?: string;
+ Status?: boolean;
+ status?: boolean | string;
}
export type AuthDialogMode = 'payment' | 'login' | 'new';
@@ -31,7 +37,6 @@ export class AuthDialogComponent {
private readonly http = inject(HttpClient);
private readonly telegramBot = 'myAMLKYCBOT';
private readonly sessionStorageKey = 'fc_session';
- private readonly maxPollAttempts = 100;
open = input(false);
mode = input
('login');
@@ -61,7 +66,6 @@ export class AuthDialogComponent {
}
private pollHandle: ReturnType | null = null;
- private pollAttempts = 0;
private wasOpen = false;
private authenticated = false;
@@ -106,7 +110,6 @@ export class AuthDialogComponent {
private startAuthFlow(): void {
this.stopPolling();
this.authenticated = false;
- this.pollAttempts = 0;
this.messageKey.set('');
this.webSessionId.set('');
this.state.set('checking');
@@ -121,9 +124,9 @@ export class AuthDialogComponent {
}
private checkExistingSession(sessionId: string): void {
- this.http.get(`${FASTCHECK_API}/websession/${sessionId}`).subscribe({
+ this.http.get(`${USERS_VITANOVA_API}/users/sessions/${sessionId}`).subscribe({
next: (response) => {
- if (response?.Status) {
+ if (this.isAuthorized(response)) {
this.webSessionId.set(sessionId);
this.handleAuthorized(response, sessionId);
return;
@@ -141,26 +144,30 @@ export class AuthDialogComponent {
private createSession(): void {
this.state.set('loading');
- this.http.get(`${FASTCHECK_API}/websession`).subscribe({
+ const sessionId = this.createGuid();
+ this.webSessionId.set(sessionId);
+
+ this.http.post(`${USERS_VITANOVA_API}/users/sessions`, {
+ webSessionID: sessionId,
+ sessionId
+ }).subscribe({
next: (response) => {
- const sessionId = response?.sessionId ?? '';
- if (!sessionId) {
+ const responseSessionId = this.getSessionId(response) || sessionId;
+ if (!responseSessionId) {
this.messageKey.set('auth.session_failed');
this.state.set('error');
return;
}
- this.webSessionId.set(sessionId);
+ this.webSessionId.set(responseSessionId);
- if (this.isMobile) {
- this.state.set('checking');
- window.location.href = this.telegramLink();
- this.startPolling(sessionId);
+ if (this.isAuthorized(response)) {
+ this.handleAuthorized(response, responseSessionId);
return;
}
this.state.set('ready');
- this.startPolling(sessionId);
+ this.startPolling(responseSessionId);
},
error: () => {
this.messageKey.set('auth.session_failed');
@@ -171,19 +178,10 @@ export class AuthDialogComponent {
private startPolling(sessionId: string): void {
this.stopPolling();
- this.pollAttempts = 0;
this.pollHandle = setInterval(() => {
- this.pollAttempts += 1;
- if (this.pollAttempts >= this.maxPollAttempts) {
- this.stopPolling();
- this.messageKey.set('auth.expired');
- this.state.set('expired');
- return;
- }
-
- this.http.get(`${FASTCHECK_API}/websession/${sessionId}`).subscribe({
+ this.http.get(`${USERS_VITANOVA_API}/users/sessions/${sessionId}`).subscribe({
next: (response) => {
- if (!response?.Status) return;
+ if (!this.isAuthorized(response)) return;
this.handleAuthorized(response, sessionId);
},
error: () => undefined
@@ -200,8 +198,8 @@ export class AuthDialogComponent {
this.state.set('checking');
this.authorized.emit({
sessionId,
- userId: response.userId ?? '',
- userSessionId: response.userSessionId ?? ''
+ userId: response.userId ?? response.userID ?? response.telegramID ?? '',
+ userSessionId: response.userSessionId ?? response.userSessionID ?? ''
});
}
@@ -212,7 +210,6 @@ export class AuthDialogComponent {
this.webSessionId.set('');
this.state.set('loading');
this.authenticated = false;
- this.pollAttempts = 0;
}
private cleanupSession(persistSession: boolean): void {
@@ -232,14 +229,33 @@ export class AuthDialogComponent {
}
this.http
- .request('DELETE', `${FASTCHECK_API}/websession/${sessionId}`, {
- body: { sessionId }
- })
+ .delete(`${USERS_VITANOVA_API}/users/sessions/${sessionId}`)
.subscribe({ error: () => undefined });
localStorage.removeItem(this.sessionStorageKey);
}
+ private getSessionId(response: WebSessionResponse | null | undefined): string {
+ return response?.webSessionID ?? response?.webSessionId ?? response?.sessionId ?? '';
+ }
+
+ private isAuthorized(response: WebSessionResponse | null | undefined): boolean {
+ const status = response?.Status ?? response?.status;
+ return status === true || String(status).toLowerCase() === 'true';
+ }
+
+ private createGuid(): string {
+ if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
+ return crypto.randomUUID();
+ }
+
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
+ const randomValue = Math.floor(Math.random() * 16);
+ const value = char === 'x' ? randomValue : (randomValue & 0x3) | 0x8;
+ return value.toString(16);
+ });
+ }
+
private stopPolling(): void {
if (this.pollHandle === null) return;
clearInterval(this.pollHandle);