Администратор открывает любой ящик: MAILBOXES → OPEN, полоса «вернуться в admin@»
continuous-integration/drone/pr Build is passing
continuous-integration/drone/pr Build is passing
POST /api/admin/mailboxes/{id}/open — сессия этого ящика без его пароля, только для
MAIL_ADMINS; каждое открытие — в лог (кто какой ящик открыл). В веб-почте: кнопка OPEN
у ящика, сверху розовая полоса «Вы в ящике X как администратор — Back to admin@».
Тесты: +1 (всего 75), ng build OK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,9 @@ export class ApiService {
|
||||
newMailboxPassword(id: number): Observable<MailboxCreated> {
|
||||
return this.http.post<MailboxCreated>(`/api/admin/mailboxes/${id}/password`, {});
|
||||
}
|
||||
openMailbox(id: number): Observable<{ token: string; email: string }> {
|
||||
return this.http.post<{ token: string; email: string }>(`/api/admin/mailboxes/${id}/open`, {});
|
||||
}
|
||||
setMailboxActive(id: number, active: boolean): Observable<Mailbox> {
|
||||
return this.http.put<Mailbox>(`/api/admin/mailboxes/${id}/active`, { active });
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ export class AuthService {
|
||||
private router = inject(Router);
|
||||
|
||||
readonly email = signal<string | null>(read('email'));
|
||||
/** Administrator who opened this mailbox (Settings → MAILBOXES → Open), or null */
|
||||
readonly openedBy = signal<string | null>(read('adminEmail'));
|
||||
readonly loggedIn = computed(() => this.email() !== null && read('token') !== null);
|
||||
|
||||
login(email: string, password: string): Observable<TokenResponse> {
|
||||
@@ -24,6 +26,32 @@ export class AuthService {
|
||||
|
||||
token(): string | null { return read('token'); }
|
||||
|
||||
/** Administrator opens another mailbox: its session, the admin session kept to come back. */
|
||||
openAs(response: TokenResponse): void {
|
||||
const adminToken = read('token');
|
||||
const adminEmail = read('email');
|
||||
try {
|
||||
if (adminToken && adminEmail && !read('adminToken')) {
|
||||
localStorage.setItem('adminToken', adminToken);
|
||||
localStorage.setItem('adminEmail', adminEmail);
|
||||
}
|
||||
} catch { /* storage blocked */ }
|
||||
this.store(response);
|
||||
this.openedBy.set(read('adminEmail'));
|
||||
}
|
||||
|
||||
/** Back to the administrator's own mailbox. */
|
||||
backToAdmin(): void {
|
||||
const token = read('adminToken');
|
||||
const email = read('adminEmail');
|
||||
try {
|
||||
localStorage.removeItem('adminToken');
|
||||
localStorage.removeItem('adminEmail');
|
||||
} catch { /* storage blocked */ }
|
||||
this.openedBy.set(null);
|
||||
if (token && email) { this.store({ token, email }); } else { this.clear(); }
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.clear();
|
||||
this.router.navigate(['/login']);
|
||||
@@ -33,8 +61,11 @@ export class AuthService {
|
||||
try {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('email');
|
||||
localStorage.removeItem('adminToken');
|
||||
localStorage.removeItem('adminEmail');
|
||||
} catch { /* storage blocked */ }
|
||||
this.email.set(null);
|
||||
this.openedBy.set(null);
|
||||
}
|
||||
|
||||
private store(response: TokenResponse): void {
|
||||
|
||||
Reference in New Issue
Block a user