Files
vitanovaPackages/dist/telegram/auth.service.js
2026-08-18 01:55:26 +04:00

162 lines
6.3 KiB
JavaScript

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Injectable, signal, computed, inject } from '@angular/core';
import { tap } from 'rxjs';
import { TelegramSessionApiService } from './telegram-session-api.service';
const WEB_SESSION_COOKIE = 'webSessionID';
const WEB_SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60;
/** Customer-facing Telegram QR/session auth. Distinct storage/state from AdminAuthService by design. */
let AuthService = class AuthService {
constructor() {
this.api = inject(TelegramSessionApiService);
this.sessionSignal = signal(null);
this.statusSignal = signal('unknown');
this.showLoginSignal = signal(false);
/** Current auth session */
this.session = this.sessionSignal.asReadonly();
/** Current auth status */
this.status = this.statusSignal.asReadonly();
/** Whether user is fully authenticated */
this.isAuthenticated = computed(() => this.statusSignal() === 'authenticated');
/** Whether to show login dialog */
this.showLoginDialog = this.showLoginSignal.asReadonly();
/** Display name of authenticated user */
this.displayName = computed(() => this.sessionSignal()?.displayName ?? null);
// On init, check existing session via cookie
this.checkSession();
}
/** Check the current webSessionID cookie against the auth backend. */
checkSession() {
const webSessionID = this.getStoredWebSessionID();
if (!webSessionID) {
this.clearAuthState('unauthenticated');
return;
}
this.statusSignal.set('checking');
this.checkSessionOnce(webSessionID).subscribe(session => {
if (!session?.active) {
this.clearAuthState('unauthenticated');
}
});
}
/** Check session without updating internal state beyond activating on success (used for polling). */
checkSessionOnce(webSessionID = this.getStoredWebSessionID()) {
return this.api.checkSessionOnce(webSessionID).pipe(tap(session => {
if (session?.active) {
this.activateSession(session);
}
}));
}
/**
* Called after user completes Telegram login.
*/
onTelegramLoginComplete() {
this.hideLogin();
if (!this.isAuthenticated()) {
this.checkSession();
}
}
/** Generate the Telegram login URL for bot-based auth */
getTelegramLoginUrl(webSessionID) {
return this.api.getBotLoginUrl(webSessionID);
}
/** Generate a Telegram app deep link for mobile login without opening a browser tab. */
getTelegramAppLoginUrl(webSessionID) {
return this.api.getBotAppLoginUrl(webSessionID);
}
/** Create a backend web session and return the Telegram start link for it. */
createWebSession() {
return this.api.createSession();
}
/** Show login dialog (called when user tries to pay without being logged in) */
requestLogin() {
this.showLoginSignal.set(true);
}
/** Hide login dialog */
hideLogin() {
this.showLoginSignal.set(false);
}
/** Logout — clears session on backend and locally */
logout() {
const webSessionID = this.sessionSignal()?.sessionId || this.getStoredWebSessionID();
if (!webSessionID) {
this.clearAuthState('unauthenticated');
return;
}
this.api.logout(webSessionID).subscribe(() => {
this.clearAuthState('unauthenticated');
});
}
activateSession(session) {
this.sessionSignal.set(session);
this.statusSignal.set('authenticated');
this.setStoredWebSessionID(session.sessionId);
this.scheduleSessionRefresh(session.expires);
}
clearAuthState(status) {
this.sessionSignal.set(null);
this.statusSignal.set(status);
this.clearStoredWebSessionID();
this.clearSessionRefresh();
}
/** Schedule a session re-check before it expires */
scheduleSessionRefresh(expiresAt) {
this.clearSessionRefresh();
const expiresMs = new Date(expiresAt).getTime();
const nowMs = Date.now();
// Re-check 60 seconds before expiry, minimum 30s from now
const refreshIn = Number.isFinite(expiresMs)
? Math.max(expiresMs - nowMs - 60_000, 30_000)
: WEB_SESSION_COOKIE_MAX_AGE_SECONDS * 1000;
this.sessionCheckTimer = setTimeout(() => {
this.checkSession();
}, refreshIn);
}
clearSessionRefresh() {
if (this.sessionCheckTimer) {
clearTimeout(this.sessionCheckTimer);
this.sessionCheckTimer = undefined;
}
}
getStoredWebSessionID() {
if (typeof document === 'undefined') {
return null;
}
const cookie = document.cookie
.split('; ')
.find(row => row.startsWith(`${WEB_SESSION_COOKIE}=`));
if (!cookie) {
return null;
}
try {
return decodeURIComponent(cookie.substring(WEB_SESSION_COOKIE.length + 1));
}
catch {
return null;
}
}
setStoredWebSessionID(webSessionID) {
if (typeof document === 'undefined') {
return;
}
const secure = typeof window !== 'undefined' && window.location.protocol === 'https:' ? '; Secure' : '';
document.cookie = `${WEB_SESSION_COOKIE}=${encodeURIComponent(webSessionID)}; Max-Age=${WEB_SESSION_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Lax${secure}`;
}
clearStoredWebSessionID() {
if (typeof document === 'undefined') {
return;
}
document.cookie = `${WEB_SESSION_COOKIE}=; Max-Age=0; Path=/; SameSite=Lax`;
}
};
AuthService = __decorate([
Injectable({
providedIn: 'root'
})
], AuthService);
export { AuthService };