repository-settings.component.ts 8.98 KB
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///

import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { UntypedFormBuilder, UntypedFormGroup, FormGroupDirective, Validators } from '@angular/forms';
import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AdminService } from '@core/http/admin.service';
import {
  RepositorySettings,
  RepositoryAuthMethod,
  repositoryAuthMethodTranslationMap
} from '@shared/models/settings.models';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
import { isNotEmptyStr } from '@core/utils';
import { DialogService } from '@core/services/dialog.service';
import { ActionAuthUpdateHasRepository } from '@core/auth/auth.actions';
import { selectHasRepository } from '@core/auth/auth.selectors';
import { catchError, mergeMap, take } from 'rxjs/operators';
import { of } from 'rxjs';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { coerceBoolean } from '@shared/decorators/coercion';

@Component({
  selector: 'tb-repository-settings',
  templateUrl: './repository-settings.component.html',
  styleUrls: ['./repository-settings.component.scss', './../../pages/admin/settings-card.scss']
})
export class RepositorySettingsComponent extends PageComponent implements OnInit {

  @Input()
  detailsMode = false;

  @Input()
  popoverComponent: TbPopoverComponent;

  @Input()
  @coerceBoolean()
  hideLoadingBar = false;

  repositorySettingsForm: UntypedFormGroup;
  settings: RepositorySettings = null;

  repositoryAuthMethod = RepositoryAuthMethod;
  repositoryAuthMethods = Object.values(RepositoryAuthMethod);
  repositoryAuthMethodTranslations = repositoryAuthMethodTranslationMap;

  showChangePassword = false;
  changePassword = false;

  showChangePrivateKeyPassword = false;
  changePrivateKeyPassword = false;

  constructor(protected store: Store<AppState>,
              private adminService: AdminService,
              private dialogService: DialogService,
              private translate: TranslateService,
              private cd: ChangeDetectorRef,
              public fb: UntypedFormBuilder) {
    super(store);
  }

  ngOnInit() {
    this.repositorySettingsForm = this.fb.group({
      repositoryUri: [null, [Validators.required]],
      defaultBranch: ['main', []],
      readOnly: [false, []],
      showMergeCommits: [false, []],
      authMethod: [RepositoryAuthMethod.USERNAME_PASSWORD, [Validators.required]],
      username: [null, []],
      password: [null, []],
      privateKeyFileName: [null, [Validators.required]],
      privateKey: [null, []],
      privateKeyPassword: [null, []]
    });
    this.updateValidators(false);
    this.repositorySettingsForm.get('authMethod').valueChanges.subscribe(() => {
      this.updateValidators(true);
    });
    this.repositorySettingsForm.get('privateKeyFileName').valueChanges.subscribe(() => {
      this.updateValidators(false);
    });
    this.store.pipe(
      select(selectHasRepository),
      take(1),
      mergeMap((hasRepository) => {
        if (hasRepository) {
          return this.adminService.getRepositorySettings({ignoreErrors: true}).pipe(
            catchError(() => of(null))
          );
        } else {
          return of(null);
        }
      })
    ).subscribe(
      (settings) => {
        this.settings = settings;
        if (this.settings != null) {
          if (this.settings.authMethod === RepositoryAuthMethod.USERNAME_PASSWORD) {
            this.showChangePassword = true;
          } else {
            this.showChangePrivateKeyPassword = true;
          }
          this.repositorySettingsForm.reset(this.settings);
          this.updateValidators(false);
        }
    });
  }

  checkAccess(): void {
    const settings: RepositorySettings = this.repositorySettingsForm.value;
    this.adminService.checkRepositoryAccess(settings).subscribe(() => {
      this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('admin.check-repository-access-success'),
        type: 'success' }));
    });
  }

  save(): void {
    const settings: RepositorySettings = this.repositorySettingsForm.value;
    this.adminService.saveRepositorySettings(settings).subscribe(
      (savedSettings) => {
        this.settings = savedSettings;
        if (this.settings.authMethod === RepositoryAuthMethod.USERNAME_PASSWORD) {
          this.showChangePassword = true;
          this.changePassword = false;
        } else {
          this.showChangePrivateKeyPassword = true;
          this.changePrivateKeyPassword = false;
        }
        this.repositorySettingsForm.reset(this.settings);
        this.updateValidators(false);
        this.store.dispatch(new ActionAuthUpdateHasRepository({ hasRepository: true }));
      }
    );
  }

  delete(formDirective: FormGroupDirective): void {
    this.dialogService.confirm(
      this.translate.instant('admin.delete-repository-settings-title', ),
      this.translate.instant('admin.delete-repository-settings-text'), null,
      this.translate.instant('action.delete')
    ).subscribe((data) => {
      if (data) {
        this.adminService.deleteRepositorySettings().subscribe(
          () => {
            this.settings = null;
            this.showChangePassword = false;
            this.changePassword = false;
            this.showChangePrivateKeyPassword = false;
            this.changePrivateKeyPassword = false;
            formDirective.resetForm();
            this.repositorySettingsForm.reset({ defaultBranch: 'main', authMethod: RepositoryAuthMethod.USERNAME_PASSWORD });
            this.updateValidators(false);
            this.store.dispatch(new ActionAuthUpdateHasRepository({ hasRepository: false }));
          }
        );
      }
    });
  }

  changePasswordChanged() {
    if (this.changePassword) {
      this.repositorySettingsForm.get('password').patchValue('');
      this.repositorySettingsForm.get('password').markAsDirty();
    }
    this.updateValidators(false);
  }

  changePrivateKeyPasswordChanged() {
    if (this.changePrivateKeyPassword) {
      this.repositorySettingsForm.get('privateKeyPassword').patchValue('');
      this.repositorySettingsForm.get('privateKeyPassword').markAsDirty();
    }
    this.updateValidators(false);
  }

  updateValidators(emitEvent?: boolean): void {
    const authMethod: RepositoryAuthMethod = this.repositorySettingsForm.get('authMethod').value;
    const privateKeyFileName: string = this.repositorySettingsForm.get('privateKeyFileName').value;
    if (authMethod === RepositoryAuthMethod.USERNAME_PASSWORD) {
      this.repositorySettingsForm.get('username').enable({emitEvent});
      if (this.changePassword || !this.showChangePassword) {
        this.repositorySettingsForm.get('password').enable({emitEvent});
      } else {
        this.repositorySettingsForm.get('password').disable({emitEvent});
      }
      this.repositorySettingsForm.get('privateKeyFileName').disable({emitEvent});
      this.repositorySettingsForm.get('privateKey').disable({emitEvent});
      this.repositorySettingsForm.get('privateKeyPassword').disable({emitEvent});
    } else {
      this.repositorySettingsForm.get('username').disable({emitEvent});
      this.repositorySettingsForm.get('password').disable({emitEvent});
      this.repositorySettingsForm.get('privateKeyFileName').enable({emitEvent});
      this.repositorySettingsForm.get('privateKey').enable({emitEvent});
      if (this.changePrivateKeyPassword || !this.showChangePrivateKeyPassword) {
        this.repositorySettingsForm.get('privateKeyPassword').enable({emitEvent});
      } else {
        this.repositorySettingsForm.get('privateKeyPassword').disable({emitEvent});
      }
      if (isNotEmptyStr(privateKeyFileName)) {
        this.repositorySettingsForm.get('privateKey').clearValidators();
      } else {
        this.repositorySettingsForm.get('privateKey').setValidators([Validators.required]);
      }
    }
    this.repositorySettingsForm.get('username').updateValueAndValidity({emitEvent: false});
    this.repositorySettingsForm.get('password').updateValueAndValidity({emitEvent: false});
    this.repositorySettingsForm.get('privateKeyFileName').updateValueAndValidity({emitEvent: false});
    this.repositorySettingsForm.get('privateKey').updateValueAndValidity({emitEvent: false});
    this.repositorySettingsForm.get('privateKeyPassword').updateValueAndValidity({emitEvent: false});
  }

}