fix(admin-auth): reuse exact same QR/session API and component for admin login
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- Removed invented adminAuthApiUrl endpoint and separate AdminLoginComponent.
Admin login now uses the exact same Telegram session backend
(TelegramSessionApiService, {authApiUrl}/users/sessions) and the exact
same TelegramLoginComponent (mode="customer" | "admin" input) as customer
login - only the storage (cookie/localStorage/signals) stays separate.
- Extracted the shared HTTP+normalization logic from AuthService into
TelegramSessionApiService so both AuthService and AdminAuthService call it
instead of duplicating request/parsing code.
- Documented the resulting backend gap in docs/Project-Editor.md: since the
session API has no concept of "admin", server-side role enforcement is
required when admin API calls are made - the frontend only decides where
to store the session, not whether the user is actually an admin.
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
import { Component, ChangeDetectionStrategy, inject, effect, OnDestroy } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, Input, Injector, Signal, inject, effect, OnDestroy, OnInit } from '@angular/core';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { AdminAuthService } from '../../core/admin-auth/admin-auth.service';
|
||||
import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
import { QrLoginEngine } from '../../shared/qr-login/qr-login.engine';
|
||||
import { QrLoginAdapter } from '../../shared/qr-login/qr-login.model';
|
||||
import { QrLoginAdapter, QrLoginStatus } from '../../shared/qr-login/qr-login.model';
|
||||
import { AuthSession } from '../../models/auth.model';
|
||||
|
||||
/**
|
||||
* The one QR-login dialog, reused as-is for both customer and admin login.
|
||||
* `mode` only decides which session service/storage backs it (AuthService's
|
||||
* customer session vs AdminAuthService's admin session) - the QR creation,
|
||||
* polling, expiry, and "return from Telegram app" logic (QrLoginEngine) and
|
||||
* the API call underneath it (TelegramSessionApiService) are identical for
|
||||
* both, by design: there is one Telegram QR/session backend, not two.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-telegram-login',
|
||||
imports: [TranslatePipe],
|
||||
@@ -12,38 +21,60 @@ import { AuthSession } from '../../models/auth.model';
|
||||
styleUrls: ['./telegram-login.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TelegramLoginComponent implements OnDestroy {
|
||||
private authService = inject(AuthService);
|
||||
export class TelegramLoginComponent implements OnInit, OnDestroy {
|
||||
@Input() mode: 'customer' | 'admin' = 'customer';
|
||||
|
||||
showDialog = this.authService.showLoginDialog;
|
||||
status = this.authService.status;
|
||||
private readonly customerAuth = inject(AuthService);
|
||||
private readonly adminAuth = inject(AdminAuthService);
|
||||
private readonly injector = inject(Injector);
|
||||
|
||||
private readonly adapter: QrLoginAdapter<AuthSession> = {
|
||||
createSession: () => this.authService.createWebSession(),
|
||||
checkSessionOnce: webSessionID => this.authService.checkSessionOnce(webSessionID),
|
||||
isSessionActive: session => !!session?.active,
|
||||
getAppLoginUrl: webSessionID => this.authService.getTelegramAppLoginUrl(webSessionID),
|
||||
onLoginComplete: () => this.authService.onTelegramLoginComplete(),
|
||||
};
|
||||
private engine!: QrLoginEngine<AuthSession>;
|
||||
|
||||
private readonly engine = new QrLoginEngine<AuthSession>(this.adapter);
|
||||
readonly loginUrl = this.engine.loginUrl;
|
||||
readonly webSessionID = this.engine.webSessionID;
|
||||
readonly qrStatus = this.engine.qrStatus;
|
||||
readonly encodedQrUrl = this.engine.encodedQrUrl;
|
||||
readonly awaitingTelegramReturn = this.engine.awaitingAppReturn;
|
||||
showDialog = this.customerAuth.showLoginDialog;
|
||||
status = this.customerAuth.status;
|
||||
|
||||
constructor() {
|
||||
effect(() => this.engine.setActive(this.showDialog()));
|
||||
loginUrl!: Signal<string>;
|
||||
webSessionID!: Signal<string>;
|
||||
qrStatus!: Signal<QrLoginStatus>;
|
||||
encodedQrUrl!: Signal<string>;
|
||||
|
||||
ngOnInit(): void {
|
||||
const service = this.mode === 'admin' ? this.adminAuth : this.customerAuth;
|
||||
this.showDialog = service.showLoginDialog;
|
||||
this.status = service.status;
|
||||
|
||||
const adapter: QrLoginAdapter<AuthSession> = this.mode === 'admin'
|
||||
? {
|
||||
createSession: () => this.adminAuth.createWebSession(),
|
||||
checkSessionOnce: id => this.adminAuth.checkSessionOnce(id),
|
||||
isSessionActive: session => !!session?.active,
|
||||
getAppLoginUrl: id => this.adminAuth.getAdminAppLoginUrl(id),
|
||||
onLoginComplete: () => this.adminAuth.onLoginComplete(),
|
||||
}
|
||||
: {
|
||||
createSession: () => this.customerAuth.createWebSession(),
|
||||
checkSessionOnce: id => this.customerAuth.checkSessionOnce(id),
|
||||
isSessionActive: session => !!session?.active,
|
||||
getAppLoginUrl: id => this.customerAuth.getTelegramAppLoginUrl(id),
|
||||
onLoginComplete: () => this.customerAuth.onTelegramLoginComplete(),
|
||||
};
|
||||
|
||||
this.engine = new QrLoginEngine<AuthSession>(adapter);
|
||||
this.loginUrl = this.engine.loginUrl;
|
||||
this.webSessionID = this.engine.webSessionID;
|
||||
this.qrStatus = this.engine.qrStatus;
|
||||
this.encodedQrUrl = this.engine.encodedQrUrl;
|
||||
|
||||
effect(() => this.engine.setActive(this.showDialog()), { injector: this.injector });
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.engine.destroy();
|
||||
this.engine?.destroy();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.engine.setActive(false);
|
||||
this.authService.hideLogin();
|
||||
(this.mode === 'admin' ? this.adminAuth : this.customerAuth).hideLogin();
|
||||
}
|
||||
|
||||
openTelegramLogin(): void {
|
||||
|
||||
Reference in New Issue
Block a user