diff --git a/src/api/v1/setup.service.ts b/src/api/v1/setup.service.ts index 1a314d9..ef300ba 100644 --- a/src/api/v1/setup.service.ts +++ b/src/api/v1/setup.service.ts @@ -1,6 +1,6 @@ import {Injectable} from "@angular/core"; import ApiService, {AvailableVersion} from "@api/api.service"; -import {catchError, of, switchMap} from "rxjs"; +import {catchError, of} from "rxjs"; import {DatabaseResponse} from "@api/v1/configuration/databaseResponse"; import {DatabaseRequest} from "@api/v1/configuration/databaseRequest"; import {CacheRequest} from "@api/v1/configuration/cacheRequest"; @@ -137,18 +137,21 @@ export default class SetupService extends ApiService { public adminConfiguration() { let request = this.createRequestBuilder() - .setEndpoint('UpdateAdminConfiguration') + .setEndpoint('AdminConfiguration') .setWithCredentials() .build; - return this.get(request).pipe(switchMap(_ => { - request = this.createRequestBuilder() - .setEndpoint('AdminConfiguration') - .setWithCredentials() - .build; + return this.get(request); + } - return this.get(request); - })); + public registerOAuth(token: string) { + let request = this.createRequestBuilder() + .setEndpoint('HandleToken') + .setQueryParams({token: token}) + .setWithCredentials() + .build; + + return this.get(request); } public setLogging(data: LoggingRequest | null = null) { diff --git a/src/components/OAuthProviders/OAuthProviders.html b/src/components/OAuthProviders/OAuthProviders.html index 5fe8d02..9a1390f 100644 --- a/src/components/OAuthProviders/OAuthProviders.html +++ b/src/components/OAuthProviders/OAuthProviders.html @@ -1,4 +1,4 @@ -@if (providers.length !== 0) { +@if (!loading && providers.length !== 0) {

{{ message }}

@@ -17,4 +17,7 @@ }
+} @else if (loading) { +
+ } diff --git a/src/components/OAuthProviders/OAuthProviders.ts b/src/components/OAuthProviders/OAuthProviders.ts index 01ae8b5..d35cac3 100644 --- a/src/components/OAuthProviders/OAuthProviders.ts +++ b/src/components/OAuthProviders/OAuthProviders.ts @@ -1,9 +1,10 @@ -import {Component, EventEmitter, Inject, Input, Output} from '@angular/core'; +import {Component, EventEmitter, Inject, Input, OnInit, Output} from '@angular/core'; import AuthApiService, {OAuthProviderData} from "@api/v1/authApiService"; import {OAuthProvider} from "@model/oAuthProvider"; import {ToastrService} from "ngx-toastr"; import { - MAT_DIALOG_DATA, MatDialog, + MAT_DIALOG_DATA, + MatDialog, MatDialogActions, MatDialogContent, MatDialogRef, @@ -11,6 +12,11 @@ import { } from "@angular/material/dialog"; import {MatButton} from "@angular/material/button"; import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component"; +import {ActivatedRoute} from "@angular/router"; +import {catchError, finalize, Observable, switchMap, tap} from "rxjs"; +import {TwoFactorAuthentication} from "@model/twoFactorAuthentication"; +import {OAuthAction} from "@model/oAuthAction"; +import SetupService from "@api/v1/setup.service"; interface AvailableOAuthProviders extends OAuthProviderData { disabled: boolean; @@ -60,12 +66,13 @@ export class DeleteConfirmDialog { ], templateUrl: './OAuthProviders.html', styleUrl: './OAuthProviders.css', - providers: [AuthApiService] + providers: [SetupService, AuthApiService] }) -export class OAuthProviders { +export class OAuthProviders implements OnInit { protected providers: AvailableOAuthProviders[] = []; protected _activeProvidersId: OAuthProvider[] = []; protected _activeProviders: string[] = []; + protected loading = true; @Input() message: string = 'Вы можете войти в аккаунт через'; @@ -80,11 +87,73 @@ export class OAuthProviders { } @Input() canUnlink: boolean = false; + @Input() action: OAuthAction = OAuthAction.Login; + @Input() isSetup: boolean = false; @Output() public oAuthUpdateProviders = new EventEmitter(); + @Output() public oAuthLoginResult: EventEmitter = new EventEmitter(); - constructor(authApi: AuthApiService, private notify: ToastrService, private dialog: MatDialog) { - authApi.availableProviders().subscribe(providers => this.updateDisabledProviders(providers)); + constructor(private setupApi: SetupService, + private authApi: AuthApiService, + private notify: ToastrService, + private dialog: MatDialog, + private route: ActivatedRoute) { + } + + ngOnInit(): void { + const fullUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}`; + this.authApi.availableProviders(fullUrl).subscribe(providers => { + this.updateDisabledProviders(providers); + }); + + this.route.queryParamMap + .pipe( + switchMap(params => { + const result = params.get('result'); + + if (!result) { + this.loading = false; // Нет результата, завершение загрузки + return []; + } + + return this.handleOAuthResult(result); // Обрабатываем результат + }), + catchError(_ => { + this.loading = false; + return []; + }) + ) + .subscribe(); + } + + private handleOAuthResult(result: string): Observable { + switch (this.action) { + case OAuthAction.Login: + return this.authApi.loginOAuth(result).pipe( + tap(auth => { + this.oAuthLoginResult.emit(auth); + }), + finalize(() => { + this.loading = false; + }) + ); + + case OAuthAction.Bind: + if (this.isSetup) { + return this.setupApi.registerOAuth(result).pipe( + tap(() => { + this.oAuthUpdateProviders.emit(); + }), + finalize(() => { + this.loading = false; + }) + ); + } else + throw new Error('Action "Bind" requires setup mode to be enabled.'); + break; + default: + throw new Error('Unknown action type for action ' + this.action); + } } private updateDisabledProviders(data: OAuthProviderData[] | null = null) { @@ -98,26 +167,15 @@ export class OAuthProviders { } protected openOAuth(provider: AvailableOAuthProviders) { - console.log(provider.redirect); const oauthWindow = window.open( provider.redirect, - '_blank', + '_self' ); if (!oauthWindow) { this.notify.error('Не удалось открыть OAuth окно'); return; } - - provider.active = true; - - const checkInterval = setInterval(() => { - if (oauthWindow.closed) { - clearInterval(checkInterval); - this.oAuthUpdateProviders.emit(); - provider.active = false; - } - }, 1500); } protected confirmDelete(provider: AvailableOAuthProviders) { diff --git a/src/pages/setup/create-admin/create-admin.component.html b/src/pages/setup/create-admin/create-admin.component.html index ca5c71a..11f6eab 100644 --- a/src/pages/setup/create-admin/create-admin.component.html +++ b/src/pages/setup/create-admin/create-admin.component.html @@ -76,7 +76,9 @@ } - + diff --git a/src/pages/setup/create-admin/create-admin.component.ts b/src/pages/setup/create-admin/create-admin.component.ts index 9df434e..20aaec2 100644 --- a/src/pages/setup/create-admin/create-admin.component.ts +++ b/src/pages/setup/create-admin/create-admin.component.ts @@ -1,4 +1,5 @@ import {Component} from '@angular/core'; +import {Location} from '@angular/common'; import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; import {NavigationService} from "@service/navigation.service"; import {passwordMatchValidator} from '@service/password-match.validator'; @@ -13,6 +14,8 @@ import AuthApiService from "@api/v1/authApiService"; import {OAuthProviders} from "@component/OAuthProviders/OAuthProviders"; import {OAuthProvider} from "@model/oAuthProvider"; import {PasswordInputComponent} from "@component/common/password-input/password-input.component"; +import {OAuthAction} from "@model/oAuthAction"; +import {Router} from "@angular/router"; @Component({ selector: 'app-create-admin', @@ -29,7 +32,7 @@ import {PasswordInputComponent} from "@component/common/password-input/password- PasswordInputComponent ], templateUrl: './create-admin.component.html', - providers: [AuthApiService] + providers: [AuthApiService, Location] }) export class CreateAdminComponent { @@ -37,8 +40,9 @@ export class CreateAdminComponent { protected hideRetypePass = true; protected activatedProviders: OAuthProvider[] = []; - constructor( - private navigationService: NavigationService, private formBuilder: FormBuilder, private api: SetupService) { + constructor(private router: Router, + private location: Location, + private navigationService: NavigationService, private formBuilder: FormBuilder, private api: SetupService) { this.createAdminForm = this.formBuilder.group({ user: ['', Validators.pattern(/^([A-Za-z0-9]){4,}$/)], email: ['', Validators.email], @@ -76,6 +80,9 @@ export class CreateAdminComponent { this.activatedProviders = configuration.usedOAuthProviders; } + + const currentPath = this.router.url.split('?')[0]; + this.location.replaceState(currentPath); }); } @@ -87,4 +94,6 @@ export class CreateAdminComponent { protected updateProviders() { this.updateAdminData(); } + + protected readonly OAuthAction = OAuthAction; } diff --git a/src/pages/setup/setup.component.ts b/src/pages/setup/setup.component.ts index 5564163..7e8d054 100644 --- a/src/pages/setup/setup.component.ts +++ b/src/pages/setup/setup.component.ts @@ -1,6 +1,6 @@ import {Component, ViewEncapsulation} from '@angular/core'; import {MatSidenavModule} from "@angular/material/sidenav"; -import {Router, RouterOutlet} from "@angular/router"; +import {ActivatedRoute, Router, RouterOutlet} from "@angular/router"; import {MatCard} from "@angular/material/card"; import {MatButton} from "@angular/material/button"; import {NavigationService} from "@service/navigation.service"; @@ -30,20 +30,29 @@ export class SetupComponent { protected skipButtonDisabled: boolean = false; protected loaderActive: boolean = false; - protected routes: Array = ['', 'welcome', 'database', 'cache', 'password-policy', 'schedule', 'logging', 'create-admin', 'two-factor', 'summary']; + protected routes: Array = ['', 'welcome', 'create-admin', 'database', 'cache', 'password-policy', 'schedule', 'logging', 'two-factor', 'summary']; private index: number = 1; protected get getIndex() { return this.index; } - constructor(private router: Router, private navigationService: NavigationService, api: SetupService, private notify: ToastrService) { + constructor(private route: ActivatedRoute, + private router: Router, + private navigationService: NavigationService, + api: SetupService, + private notify: ToastrService) { + api.isConfigured().subscribe(x => { if (x) this.router.navigate(['/']).then(); }); if (!this.router.url.includes(this.routes[this.index])) { - this.router.navigate(['setup/', this.routes[this.index]]).then(); + const currentQueryParams = this.route.snapshot.queryParams; + this.router.navigate( + ['setup/', this.routes[this.index]], + {queryParams: currentQueryParams} + ).then(); } this.initializeButtonSubscriptions(); @@ -104,13 +113,25 @@ export class SetupComponent { this.moveToPreviousPage(); } - private moveToNextPage() { + private moveToNextPage(): void { if (this.index < this.routes.length - 1) { this.index++; - this.router.navigate(['setup/', this.routes[this.index]]).then(); - this.initializePage(); + + const currentQueryParams = this.route.snapshot.queryParams; + + this.router.navigate( + ['setup/', this.routes[this.index]], + {queryParams: currentQueryParams} + ).then(() => { + this.initializePage(); + }); } else { - this.router.navigate(['/']).then(); + const currentQueryParams = this.route.snapshot.queryParams; + + this.router.navigate( + ['/'], + {queryParams: currentQueryParams} + ).then(); } }