diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-beta.yml similarity index 97% rename from .github/workflows/build-docker-image.yml rename to .github/workflows/build-beta.yml index 7bc2bfad..7a876060 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-beta.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: push: branches: - - main + - 'release-*' env: REPO: ${{ github.repository }} diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml new file mode 100644 index 00000000..a55b2142 --- /dev/null +++ b/.github/workflows/build-dev.yml @@ -0,0 +1,32 @@ +name: build +on: + workflow_dispatch: + push: + branches: + - dev + +env: + REPO: ${{ github.repository }} + +jobs: + build-app: + runs-on: ubuntu-latest + steps: + - name: downcase REPO + run: | + echo "REPO=${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} + - uses: actions/checkout@v2 + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build docker image + env: + TAG: ghcr.io/${{ env.REPO }}:dev + run: | + echo $TAG + docker build -f configurations/docker/Dockerfile . --tag $TAG --cache-from $TAG + docker push $TAG diff --git a/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-contact-dialog/create-contact-dialog.component.ts b/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-contact-dialog/create-contact-dialog.component.ts index 879c7765..851b7786 100644 --- a/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-contact-dialog/create-contact-dialog.component.ts +++ b/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-contact-dialog/create-contact-dialog.component.ts @@ -6,7 +6,7 @@ import { MultidirectoryUiKitModule, StepperComponent } from 'multidirectory-ui-k import { DialogService } from '../../../services/dialog.service'; import { MultidirectoryApiService } from '@services/multidirectory-api.service'; import { ToastrService } from 'ngx-toastr'; -import { catchError, debounceTime, EMPTY, map, Subject, switchMap } from 'rxjs'; +import { debounceTime, finalize, map, Subject, switchMap } from 'rxjs'; import { CreateUserDialogData, CreateUserDialogReturnData } from '../../../interfaces/user-create-dialog.interface'; import { UserCreateRequest } from '@models/api/user-create/user-create.request'; import { SchemaService } from '@services/schema/schema.service'; @@ -15,7 +15,7 @@ import { CreateEntryRequest } from '@models/api/entry/create-request'; import { LdapAttribute } from '@core/ldap/ldap-attributes/ldap-attribute'; @Component({ - selector: 'app-user-create-dialog', + selector: 'app-contact-create-dialog', standalone: true, imports: [DialogComponent, TranslocoPipe, MultidirectoryUiKitModule, ReactiveFormsModule], templateUrl: './create-contact-dialog.component.html', @@ -125,11 +125,8 @@ export class CreateContactDialogComponent implements OnInit { }), ); }), - catchError(() => { - this.dialogComponent?.hideSpinner(); - this.toastr.error(translate('contact-create.unable-create-contact')); - this.dialogService.close(this.dialogRef); - return EMPTY; + finalize(() => { + this.dialogComponent.hideSpinner(); }), ) .subscribe(() => { @@ -150,7 +147,6 @@ export class CreateContactDialogComponent implements OnInit { }, ], }; - this.dialogComponent?.hideSpinner(); this.dialogService.close(this.dialogRef, newItem); }); } diff --git a/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-group-dialog/create-group-dialog.component.ts b/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-group-dialog/create-group-dialog.component.ts index da47c7ff..be7b5ce0 100644 --- a/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-group-dialog/create-group-dialog.component.ts +++ b/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-group-dialog/create-group-dialog.component.ts @@ -4,7 +4,7 @@ import { MdFormComponent, MultidirectoryUiKitModule } from 'multidirectory-ui-ki import { RequiredWithMessageDirective } from '@core/validators/required-with-message.directive'; import { translate, TranslocoDirective, TranslocoPipe } from '@jsverse/transloco'; import { FormsModule } from '@angular/forms'; -import { catchError, EMPTY, map, Observable, Subject, switchMap, takeUntil } from 'rxjs'; +import { finalize, map, Observable, Subject, switchMap, takeUntil } from 'rxjs'; import { DialogService } from '../../../services/dialog.service'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; import { CreateGroupDialogData, CreateGroupDialogReturnData } from '../../../interfaces/create-group-dialog.interface'; @@ -88,13 +88,11 @@ export class CreateGroupDialogComponent implements OnInit { }), ); }), - catchError(() => { - this.dialogService.close(this.dialogRef); - return EMPTY; + finalize(() => { + this.dialogComponent.hideSpinner(); }), ) .subscribe((x) => { - this.dialogComponent?.hideSpinner(); this.setupRequest = new GroupCreateRequest(); this.dialogService.close(this.dialogRef, x); }); diff --git a/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-user-dialog/create-user-dialog.component.ts b/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-user-dialog/create-user-dialog.component.ts index e80989c9..a270a207 100644 --- a/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-user-dialog/create-user-dialog.component.ts +++ b/projects/multidirectory-app/src/app/components/modals/components/dialogs/create-user-dialog/create-user-dialog.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - DestroyRef, - inject, - OnInit, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, DestroyRef, inject, OnInit, ViewChild } from '@angular/core'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; import { DialogComponent } from '../../core/dialog/dialog.component'; import { translate, TranslocoPipe } from '@jsverse/transloco'; @@ -14,11 +7,8 @@ import { DialogService } from '../../../services/dialog.service'; import { UserCreateService } from '@services/user-create.service'; import { MultidirectoryApiService } from '@services/multidirectory-api.service'; import { ToastrService } from 'ngx-toastr'; -import { catchError, map, Subject, switchMap } from 'rxjs'; -import { - CreateUserDialogData, - CreateUserDialogReturnData, -} from '../../../interfaces/user-create-dialog.interface'; +import { finalize, map, Subject, switchMap } from 'rxjs'; +import { CreateUserDialogData, CreateUserDialogReturnData } from '../../../interfaces/user-create-dialog.interface'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { UserCreateGeneralInfoComponent } from '@features/forms/user-create/general-info/general-info.component'; import { UserCreatePasswordSettingsComponent } from '@features/forms/user-create/password-settings/password-settings.component'; @@ -52,8 +42,7 @@ export class CreateUserDialogComponent implements OnInit { unsubscribe = new Subject(); formValid = false; - private dialogRef: DialogRef = - inject(DialogRef); + private dialogRef: DialogRef = inject(DialogRef); private dialogService: DialogService = inject(DialogService); private setup: UserCreateService = inject(UserCreateService); private api: MultidirectoryApiService = inject(MultidirectoryApiService); @@ -69,14 +58,7 @@ export class CreateUserDialogComponent implements OnInit { }); } - objectClasses: string[] = [ - 'user', - 'top', - 'person', - 'organizationalPerson', - 'posixAccount', - 'shadowAccount', - ]; + objectClasses: string[] = ['user', 'top', 'person', 'organizationalPerson', 'posixAccount', 'shadowAccount']; getObjectClasses() { return this.schema.getSchemaEntity('User').pipe( map((result) => { @@ -152,13 +134,11 @@ export class CreateUserDialogComponent implements OnInit { }), ); }), - catchError((err) => { + finalize(() => { this.dialogComponent.hideSpinner(); - throw err; }), ) .subscribe((x) => { - this.dialogComponent.hideSpinner(); this.close(x); }); } diff --git a/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.html b/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.html index 97bc966d..5cc0e7c2 100644 --- a/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.html +++ b/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.html @@ -13,6 +13,9 @@ @case (EntityTypes.Computer) { } + @case (EntityTypes.Contact) { + + } @default {
@@ -22,11 +25,16 @@
diff --git a/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.ts b/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.ts index 8dd62e3a..f5c72746 100644 --- a/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.ts +++ b/projects/multidirectory-app/src/app/components/modals/components/dialogs/entity-properties-dialog/entity-properties-dialog.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - inject, - OnInit, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit, ViewChild } from '@angular/core'; import { ComputerPropertiesComponent } from '@features/ldap-properties/computer-properties/computer-properties.component'; import { DialogComponent } from '../../core/dialog/dialog.component'; import { EntityAttributesComponent } from '@features/entity-attributes/entity-attributes.component'; @@ -13,23 +6,18 @@ import { GroupPropertiesComponent } from '@features/ldap-properties/group-proper import { MultidirectoryUiKitModule } from 'multidirectory-ui-kit'; import { translate, TranslocoPipe } from '@jsverse/transloco'; import { UserPropertiesComponent } from '@features/ldap-properties/user-properties/user-properties.component'; -import { EMPTY, of, switchMap, take } from 'rxjs'; +import { EMPTY, finalize, of, switchMap, take } from 'rxjs'; import { DialogService } from '../../../services/dialog.service'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; -import { - EntityPropertiesDialogData, - EntityPropertiesDialogReturnData, -} from '../../../interfaces/entity-properties-dialog.interface'; +import { EntityPropertiesDialogData, EntityPropertiesDialogReturnData } from '../../../interfaces/entity-properties-dialog.interface'; import { AttributeService } from '@services/attributes.service'; import { MultidirectoryApiService } from '@services/multidirectory-api.service'; -import { - ConfirmDialogData, - ConfirmDialogReturnData, -} from '../../../interfaces/confirm-dialog.interface'; +import { ConfirmDialogData, ConfirmDialogReturnData } from '../../../interfaces/confirm-dialog.interface'; import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component'; import { LdapAttributes } from '@core/ldap/ldap-attributes/ldap-attributes'; import { SearchQueries } from '@core/ldap/search'; import { LdapEntryType } from '@models/core/ldap/ldap-entry-type'; +import { ContactPropertiesComponent } from '@features/ldap-properties/contact-properties/contact-properties.component'; @Component({ selector: 'app-entity-properties-dialog', @@ -42,6 +30,7 @@ import { LdapEntryType } from '@models/core/ldap/ldap-entry-type'; MultidirectoryUiKitModule, TranslocoPipe, UserPropertiesComponent, + ContactPropertiesComponent, ], templateUrl: './entity-properties-dialog.component.html', styleUrl: './entity-properties-dialog.component.scss', @@ -51,6 +40,7 @@ export class EntityPropertiesDialogComponent implements OnInit { dialogData: EntityPropertiesDialogData = inject(DIALOG_DATA); @ViewChild('userProps') userProps!: UserPropertiesComponent; + @ViewChild('contactProps') contactProps!: ContactPropertiesComponent; @ViewChild(DialogComponent, { static: true }) dialogComponent!: DialogComponent; EntityTypes = LdapEntryType; @@ -58,8 +48,7 @@ export class EntityPropertiesDialogComponent implements OnInit { accessor: LdapAttributes = new LdapAttributes([]); private dialogService: DialogService = inject(DialogService); - private dialogRef: DialogRef = - inject(DialogRef); + private dialogRef: DialogRef = inject(DialogRef); private attributeService: AttributeService = inject(AttributeService); private api: MultidirectoryApiService = inject(MultidirectoryApiService); private cdr: ChangeDetectorRef = inject(ChangeDetectorRef); @@ -71,10 +60,7 @@ export class EntityPropertiesDialogComponent implements OnInit { .subscribe((props) => { const attributes = props.search_result[0].partial_attributes; - this.accessor = this.attributeService.getTrackableAttributes( - this.dialogData.entity, - new LdapAttributes(attributes), - ); + this.accessor = this.attributeService.getTrackableAttributes(this.dialogData.entity, new LdapAttributes(attributes)); this.cdr.detectChanges(); }); @@ -97,23 +83,21 @@ export class EntityPropertiesDialogComponent implements OnInit { const confirmObservable$ = !needConfirmation ? of(true) - : this.dialogService.open( - { - component: ConfirmDialogComponent, - dialogConfig: { - minHeight: '160px', - data: { - promptHeader: translate('confirmation-dialog.prompt-header'), - promptText: translate('confirmation-dialog.prompt-text'), - primaryButtons: [{ id: true, text: translate('confirmation-dialog.yes') }], - secondaryButtons: [ - { id: false, text: translate('confirmation-dialog.no') }, - { id: 'cancel', text: translate('confirmation-dialog.cancel') }, - ], - }, + : this.dialogService.open({ + component: ConfirmDialogComponent, + dialogConfig: { + minHeight: '160px', + data: { + promptHeader: translate('confirmation-dialog.prompt-header'), + promptText: translate('confirmation-dialog.prompt-text'), + primaryButtons: [{ id: true, text: translate('confirmation-dialog.yes') }], + secondaryButtons: [ + { id: false, text: translate('confirmation-dialog.no') }, + { id: 'cancel', text: translate('confirmation-dialog.cancel') }, + ], }, }, - ).closed; + }).closed; confirmObservable$ .pipe( @@ -130,17 +114,14 @@ export class EntityPropertiesDialogComponent implements OnInit { return of(''); }), + finalize(() => { + this.dialogComponent.hideSpinner(); + }), ) .subscribe({ next: () => { - this.dialogComponent.hideSpinner(); this.close(); }, - error: (err) => { - this.dialogComponent.hideSpinner(); - - throw err; - }, }); return false; } diff --git a/projects/multidirectory-app/src/app/core/interceptors/error.interceptor.ts b/projects/multidirectory-app/src/app/core/interceptors/error.interceptor.ts index 00064bef..d391ca0a 100644 --- a/projects/multidirectory-app/src/app/core/interceptors/error.interceptor.ts +++ b/projects/multidirectory-app/src/app/core/interceptors/error.interceptor.ts @@ -1,12 +1,12 @@ import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { NgxSpinnerService } from 'ngx-spinner'; import { ToastrService } from 'ngx-toastr'; import { catchError, EMPTY, Observable, take, throwError } from 'rxjs'; import { AppSettingsService } from '@services/app-settings.service'; import { DialogService } from '@components/modals/services/dialog.service'; import { translate } from '@jsverse/transloco'; + export const ErrorCode = { BadRequest: 400, NotAuthorized: 401, @@ -17,7 +17,6 @@ export const ErrorCode = { @Injectable() export class ErrorInterceptor implements HttpInterceptor { private readonly toastr = inject(ToastrService); - private readonly spinner = inject(NgxSpinnerService); private readonly router = inject(Router); private app: AppSettingsService = inject(AppSettingsService); private dialogService = inject(DialogService); @@ -25,13 +24,8 @@ export class ErrorInterceptor implements HttpInterceptor { intercept(req: HttpRequest, next: HttpHandler): Observable> { return next.handle(req).pipe( catchError((error: HttpErrorResponse) => { - this.spinner.hide(); if (error.status === ErrorCode.NotAuthorized) { - const errText = this.parseBadRequestError(error); - this.toastr.info(errText); - this.dialogService.closeAll(); - - return throwError(() => error); + return this.handle401(error); } if ( error.status === ErrorCode.BadRequest || @@ -55,4 +49,27 @@ export class ErrorInterceptor implements HttpInterceptor { return errorName; } + + private handle401(error: HttpErrorResponse) { + this.dialogService.closeAll(); + + const isAuthCheck = error?.url?.includes('/auth/me'); + if (!isAuthCheck) { + const errText = this.parseBadRequestError(error); + this.toastr.info(errText); + } + + const userAbsent = Object.keys(this.app.user).length === 0; + + if (!window.location.href.includes('/login') && userAbsent) { + this.app + .logout() + .pipe(take(1)) + .subscribe(() => { + this.router.navigate(['login']); + }); + return EMPTY; + } + return throwError(() => error); + } } diff --git a/projects/multidirectory-app/src/app/core/ldap/search.ts b/projects/multidirectory-app/src/app/core/ldap/search.ts index f0f923c5..2458edf7 100644 --- a/projects/multidirectory-app/src/app/core/ldap/search.ts +++ b/projects/multidirectory-app/src/app/core/ldap/search.ts @@ -10,6 +10,8 @@ export const SearchQueries = { types_only: false, filter: '(objectClass=*)', attributes: [ + 'dnsHostName', + 'rootDomainNamingContext', 'defaultNamingContext', 'namingContexts', 'subschemaSubentry', @@ -33,8 +35,7 @@ export const SearchQueries = { size_limit: 0, time_limit: 0, types_only: false, - filter: - '(|(objectClass=builtinDomain)(objectClass=container)(objectClass=krbContainer)(objectClass=krbrealmcontainer))', + filter: '(|(objectClass=builtinDomain)(objectClass=container)(objectClass=krbContainer)(objectClass=krbrealmcontainer))', attributes: ['defaultNamingContext', 'sAMAccountName', 'name', 'objectClass'], }); }, @@ -48,15 +49,7 @@ export const SearchQueries = { time_limit: 0, types_only: false, filter: query ? `(&(objectClass=*)(name=*${query}*))` : '(objectClass=*)', - attributes: [ - 'defaultNamingContext', - 'sAMAccountName', - 'name', - 'objectClass', - 'userAccountControl', - 'entityTypeName', - 'description', - ], + attributes: ['defaultNamingContext', 'sAMAccountName', 'name', 'objectClass', 'userAccountControl', 'entityTypeName', 'description'], page_number: Math.floor(offset / Math.max(limit, 1) + 1), }); return req; @@ -185,14 +178,7 @@ export const SearchQueries = { size_limit: limit, time_limit: 1000, filter: `(|(objectClass=${objectClass}))`, - attributes: [ - 'displayName', - 'distinguishedName', - 'objectClass', - 'name', - 'cn', - 'entityTypeName', - ], + attributes: ['displayName', 'distinguishedName', 'objectClass', 'name', 'cn', 'entityTypeName'], page_number: Math.floor(offset / limit) + 1, }); }, diff --git a/projects/multidirectory-app/src/app/core/validators/ip-universal.directive.ts b/projects/multidirectory-app/src/app/core/validators/ip-universal.directive.ts new file mode 100644 index 00000000..977ba69c --- /dev/null +++ b/projects/multidirectory-app/src/app/core/validators/ip-universal.directive.ts @@ -0,0 +1,25 @@ +import { Directive, input } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; +import { translate } from '@jsverse/transloco'; + +@Directive({ + selector: '[validIp]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: UniversalIpValidatorDirective, + multi: true, + }, + ], + standalone: true, +}) +export class UniversalIpValidatorDirective implements Validator { + readonly errorLabel = input(translate('error-message.ip-valid')); + ipPattern = new RegExp( + '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:-(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))?(?:\\/(?:[0-9]|[1-2][0-9]|3[0-2]))?$', + ); + + validate(control: AbstractControl): ValidationErrors | null { + return this.ipPattern.test(control.value) ? null : { IpAddress: this.errorLabel() }; + } +} diff --git a/projects/multidirectory-app/src/app/core/validators/ip6-address.directive.ts b/projects/multidirectory-app/src/app/core/validators/ip6-address.directive.ts index 577e3f00..1576f136 100644 --- a/projects/multidirectory-app/src/app/core/validators/ip6-address.directive.ts +++ b/projects/multidirectory-app/src/app/core/validators/ip6-address.directive.ts @@ -16,7 +16,6 @@ import { translate } from '@jsverse/transloco'; export class Ip6AddressValidatorDirective implements Validator { readonly errorLabel = input(translate('error-message.ip6-valid')); ipPattern = new RegExp('^([a-f0-9:]+:+)+[a-f0-9]+$'); - validate(control: AbstractControl): ValidationErrors | null { const result = this.ipPattern.test(control.value); if (result) { diff --git a/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.html b/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.html index 6d4e07b5..1650dd78 100644 --- a/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.html +++ b/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.html @@ -6,37 +6,31 @@
- +
+ + + + + + + + +
- -
-
- - {{ - 'add-zone-dialog.add' | transloco - }} + + + +
+
+ + {{ 'entity-attributes.with-values-only' | transloco }} + +
- {{ - 'add-zone-dialog.apply' | transloco - }} + {{ 'add-zone-dialog.apply' | transloco }}
diff --git a/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.ts b/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.ts index a6e49966..5537bb9b 100644 --- a/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.ts +++ b/projects/multidirectory-app/src/app/features/dns/add-zone-dialog/add-zone-dialog.component.ts @@ -18,14 +18,7 @@ import { RequiredWithMessageDirective } from '@core/validators/required-with-mes selector: 'app-add-zone-dialog', templateUrl: './add-zone-dialog.component.html', styleUrls: ['./add-zone-dialog.component.scss'], - imports: [ - FormsModule, - DialogComponent, - TranslocoModule, - MultidirectoryUiKitModule, - CommonModule, - RequiredWithMessageDirective, - ], + imports: [FormsModule, DialogComponent, TranslocoModule, MultidirectoryUiKitModule, CommonModule, RequiredWithMessageDirective], }) export class AddZoneDialogComponent { private api = inject(DnsApiService); @@ -33,7 +26,7 @@ export class AddZoneDialogComponent { private dialogRef: DialogRef = inject(DialogRef); dnsZone = new DnsAddZoneRequest({ - zone_type: 'master', + dnssec: false, }); onSumbit(event: SubmitEvent) { @@ -44,41 +37,41 @@ export class AddZoneDialogComponent { }); } - openIpAddressDialog() { - let aclparameters = this.dnsZone.params.find((x) => x.name == 'acl'); - if (!aclparameters) { - aclparameters = new DnsZoneParam({ name: 'acl', value: [] }); - this.dnsZone.params.push(aclparameters); - } - - let address: IpOption[] = []; - if (aclparameters.value instanceof Array) { - address = aclparameters.value.flatMap((x) => this.toIpOption(x)); - } - - this.dialogService - .open({ - component: IpListDialogComponent, - dialogConfig: { - data: { - addresses: address, - }, - }, - }) - .closed.pipe(take(1)) - .subscribe((result) => { - if (!result) { - return; - } - let aclparameters = this.dnsZone.params.find((x) => x.name == 'acl'); - if (!aclparameters) { - aclparameters = new DnsZoneParam({ name: 'acl', value: [] }); - this.dnsZone.params.push(aclparameters); - } - this._ipString = this.fromIpOption(result.addresses); - aclparameters.value = this._ipString.split(',').map((x) => x.trim()); - }); - } + // openIpAddressDialog() { + // let aclparameters = this.dnsZone.params.find((x) => x.name == 'acl'); + // if (!aclparameters) { + // aclparameters = new DnsZoneParam({ name: 'acl', value: [] }); + // this.dnsZone.params.push(aclparameters); + // } + // + // let address: IpOption[] = []; + // if (aclparameters.value instanceof Array) { + // address = aclparameters.value.flatMap((x) => this.toIpOption(x)); + // } + // + // this.dialogService + // .open({ + // component: IpListDialogComponent, + // dialogConfig: { + // data: { + // addresses: address, + // }, + // }, + // }) + // .closed.pipe(take(1)) + // .subscribe((result) => { + // if (!result) { + // return; + // } + // let aclparameters = this.dnsZone.params.find((x) => x.name == 'acl'); + // if (!aclparameters) { + // aclparameters = new DnsZoneParam({ name: 'acl', value: [] }); + // this.dnsZone.params.push(aclparameters); + // } + // this._ipString = this.fromIpOption(result.addresses); + // aclparameters.value = this._ipString.split(',').map((x) => x.trim()); + // }); + // } private _ipString = ''; get ipString() { @@ -86,29 +79,30 @@ export class AddZoneDialogComponent { } set ipString(x: string) { this._ipString = x; - let aclparameters = this.dnsZone.params.find((x) => x.name == 'acl'); - if (!aclparameters) { - aclparameters = new DnsZoneParam({ name: 'acl', value: [] }); - this.dnsZone.params.push(aclparameters); - } - aclparameters.value = this.fromIpOption(this.toIpOption(x)) - .split(',') - .map((x) => x.trim()); + this.dnsZone.nameserver_ip = this._ipString; + // let aclparameters = this.dnsZone.params.find((x) => x.name == 'acl'); + // if (!aclparameters) { + // aclparameters = new DnsZoneParam({ name: 'acl', value: [] }); + // this.dnsZone.params.push(aclparameters); + // } + // aclparameters.value = this.fromIpOption(this.toIpOption(x)) + // .split(',') + // .map((x) => x.trim()); } - toIpOption(ipString: string): IpOption[] { - return ipString.split(',').map((x) => { - x = x.trim(); - if (x.includes('-')) { - const parts = x.split('-').map((x) => x.trim()); - return new IpRange({ - start: parts[0], - end: parts[1], - }); - } - return x.trim(); - }); - } + // toIpOption(ipString: string): IpOption[] { + // return ipString.split(',').map((x) => { + // x = x.trim(); + // if (x.includes('-')) { + // const parts = x.split('-').map((x) => x.trim()); + // return new IpRange({ + // start: parts[0], + // end: parts[1], + // }); + // } + // return x.trim(); + // }); + // } fromIpOption(ips: IpOption[]) { return ips.map((x: any) => (x instanceof Object ? x.start + '-' + x.end : x)).join(', '); diff --git a/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.html b/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.html index 0fec0246..4252252d 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.html +++ b/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.html @@ -3,14 +3,12 @@
{{ 'add-forward-zone-dialog.modal-header' | transloco }}
- +
-
- @for (forwarder of zone.forwarders; track $index) { +
+ @for (forwarder of zone.servers; track $index) {
- -
- {{ - forwarderResponse.has($index) - ? (forwarderResponse.get($index)?.FQDN ?? 'FQDN') - : 'FQDN' - }} + +
+ {{ forwarderResponse.has($index) ? (forwarderResponse.get($index)?.FQDN ?? 'FQDN') : 'FQDN' }}
@@ -43,10 +34,7 @@ }
- +
@@ -56,18 +44,14 @@
- {{ - 'add-forward-zone-dialog.add' | transloco - }} + {{ 'add-forward-zone-dialog.add' | transloco }}
diff --git a/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.ts b/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.ts index 742b2d0e..c1dbc5c6 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.ts +++ b/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/add-forward-zone-dialog/add-forward-zone-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, inject } from '@angular/core'; -import { DialogComponent } from '../../../../components/modals/components/core/dialog/dialog.component'; +import { DialogComponent } from '@components/modals/components/core/dialog/dialog.component'; import { translate, TranslocoModule } from '@jsverse/transloco'; import { DialogService } from '@components/modals/services/dialog.service'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; @@ -7,23 +7,13 @@ import { AddForwardZoneDialogData } from './add-forward-zone-dialog.interface'; import { FormsModule, NgForm } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { DnsApiService } from '@services/dns-api.service'; -import { - DnsCheckForwardZoneRequest, - DnsCheckForwardZoneResponse, -} from '@models/api/dns/dns-check-forward-zone'; +import { DnsCheckForwardZoneRequest, DnsCheckForwardZoneResponse } from '@models/api/dns/dns-check-forward-zone'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { faCheck, faClose } from '@fortawesome/free-solid-svg-icons'; import { MultidirectoryUiKitModule } from 'multidirectory-ui-kit'; @Component({ - imports: [ - DialogComponent, - TranslocoModule, - MultidirectoryUiKitModule, - FormsModule, - CommonModule, - FontAwesomeModule, - ], + imports: [DialogComponent, TranslocoModule, MultidirectoryUiKitModule, FormsModule, CommonModule, FontAwesomeModule], templateUrl: './add-forward-zone-dialog.component.html', styleUrl: './add-forward-zone-dialog.component.scss', }) @@ -40,7 +30,7 @@ export class AddForwardZoneDialogComponent { tooltip = translate('add-forward-zone-dialog.tooltip'); onDeleteForwarderClick(toDelete: string) { - this.zone.forwarders = this.zone.forwarders.filter((forwarder) => forwarder !== toDelete); + this.zone.servers = this.zone.servers.filter((forwarder) => forwarder !== toDelete); this.selectedForwaderIndex = undefined; } @@ -48,10 +38,9 @@ export class AddForwardZoneDialogComponent { if (this.selectedForwaderIndex == undefined) { return; } - const temp = this.zone.forwarders?.[this.selectedForwaderIndex + 1]; - this.zone.forwarders[this.selectedForwaderIndex + 1] = - this.zone.forwarders[this.selectedForwaderIndex]; - this.zone.forwarders[this.selectedForwaderIndex] = temp; + const temp = this.zone.servers?.[this.selectedForwaderIndex + 1]; + this.zone.servers[this.selectedForwaderIndex + 1] = this.zone.servers[this.selectedForwaderIndex]; + this.zone.servers[this.selectedForwaderIndex] = temp; this.selectedForwaderIndex++; } moveUp() { @@ -59,23 +48,20 @@ export class AddForwardZoneDialogComponent { return; } - const temp = this.zone.forwarders?.[this.selectedForwaderIndex - 1]; - this.zone.forwarders[this.selectedForwaderIndex - 1] = - this.zone.forwarders[this.selectedForwaderIndex]; - this.zone.forwarders[this.selectedForwaderIndex] = temp; + const temp = this.zone.servers?.[this.selectedForwaderIndex - 1]; + this.zone.servers[this.selectedForwaderIndex - 1] = this.zone.servers[this.selectedForwaderIndex]; + this.zone.servers[this.selectedForwaderIndex] = temp; this.selectedForwaderIndex--; } addForwarder() { - this.zone.forwarders.push(''); + this.zone.servers.push(''); } submitForwarder(form: NgForm, index: number) { - this.dns - .checkForwardZone(new DnsCheckForwardZoneRequest({ dns_server_ips: [form.value.ip] })) - .subscribe((response) => { - this.forwarderResponse?.set(index, response[0]); - }); + this.dns.checkForwardZone(new DnsCheckForwardZoneRequest({ dns_server_ips: [form.value.ip] })).subscribe((response) => { + this.forwarderResponse?.set(index, response[0]); + }); } apply() { diff --git a/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/dns-forward-zones.component.ts b/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/dns-forward-zones.component.ts index 24359b0f..f0f0d3fd 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/dns-forward-zones.component.ts +++ b/projects/multidirectory-app/src/app/features/dns/dns-forward-zones/dns-forward-zones.component.ts @@ -6,14 +6,11 @@ import { ToastrService } from 'ngx-toastr'; import { DialogService } from '../../../components/modals/services/dialog.service'; import { CommonModule } from '@angular/common'; import { DnsApiService } from '@services/dns-api.service'; -import { DnsForwardZone } from '@models/api/dns/dns-forward-zone'; -import { - AddForwardZoneDialogData, - AddForwardZoneDialogReturnData, -} from './add-forward-zone-dialog/add-forward-zone-dialog.interface'; +import { DnsForwardGetData, DnsForwardZone } from '@models/api/dns/dns-forward-zone'; +import { AddForwardZoneDialogData, AddForwardZoneDialogReturnData } from './add-forward-zone-dialog/add-forward-zone-dialog.interface'; import { AddForwardZoneDialogComponent } from './add-forward-zone-dialog/add-forward-zone-dialog.component'; import { DnsAddZoneRequest } from '@models/dhcp/areas/dhcp-add-areas-response'; -import { EMPTY, switchMap, take } from 'rxjs'; +import { EMPTY, filter, switchMap, take } from 'rxjs'; import { TableColumn } from 'ngx-datatable-gimefork'; @Component({ @@ -43,22 +40,18 @@ export class DnsForwardZonesComponent implements OnInit { } private loadData() { - this.dnsApi.getForwardZones().subscribe((forwardZones) => { + this.dnsApi.getForwardZones().subscribe((forwardZones: DnsForwardZone[]) => { this.selectedRedirectionZones.set([]); this.redirectionZonesRows.set(forwardZones); this.total = forwardZones.length; }); } - selectedRedirectionZones = signal([]); + selectedRedirectionZones = signal([]); onAddRedirectionZone() { this.dialogService - .open< - AddForwardZoneDialogReturnData, - AddForwardZoneDialogData, - AddForwardZoneDialogComponent - >({ + .open({ component: AddForwardZoneDialogComponent, dialogConfig: { minWidth: '620px', @@ -67,11 +60,9 @@ export class DnsForwardZonesComponent implements OnInit { }) .closed.pipe( take(1), - switchMap((result) => { - if (!result) { - return EMPTY; - } - return this.dnsApi.addZone(result.toDnsAddZoneRequest()); + filter((x) => !!x), + switchMap((result: DnsForwardZone) => { + return this.dnsApi.addForwardZone(result); }), ) .subscribe((result) => { @@ -79,26 +70,21 @@ export class DnsForwardZonesComponent implements OnInit { }); } - onEditRedirectionZone($event: InputEvent) { + onEditRedirectionZone($event: any) { + const dialogData = new DnsForwardZone({ servers: ($event.row as DnsForwardGetData).servers, zone_name: ($event as any).row.name }); this.dialogService - .open< - AddForwardZoneDialogReturnData, - AddForwardZoneDialogData, - AddForwardZoneDialogComponent - >({ + .open({ component: AddForwardZoneDialogComponent, dialogConfig: { minWidth: '620px', - data: new DnsForwardZone(($event as any).row as DnsForwardZone), + data: dialogData, }, }) .closed.pipe( take(1), - switchMap((result) => { - if (!result) { - return EMPTY; - } - return this.dnsApi.updateZone(result.toDnsAddZoneRequest()); + filter((x) => !!x), + switchMap((result: DnsForwardZone) => { + return this.dnsApi.changeForwardZones(result); }), ) .subscribe((result) => { @@ -122,13 +108,13 @@ export class DnsForwardZonesComponent implements OnInit { return; } - this.dnsApi.deleteZone(selectedRows.map((x) => x.name)).subscribe((x) => { + this.dnsApi.deleteForwardZones(selectedRows.map((x) => x.id)).subscribe(() => { this.toastr.success(translate('dns-forward-zones.delete-successful')); this.loadData(); }); } - onRedirectionZonesSelectionChanged(rows: DnsForwardZone[]) { + onRedirectionZonesSelectionChanged(rows: DnsForwardGetData[]) { this.selectedRedirectionZones.set(rows); } } diff --git a/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.html b/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.html index a2e52a67..12d35860 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.html +++ b/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.html @@ -6,11 +6,7 @@
- +
{{ 'dns-rule-dialog.same-as-zone-name' | transloco }} @@ -19,26 +15,21 @@
@switch (recordDataType) { @case (DnsRuleClass.IP6.valueOf()) { - + } @case (DnsRuleClass.IP4.valueOf()) { - + } @case (DnsRuleClass.SRV.valueOf()) { - + } @default { - + } }
- +
diff --git a/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.ts b/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.ts index 2b3947d1..2ec17325 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.ts +++ b/projects/multidirectory-app/src/app/features/dns/dns-rule-dialog/dns-rule-dialog.component.ts @@ -1,31 +1,15 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - DestroyRef, - inject, - OnInit, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit, ViewChild } from '@angular/core'; import { Ip6AddressValidatorDirective } from '@core/validators/ip6-address.directive'; import { IpAddressValidatorDirective } from '@core/validators/ip-address.directive'; import { MdFormComponent, MultidirectoryUiKitModule } from 'multidirectory-ui-kit'; import { RequiredWithMessageDirective } from '@core/validators/required-with-message.directive'; import { TranslocoPipe } from '@jsverse/transloco'; import { FormsModule } from '@angular/forms'; -import { - AvailableDnsRecordTypes, - DnsRuleClass, - DnsRuleType, - DnsTypeToDataType, -} from '@models/api/dns/dns-rule-type'; +import { AvailableDnsRecordTypes, DnsRuleClass, DnsRuleType, DnsTypeToDataType } from '@models/api/dns/dns-rule-type'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; import { DialogService } from '../../../components/modals/services/dialog.service'; -import { - DnsRuleDialogData, - DnsRuleDialogReturnData, -} from '../../../components/modals/interfaces/dns-rule-dialog.interface'; +import { DnsRuleDialogData, DnsRuleDialogReturnData } from '../../../components/modals/interfaces/dns-rule-dialog.interface'; import { DialogComponent } from '../../../components/modals/components/core/dialog/dialog.component'; import { DnsRule } from '@models/api/dns/dns-rule'; @@ -69,17 +53,17 @@ export class DnsRuleDialogComponent implements OnInit { set sameAsZoneName(val: boolean) { this._sameAsZoneName = val; if (val) { - this.dnsRule.name = '@'; + this.dnsRule.record_name = '@'; } this.cdr.detectChanges(); } get recordType() { - return this.dnsRule.type; + return this.dnsRule.record_type; } set recordType(type: DnsRuleType) { - this.dnsRule.type = type; + this.dnsRule.record_type = type; this.recordDataType = DnsTypeToDataType.get(type)?.valueOf() ?? -1; this.cdr.detectChanges(); } @@ -90,7 +74,7 @@ export class DnsRuleDialogComponent implements OnInit { this.formValid = x; }); this.dnsRule = this.dialogData.rule ?? {}; - this.recordType = this.dnsRule.type; + this.recordType = this.dnsRule.record_type; } onFinish(event: MouseEvent) { diff --git a/projects/multidirectory-app/src/app/features/dns/dns-rule-list-item/dns-rule-list-item.component.html b/projects/multidirectory-app/src/app/features/dns/dns-rule-list-item/dns-rule-list-item.component.html index 5104a074..7113df17 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-rule-list-item/dns-rule-list-item.component.html +++ b/projects/multidirectory-app/src/app/features/dns/dns-rule-list-item/dns-rule-list-item.component.html @@ -1,9 +1,9 @@ @if (dnsRule) {
{{ index() }}
-
{{ dnsRule.name }}
-
{{ dnsRule.value }}
-
{{ getType(dnsRule.type) }}
+
{{ dnsRule.record_name }}
+
{{ dnsRule.record_value }}
+
{{ getType(dnsRule.record_type) }}
diff --git a/projects/multidirectory-app/src/app/features/dns/dns-settings.component.ts b/projects/multidirectory-app/src/app/features/dns/dns-settings.component.ts index 1527580f..06635706 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-settings.component.ts +++ b/projects/multidirectory-app/src/app/features/dns/dns-settings.component.ts @@ -1,24 +1,17 @@ import { Component, DestroyRef, inject, OnInit } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; -import { faCircleExclamation, faL } from '@fortawesome/free-solid-svg-icons'; +import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons'; import { translate, TranslocoPipe } from '@jsverse/transloco'; -import { MuiButtonComponent, MuiTabDirective, MuiTabsComponent } from '@mflab/mui-kit'; +import { MuiTabDirective, MuiTabsComponent } from '@mflab/mui-kit'; import { AppSettingsService } from '@services/app-settings.service'; import { AppWindowsService } from '@services/app-windows.service'; import { DnsApiService } from '@services/dns-api.service'; import { ToastrService } from 'ngx-toastr'; -import { catchError, EMPTY, switchMap, take } from 'rxjs'; -import { ConfirmDialogComponent } from '../../components/modals/components/dialogs/confirm-dialog/confirm-dialog.component'; -import { - ConfirmDialogData, - ConfirmDialogReturnData, -} from '../../components/modals/interfaces/confirm-dialog.interface'; +import { EMPTY, finalize, switchMap, take } from 'rxjs'; import { DialogService } from '../../components/modals/services/dialog.service'; import DnsZonesComponent from './dns-zones/dns-zones.component'; -import { ConfirmDialogDescriptor } from '@models/api/confirm-dialog/confirm-dialog-descriptor'; import { DnsRule } from '@models/api/dns/dns-rule'; -import { DnsRuleType } from '@models/api/dns/dns-rule-type'; import { DnsSetupRequest } from '@models/api/dns/dns-setup-request'; import { DnsStatusResponse } from '@models/api/dns/dns-status-response'; import { DnsStatuses } from '@models/api/dns/dns-statuses'; @@ -81,13 +74,11 @@ export class DnsSettingsComponent implements OnInit { this.app.dnsStatusRx .pipe( takeUntilDestroyed(this.destroyRef$), - catchError((err) => { + finalize(() => { this.windows.hideSpinner(); - throw err; }), ) .subscribe((status) => { - this.windows.hideSpinner(); this.dnsStatus = status; }); @@ -100,36 +91,36 @@ export class DnsSettingsComponent implements OnInit { }); } - onDelete(toDeleteIndex: number) { - const prompt: ConfirmDialogDescriptor = { - promptHeader: translate('remove-confirmation-dialog.prompt-header'), - promptText: translate('remove-confirmation-dialog.prompt-text'), - primaryButtons: [{ id: 'yes', text: translate('remove-confirmation-dialog.yes') }], - secondaryButtons: [{ id: 'cancel', text: translate('remove-confirmation-dialog.cancel') }], - }; - - this.dialogService - .open({ - component: ConfirmDialogComponent, - dialogConfig: { - minHeight: '160px', - data: prompt, - }, - }) - .closed.pipe( - take(1), - switchMap((x) => { - if (x === 'cancel' || !x) { - return EMPTY; - } - const rule = this.enusreHostname(this.rules[toDeleteIndex]); - return this.dns.delete(rule); - }), - ) - .subscribe(() => { - this.rules = this.rules.filter((_, ind) => ind !== toDeleteIndex); - }); - } + // onDelete(toDeleteIndex: number) { + // const prompt: ConfirmDialogDescriptor = { + // promptHeader: translate('remove-confirmation-dialog.prompt-header'), + // promptText: translate('remove-confirmation-dialog.prompt-text'), + // primaryButtons: [{ id: 'yes', text: translate('remove-confirmation-dialog.yes') }], + // secondaryButtons: [{ id: 'cancel', text: translate('remove-confirmation-dialog.cancel') }], + // }; + // + // this.dialogService + // .open({ + // component: ConfirmDialogComponent, + // dialogConfig: { + // minHeight: '160px', + // data: prompt, + // }, + // }) + // .closed.pipe( + // take(1), + // switchMap((x) => { + // if (x === 'cancel' || !x) { + // return EMPTY; + // } + // const rule = this.enusreHostname(this.rules[toDeleteIndex]); + // return this.dns.deleteZoneRule(rule); + // }), + // ) + // .subscribe(() => { + // this.rules = this.rules.filter((_, ind) => ind !== toDeleteIndex); + // }); + // } handleSetupClick() { this.windows @@ -148,9 +139,9 @@ export class DnsSettingsComponent implements OnInit { }); } - private enusreHostname(rule: DnsRule): DnsRule { - const result = new DnsRule(rule); - result.name = result.name.replace('.' + this.dnsStatus.zone_name, ''); - return result; - } + // private enusreHostname(rule: DnsRule): DnsRule { + // const result = new DnsRule(rule); + // result.record_name = result.record_name?.replace('.' + this.dnsStatus.zone_name, ''); + // return result; + // } } diff --git a/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.html b/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.html index 12bac585..e97f7e4d 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.html +++ b/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.html @@ -1,25 +1,19 @@ -
- {{ 'dns-zone-details.modal-header' | transloco }} {{ zone().name }} -
+
{{ 'dns-zone-details.modal-header' | transloco }} {{ zone().name }}
- {{ 'dns-zone-details.add' | transloco }} + {{ 'dns-zone-details.add' | transloco }}
- +
- @for (type of zone().records; track $index; let indexA = $index) { + @for (type of zone().rrsets; track $index; let indexA = $index) { @for (record of type.records; track $index; let indexB = $index) { diff --git a/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.ts b/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.ts index 14a907f0..5a3b77cb 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.ts +++ b/projects/multidirectory-app/src/app/features/dns/dns-zone-details/dns-zone-details.component.ts @@ -1,28 +1,8 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - computed, - inject, - OnInit, - output, - signal, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, computed, inject, OnInit, output, signal } from '@angular/core'; import { toObservable, toSignal } from '@angular/core/rxjs-interop'; import { DnsZoneListResponse } from '@models/dns/zones/dns-zone-response'; import { DnsApiService } from '@services/dns-api.service'; -import { - BehaviorSubject, - combineLatest, - EMPTY, - filter, - lastValueFrom, - map, - pipe, - switchMap, - take, - tap, -} from 'rxjs'; +import { BehaviorSubject, combineLatest, EMPTY, filter, lastValueFrom, map, pipe, switchMap, take, tap } from 'rxjs'; import { DnsRuleListItemComponent } from '../dns-rule-list-item/dns-rule-list-item.component'; import { translate, TranslocoModule } from '@jsverse/transloco'; import { ToastrService } from 'ngx-toastr'; @@ -31,10 +11,7 @@ import { DIALOG_DATA } from '@angular/cdk/dialog'; import { DnsZoneDetailsDialogData } from './dns-zone-details-dialog-data'; import { MuiButtonComponent, MuiInputComponent } from '@mflab/mui-kit'; import { DialogService } from '../../../components/modals/services/dialog.service'; -import { - DnsRuleDialogReturnData, - DnsRuleDialogData, -} from '../../../components/modals/interfaces/dns-rule-dialog.interface'; +import { DnsRuleDialogReturnData, DnsRuleDialogData } from '../../../components/modals/interfaces/dns-rule-dialog.interface'; import { DnsRuleDialogComponent } from '../dns-rule-dialog/dns-rule-dialog.component'; import { FormsModule } from '@angular/forms'; import { DnsRule } from '@models/api/dns/dns-rule'; @@ -44,13 +21,7 @@ import { MultidirectoryUiKitModule } from 'multidirectory-ui-kit'; selector: 'app-dns-zone-details', templateUrl: './dns-zone-details.component.html', styleUrls: ['./dns-zone-details.component.scss'], - imports: [ - DnsRuleListItemComponent, - DialogComponent, - MultidirectoryUiKitModule, - FormsModule, - TranslocoModule, - ], + imports: [DnsRuleListItemComponent, DialogComponent, MultidirectoryUiKitModule, FormsModule, TranslocoModule], }) export class DnsZoneDetailsComponent implements AfterViewInit { private readonly dialogData: DnsZoneDetailsDialogData = inject(DIALOG_DATA); @@ -65,17 +36,12 @@ export class DnsZoneDetailsComponent implements AfterViewInit { readonly reloadRx = new BehaviorSubject(false); readonly zone = toSignal( - combineLatest([ - toObservable(this.zoneName), - toObservable(this.search), - this.reloadRx.asObservable(), - ]).pipe( + combineLatest([toObservable(this.zoneName), toObservable(this.search), this.reloadRx.asObservable()]).pipe( switchMap(() => this.dns.zone()), map((zones) => zones.filter((x) => x.name == this.zoneName())?.[0] ?? {}), - tap((zones) => - zones.records.filter((x) => { - x.records = x.records.filter((y) => y?.name?.includes(this.search())); - return x; + tap((zone) => + zone.rrsets.filter((x) => { + return x.name.includes(this.search()); }), ), ), @@ -105,7 +71,7 @@ export class DnsZoneDetailsComponent implements AfterViewInit { }) .closed.pipe( take(1), - switchMap((x) => (x ? this.dns.update(x) : EMPTY)), + switchMap((x) => (x ? this.dns.updateZoneRule(x, this.zone().id) : EMPTY)), ) .subscribe(() => { this.reloadRx.next(true); @@ -113,29 +79,26 @@ export class DnsZoneDetailsComponent implements AfterViewInit { }); } - onDeleteRuleClick(record: DnsRule) { - this.dns.delete(record).subscribe((response) => { + onDeleteRuleClick($event: DnsRule) { + this.dns.deleteZoneRule($event, this.zone().id).subscribe((response) => { this.zoneName.set(this.dialogData.zoneName); + + this.reloadRx.next(true); }); } - onAdd() { + onAddRule() { this.dialog .open({ component: DnsRuleDialogComponent, dialogConfig: { minHeight: '512px', - data: { rule: new DnsRule({ zone_name: this.zoneName() }) }, + data: { rule: new DnsRule({ record_name: this.zoneName() }) }, }, }) .closed.pipe( take(1), - tap((x) => { - if (!!x && !x.name.endsWith(x.zone_name)) { - x.name += '.' + x.zone_name; - } - }), - switchMap((x) => (x ? this.dns.post(x) : EMPTY)), + switchMap((x) => (x ? this.dns.addZoneRule(x, this.zone().id) : EMPTY)), ) .subscribe(() => { this.reloadRx.next(true); diff --git a/projects/multidirectory-app/src/app/features/dns/dns-zones/dns-zones.component.ts b/projects/multidirectory-app/src/app/features/dns/dns-zones/dns-zones.component.ts index daae1eb2..2f9a96d0 100644 --- a/projects/multidirectory-app/src/app/features/dns/dns-zones/dns-zones.component.ts +++ b/projects/multidirectory-app/src/app/features/dns/dns-zones/dns-zones.component.ts @@ -7,24 +7,15 @@ import { translate, TranslocoModule } from '@jsverse/transloco'; import { DialogService } from '../../../components/modals/services/dialog.service'; import { AddZoneDialogComponent } from '../add-zone-dialog/add-zone-dialog.component'; import { DnsRuleDialogComponent } from '../dns-rule-dialog/dns-rule-dialog.component'; -import { - DnsRuleDialogData, - DnsRuleDialogReturnData, -} from '../../../components/modals/interfaces/dns-rule-dialog.interface'; +import { DnsRuleDialogData, DnsRuleDialogReturnData } from '../../../components/modals/interfaces/dns-rule-dialog.interface'; import { EMPTY, switchMap, take } from 'rxjs'; import { ToastrService } from 'ngx-toastr'; import { ConfirmDialogComponent } from '../../../components/modals/components/dialogs/confirm-dialog/confirm-dialog.component'; -import { - ConfirmDialogReturnData, - ConfirmDialogData, -} from '../../../components/modals/interfaces/confirm-dialog.interface'; +import { ConfirmDialogReturnData, ConfirmDialogData } from '../../../components/modals/interfaces/confirm-dialog.interface'; import { ConfirmDialogDescriptor } from '@models/api/confirm-dialog/confirm-dialog-descriptor'; import { DnsRule } from '@models/api/dns/dns-rule'; import { MultidirectoryUiKitModule } from 'multidirectory-ui-kit'; -import { - AddZoneDialogData, - AddZoneDialogReturnData, -} from '../add-zone-dialog/add-zone-dialog.interface'; +import { AddZoneDialogData, AddZoneDialogReturnData } from '../add-zone-dialog/add-zone-dialog.interface'; @Component({ selector: 'app-dns-zones', @@ -62,27 +53,6 @@ export default class DnsZonesComponent implements OnInit { }); } - onEditRuleClick(rule: DnsRule) { - this.dialogService - .open({ - component: DnsRuleDialogComponent, - dialogConfig: { - minHeight: '360px', - data: { rule, isEdit: true }, - }, - }) - .closed.pipe( - take(1), - switchMap((x) => { - if (!x) return EMPTY; - return this.dns.update(rule); - }), - ) - .subscribe(() => { - this.toastr.success(translate('dns-settings.success')); - }); - } - onDeleteZoneClick(zone: DnsZoneListResponse) { const prompt: ConfirmDialogDescriptor = { promptHeader: translate('remove-confirmation-dialog.prompt-header'), @@ -102,7 +72,7 @@ export default class DnsZonesComponent implements OnInit { .closed.pipe(take(1)) .subscribe((result) => { if (result === 'yes') { - this.dns.deleteZone([zone.name]).subscribe((result) => { + this.dns.deleteZone([zone.id]).subscribe((result) => { this.zones = this.zones.filter((x) => x !== zone); }); return; diff --git a/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.html b/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.html index e9f6fba7..d91d6b74 100644 --- a/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.html +++ b/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.html @@ -6,11 +6,11 @@
- - + + - - + + {{ 'dns-setup.external_service' | transloco @@ -18,25 +18,13 @@ @if (useExternalService) { - + - + } - +
diff --git a/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.ts b/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.ts index 9740a49a..9cfd97eb 100644 --- a/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.ts +++ b/projects/multidirectory-app/src/app/features/forms/dns-setup/dns-setup/dns-setup.component.ts @@ -1,25 +1,9 @@ -import { - AfterViewInit, - ChangeDetectorRef, - Component, - inject, - Input, - OnDestroy, - output, - viewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, inject, Input, OnDestroy, output, viewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RequiredWithMessageDirective } from '@core/validators/required-with-message.directive'; import { TranslocoPipe } from '@jsverse/transloco'; import { DnsSetupRequest } from '@models/api/dns/dns-setup-request'; -import { DnsStatuses } from '@models/api/dns/dns-statuses'; -import { - CheckboxComponent, - MdFormComponent, - NumberComponent, - TextareaComponent, - TextboxComponent, -} from 'multidirectory-ui-kit'; +import { CheckboxComponent, MdFormComponent, NumberComponent, TextareaComponent, TextboxComponent } from 'multidirectory-ui-kit'; import { Subject, takeUntil } from 'rxjs'; @Component({ @@ -54,14 +38,10 @@ export class DnsSetupComponent implements AfterViewInit, OnDestroy { set useExternalService(value: boolean) { this._useExternalService = value; - this.dnsSetupRequest.dns_status = value ? DnsStatuses.HOSTED : DnsStatuses.SELFHOSTED; this.cdr.detectChanges(); } ngAfterViewInit(): void { - this.dnsSetupRequest.dns_status = this._useExternalService - ? DnsStatuses.HOSTED - : DnsStatuses.SELFHOSTED; this.form() .onValidChanges.pipe(takeUntil(this.unsubscribe)) .subscribe((valid) => { diff --git a/projects/multidirectory-app/src/app/features/forms/setup-kerberos/setup-kerberos.component.ts b/projects/multidirectory-app/src/app/features/forms/setup-kerberos/setup-kerberos.component.ts index b292e24e..30b4be91 100644 --- a/projects/multidirectory-app/src/app/features/forms/setup-kerberos/setup-kerberos.component.ts +++ b/projects/multidirectory-app/src/app/features/forms/setup-kerberos/setup-kerberos.component.ts @@ -13,7 +13,7 @@ import { MultidirectoryApiService } from '@services/multidirectory-api.service'; import { SetupService } from '@services/setup.service'; import { ButtonComponent, MdFormComponent, ModalInjectDirective, TextboxComponent } from 'multidirectory-ui-kit'; import { ToastrService } from 'ngx-toastr'; -import { catchError, Subject } from 'rxjs'; +import { finalize, Subject } from 'rxjs'; @Component({ selector: 'app-setup-kerberos-dialog', @@ -94,14 +94,12 @@ export class SetupKerberosDialogComponent implements OnDestroy { this.setup .kerberosSetup(this.setupRequest) .pipe( - catchError((err) => { + finalize(() => { this.modalInjector.hideSpinner(); - throw err; }), ) .subscribe(() => { this.toastr.success(translate('setup.kerberos-setup-complete')); - this.modalInjector.hideSpinner(); window.location.reload(); }); } diff --git a/projects/multidirectory-app/src/app/features/ldap-browser/components/catalog-content/views/table-view/table-view.component.ts b/projects/multidirectory-app/src/app/features/ldap-browser/components/catalog-content/views/table-view/table-view.component.ts index 9d0af97d..2b80a9a3 100644 --- a/projects/multidirectory-app/src/app/features/ldap-browser/components/catalog-content/views/table-view/table-view.component.ts +++ b/projects/multidirectory-app/src/app/features/ldap-browser/components/catalog-content/views/table-view/table-view.component.ts @@ -105,12 +105,13 @@ export class TableViewComponent implements OnInit, AfterViewInit, OnDestroy { @Input() set newRows(element: newCatalogRow | undefined) { if (element) { const name: string = element.partial_attributes.find((attr) => attr.type === 'name')?.['vals'][0] ?? ''; + const type: string = element.partial_attributes.find((attr) => attr.type === 'entityTypeName')?.['vals'][0] ?? ''; const newEl: LdapBrowserEntry = new LdapBrowserEntry({ id: element.object_name, dn: element.object_name, icon: '', name: name, - type: LdapEntryType.Contact, + type: LdapEntryType[type as keyof typeof LdapEntryType], description: '', expandable: false, attributes: element.partial_attributes, diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.html b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.html new file mode 100644 index 00000000..d9768c6a --- /dev/null +++ b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.html @@ -0,0 +1,43 @@ +@if (accessor) { +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+} diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.scss b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.scss new file mode 100644 index 00000000..d7dcc16b --- /dev/null +++ b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.scss @@ -0,0 +1,8 @@ +:host ::ng-deep .w-full select, +textarea { + width: 100%; +} + +:host ::ng-deep textarea { + width: 100%; +} diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.ts b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.ts new file mode 100644 index 00000000..d1fa375d --- /dev/null +++ b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/address/contact-properties-address.component.ts @@ -0,0 +1,46 @@ +import { AfterViewInit, Component, Input, OnDestroy } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { LdapAttributes } from '@core/ldap/ldap-attributes/ldap-attributes'; +import { TranslocoPipe } from '@jsverse/transloco'; +import { DropdownComponent, DropdownOption, TextareaComponent, TextboxComponent } from 'multidirectory-ui-kit'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'app-contact-properties-address', + templateUrl: './contact-properties-address.component.html', + styleUrls: ['./contact-properties-address.component.scss'], + imports: [TranslocoPipe, TextareaComponent, FormsModule, TextboxComponent, DropdownComponent], +}) +export class ContactPropertiesAddressComponent implements AfterViewInit, OnDestroy { + @Input() accessor: LdapAttributes = {}; + unsubscribe = new Subject(); + countries = [new DropdownOption({ title: 'Russia', value: 'Russia' }), new DropdownOption({ title: 'Kazakhstan', value: 'Kazakhstan' })]; + + private _country?: DropdownOption; + + get country(): string { + return this._country?.value; + } + + set country(value: string) { + this._country = this.countries.find((x) => x.value == value); + if (this.accessor) { + this.accessor.country = [value]; + } + } + + ngAfterViewInit(): void { + if (!this.accessor) { + return; + } + + if (this.accessor.country?.[0]) { + this.country = this.countries?.find((x) => x.value == this.accessor.country[0])?.value ?? ''; + } + } + + ngOnDestroy(): void { + this.unsubscribe.next(false); + this.unsubscribe.complete(); + } +} diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.html b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.html new file mode 100644 index 00000000..99da97fb --- /dev/null +++ b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.html @@ -0,0 +1,22 @@ + + @defer { +
+ + {{ 'user-properties-tabs.general' | transloco }} + + + {{ 'user-properties-tabs.attributes' | transloco }} + + + {{ 'user-properties-tabs.address' | transloco }} + + + {{ 'user-properties-tabs.groups' | transloco }} + +
+ + + + + } +
diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.scss b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.ts b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.ts new file mode 100644 index 00000000..d00a3d8b --- /dev/null +++ b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/contact-properties.component.ts @@ -0,0 +1,41 @@ +import { ChangeDetectorRef, Component, computed, effect, inject, input, OnInit, ViewChild } from '@angular/core'; +import { LdapAttributes } from '@core/ldap/ldap-attributes/ldap-attributes'; +import { TranslocoPipe } from '@jsverse/transloco'; +import { TabComponent, TabDirective, TabPaneComponent } from 'multidirectory-ui-kit'; +import { EntityAttributesComponent } from '../../entity-attributes/entity-attributes.component'; +import { MemberOfComponent } from '../member-of/member-of.component'; +import { ContactPropertiesAddressComponent } from './address/contact-properties-address.component'; +import { ContactPropertiesGeneralComponent } from './general/contact-properties-general.component'; + +@Component({ + selector: 'app-contact-properties', + styleUrls: ['./contact-properties.component.scss'], + templateUrl: 'contact-properties.component.html', + imports: [ + TabPaneComponent, + TabComponent, + TranslocoPipe, + ContactPropertiesGeneralComponent, + TabDirective, + EntityAttributesComponent, + ContactPropertiesAddressComponent, + MemberOfComponent, + ContactPropertiesGeneralComponent, + ], +}) +export class ContactPropertiesComponent { + @ViewChild('cg') generalProperties!: ContactPropertiesGeneralComponent; + private cdr = inject(ChangeDetectorRef); + public accessor = input.required(); + + onTabChanged() { + this.cdr.detectChanges(); + } + + get generalPropertiesValid(): boolean { + if (!this.generalProperties) { + return true; + } + return this.generalProperties.formValid; + } +} diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.html b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.html new file mode 100644 index 00000000..3cd54878 --- /dev/null +++ b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.html @@ -0,0 +1,22 @@ +@if (this.accessor) { +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+} diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.scss b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.ts b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.ts new file mode 100644 index 00000000..7de81ccb --- /dev/null +++ b/projects/multidirectory-app/src/app/features/ldap-properties/contact-properties/general/contact-properties-general.component.ts @@ -0,0 +1,81 @@ +import { + AfterViewInit, + ChangeDetectionStrategy, + Component, + effect, + inject, + Input, + OnChanges, + OnInit, + SimpleChanges, + ViewChild, +} from '@angular/core'; +import { TranslocoPipe } from '@jsverse/transloco'; +import { MdFormComponent, MultidirectoryUiKitModule } from 'multidirectory-ui-kit'; +import { debounceTime, Subject } from 'rxjs'; +import { UserCreateRequest } from '@models/api/user-create/user-create.request'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { LdapAttributes } from '@core/ldap/ldap-attributes/ldap-attributes'; + +@Component({ + selector: 'app-contact-properties-general', + standalone: true, + imports: [TranslocoPipe, MultidirectoryUiKitModule, ReactiveFormsModule, FormsModule], + templateUrl: './contact-properties-general.component.html', + styleUrl: './contact-properties-general.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ContactPropertiesGeneralComponent implements OnInit, OnChanges { + @Input() accessor!: LdapAttributes; + setupRequest = new UserCreateRequest(); + unsubscribe = new Subject(); + + private fb = inject(FormBuilder); + contactForm!: FormGroup; + + ngOnChanges(changes: SimpleChanges) { + if (changes['accessor'] && this.accessor.cn) { + if (!this.contactForm) { + this.contactForm = this.fb.group({ + cn: [{ value: this.accessor['cn'], disabled: true }, [Validators.required]], + displayName: [''], + givenName: [this.accessor['givenName'], [Validators.required]], + surname: [this.accessor['surname'], [Validators.required]], + }); + } + } + } + + ngOnInit() { + this.calcDisplayName('givenName', 'surname'); + this.calcDisplayName('surname', 'givenName'); + this.contactForm + .get('displayName') + ?.valueChanges.pipe(debounceTime(300)) + .subscribe((newValue) => { + this.accessor.displayName = newValue; + }); + } + + calcDisplayName(name: string, secondName: string) { + this.contactForm + .get(name) + ?.valueChanges.pipe(debounceTime(300)) + .subscribe((value) => { + const surname = this.contactForm.get(secondName)?.value; + const nameToSet = `${surname} ${value}`; + if (value) { + this.contactForm.get('cn')?.setValue(nameToSet); // Пример: преобразуем в верхний регистр + } + this.accessor[name] = value; + this.accessor.cn = [nameToSet]; + }); + } + + get formValid(): boolean { + if (!this.contactForm) { + return true; + } + return this.contactForm.valid; + } +} diff --git a/projects/multidirectory-app/src/app/features/login/login.component.html b/projects/multidirectory-app/src/app/features/login/login.component.html index 6bc9c480..616f6d60 100644 --- a/projects/multidirectory-app/src/app/features/login/login.component.html +++ b/projects/multidirectory-app/src/app/features/login/login.component.html @@ -2,7 +2,6 @@ -
@@ -20,21 +19,16 @@
- {{ 'login.login' | transloco }} - + {{ 'login.login' | transloco }}
diff --git a/projects/multidirectory-app/src/app/features/login/login.component.ts b/projects/multidirectory-app/src/app/features/login/login.component.ts index ce00f8a6..4dd52849 100644 --- a/projects/multidirectory-app/src/app/features/login/login.component.ts +++ b/projects/multidirectory-app/src/app/features/login/login.component.ts @@ -1,17 +1,11 @@ -import { Component, ElementRef, inject, OnDestroy, ViewChild, viewChild } from '@angular/core'; -import { - FormBuilder, - FormGroup, - FormsModule, - ReactiveFormsModule, - Validators, -} from '@angular/forms'; +import { ChangeDetectorRef, Component, ElementRef, inject, OnDestroy, ViewChild, viewChild } from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { RequiredWithMessageDirective } from '@core/validators/required-with-message.directive'; import { TranslocoPipe } from '@jsverse/transloco'; import { LoginService } from '@services/login.service'; import { ButtonComponent, TextboxComponent } from 'multidirectory-ui-kit'; -import { catchError, Subject } from 'rxjs'; +import { finalize, Subject } from 'rxjs'; import { DialogComponent } from '@components/modals/components/core/dialog/dialog.component'; import { DIALOG_COMPONENT_WRAPPER_CONFIG } from '@components/modals/constants/dialog.constants'; @@ -42,11 +36,13 @@ export class LoginComponent implements OnDestroy { private router = inject(Router); private loginService = inject(LoginService); private unsubscribe = new Subject(); + protected cdr = inject(ChangeDetectorRef); username = ''; password = ''; readonly dialogComponent = viewChild.required(DialogComponent); - loginValid = false; loginForm: FormGroup; + isFirstLoad: boolean = true; + isValid: boolean = false; autoFilled: { allAutoFilled: boolean; fields: { [key: string]: boolean } } = { fields: { username: false, password: false }, allAutoFilled: false, @@ -59,6 +55,9 @@ export class LoginComponent implements OnDestroy { username: ['', [Validators.required]], password: ['', [Validators.required]], }); + this.loginForm.valueChanges.subscribe((data) => { + this.checkValid(); + }); } ngOnDestroy(): void { @@ -68,11 +67,21 @@ export class LoginComponent implements OnDestroy { setaAutofilledValue(name: string, event: boolean) { this.autoFilled.fields[name] = event; - this.autoFilled.allAutoFilled = Object.values(this.autoFilled.fields).reduce( - (acc, curr) => acc && curr, - true, - ); + this.autoFilled.allAutoFilled = Object.values(this.autoFilled.fields).reduce((acc, curr) => acc && curr, true); this.loginForm.markAsTouched(); + if (name === 'password') { + this.checkValid(); + } + } + + checkValid() { + if (this.isFirstLoad) { + this.isFirstLoad = false; + this.isValid = !(this.loginForm.valid || this.autoFilled.allAutoFilled); + } else { + this.isValid = !this.loginForm.valid; + } + this.cdr.detectChanges(); } onLogin(event: Event) { @@ -83,13 +92,11 @@ export class LoginComponent implements OnDestroy { this.loginService .login(this.loginForm.get('username')?.value, this.loginForm.get('password')?.value) .pipe( - catchError((err) => { + finalize(() => { this.dialogComponent().hideSpinner(); - throw err; }), ) .subscribe(() => { - this.dialogComponent().hideSpinner(); this.router.navigate(['/']); }); } diff --git a/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-list.component.ts b/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-list.component.ts index b83125c8..137515e9 100644 --- a/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-list.component.ts +++ b/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-list.component.ts @@ -9,7 +9,7 @@ import { AppWindowsService } from '@services/app-windows.service'; import { MultidirectoryApiService } from '@services/multidirectory-api.service'; import { ButtonComponent, ModalInjectDirective } from 'multidirectory-ui-kit'; import { ToastrService } from 'ngx-toastr'; -import { EMPTY, switchMap, take } from 'rxjs'; +import { EMPTY, finalize, switchMap, take } from 'rxjs'; @Component({ selector: 'app-access-policy-list', @@ -46,10 +46,13 @@ export class AccessPolicySettingsComponent implements OnInit { ngOnInit(): void { this.windows.showSpinner(); - this.api.getAccessPolicy().subscribe((x) => { - this.clients = x; - this.windows.hideSpinner(); - }); + this.api + .getAccessPolicy() + .pipe(finalize(() => this.windows.hideSpinner())) + .subscribe((x) => { + this.clients = x; + this.windows.hideSpinner(); + }); } onDeleteClick(client: AccessPolicy) { diff --git a/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.html b/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.html index 8a344e6d..02e2a751 100644 --- a/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.html +++ b/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.html @@ -8,17 +8,11 @@
{{ 'access-policy-create.domain' | transloco }}
- +
{{ 'access-policy-create.ip-addresses' | transloco }}
- - + {{ 'access-policy-create.add' | transloco }} @@ -28,6 +22,7 @@
MFA
- - + @if (mfaAccess === MfaAccessEnum.SelectedGroups) { @@ -77,25 +71,15 @@ @if (!bypassAllowed) {
- + {{ 'access-policy-create.enable-2fa' | transloco }}
}
- + {{ 'access-policy-create.bypass-no-connection' | transloco }} - + {{ 'access-policy-create.bypass-service-failure' | transloco }}
@@ -105,9 +89,7 @@ @if (showConrolButton()) {
- {{ 'access-policy-create.approve' | transloco }} - + {{ 'access-policy-create.approve' | transloco }}
}
diff --git a/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.ts b/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.ts index 07093ceb..63d0ca8d 100644 --- a/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.ts +++ b/projects/multidirectory-app/src/app/features/policies/access-policy/access-policy-view/access-policy-view.component.ts @@ -1,14 +1,5 @@ import { NgClass } from '@angular/common'; -import { - Component, - inject, - Input, - input, - OnDestroy, - OnInit, - output, - viewChild, -} from '@angular/core'; +import { Component, inject, Input, input, OnDestroy, OnInit, output, viewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { AccessPolicy } from '@core/access-policy/access-policy'; @@ -16,7 +7,6 @@ import { IpRange } from '@core/access-policy/access-policy-ip-address'; import { MfaAccessEnum } from '@core/access-policy/mfa-access-enum'; import { Constants } from '@core/constants'; import { SearchQueries } from '@core/ldap/search'; -import { IpAddressValidatorDirective } from '@core/validators/ip-address.directive'; import { RequiredWithMessageDirective } from '@core/validators/required-with-message.directive'; import { IpListDialogComponent } from 'projects/multidirectory-app/src/app/components/modals/components/dialogs/access-policy-ip-list/ip-list-dialog.component'; import { translate, TranslocoPipe } from '@jsverse/transloco'; @@ -33,12 +23,13 @@ import { TextboxComponent, } from 'multidirectory-ui-kit'; import { ToastrService } from 'ngx-toastr'; -import { from, map, Observable, of, Subject, switchMap, take, takeUntil } from 'rxjs'; -import { DialogService } from '../../../../components/modals/services/dialog.service'; -import { IplistDialogData } from '../../../../components/modals/interfaces/ip-list-dialog.interface'; +import { finalize, from, map, Observable, of, Subject, switchMap, take } from 'rxjs'; +import { DialogService } from '@components/modals/services/dialog.service'; +import { IplistDialogData } from '@components/modals/interfaces/ip-list-dialog.interface'; import { MultiselectModel } from 'projects/multidirectory-ui-kit/src/lib/components/multiselect/mutliselect-model'; import { LdapTreeviewService } from '@services/ldap/ldap-treeview.service'; import { MaxLengthValidatorDirective } from '@core/validators/max-length.directive'; +import { UniversalIpValidatorDirective } from '@core/validators/ip-universal.directive'; @Component({ selector: 'app-access-policy-view', @@ -55,9 +46,9 @@ import { MaxLengthValidatorDirective } from '@core/validators/max-length.directi GroupComponent, ShiftCheckboxComponent, RequiredWithMessageDirective, - IpAddressValidatorDirective, TranslocoPipe, MaxLengthValidatorDirective, + UniversalIpValidatorDirective, ], }) export class AccessPolicyViewComponent implements OnInit, OnDestroy { @@ -113,11 +104,7 @@ export class AccessPolicyViewComponent implements OnInit, OnDestroy { .getMultifactor() .pipe(take(1)) .subscribe((mfSettings) => { - this.bypassAllowed = - !!mfSettings.mfa_key_ldap || - !!mfSettings.mfa_secret_ldap || - !!mfSettings.mfa_key || - !!mfSettings.mfa_secret; + this.bypassAllowed = !!mfSettings.mfa_key_ldap || !!mfSettings.mfa_secret_ldap || !!mfSettings.mfa_key || !!mfSettings.mfa_secret; }); } @@ -128,30 +115,27 @@ export class AccessPolicyViewComponent implements OnInit, OnDestroy { load() { this.windows.showSpinner(); - this.api.getAccessPolicy().subscribe({ - next: (policies) => { - this.windows.hideSpinner(); - if (this.activatedRoute.snapshot.params.id) { - this.accessClient = - policies.find( - (x) => - x.id == this.activatedRoute.snapshot.params.id || x.id == this.accessPolicyId(), - ) ?? new AccessPolicy(); - } - this.ipAddresses = this.accessClient.ipRange - .map((x: any) => (x instanceof Object ? x.start + '-' + x.end : x)) - .join(', '); - - this.mfaAccess = this.accessClient.mfaStatus ?? MfaAccessEnum.Noone; - this.selectedGroups = this.accessClient.groups.map((x) => this.getMultiselectOption(x)); - this.selectedMfaGroups = this.accessClient.mfaGroups.map((x) => - this.getMultiselectOption(x), - ); - }, - error: () => { - this.windows.hideSpinner(); - }, - }); + this.api + .getAccessPolicy() + .pipe( + finalize(() => { + this.windows.hideSpinner(); + }), + ) + .subscribe({ + next: (policies) => { + this.windows.hideSpinner(); + if (this.activatedRoute.snapshot.params.id) { + this.accessClient = + policies.find((x) => x.id == this.activatedRoute.snapshot.params.id || x.id == this.accessPolicyId()) ?? new AccessPolicy(); + } + this.ipAddresses = this.accessClient.ipRange.map((x: any) => (x instanceof Object ? x.start + '-' + x.end : x)).join(', '); + + this.mfaAccess = this.accessClient.mfaStatus ?? MfaAccessEnum.Noone; + this.selectedGroups = this.accessClient.groups.map((x) => this.getMultiselectOption(x)); + this.selectedMfaGroups = this.accessClient.mfaGroups.map((x) => this.getMultiselectOption(x)); + }, + }); } close() { @@ -189,9 +173,7 @@ export class AccessPolicyViewComponent implements OnInit, OnDestroy { if (!result) { return; } - this.ipAddresses = result.addresses - .map((x: any) => (x instanceof Object ? x.start + '-' + x.end : x)) - .join(', '); + this.ipAddresses = result.addresses.map((x: any) => (x instanceof Object ? x.start + '-' + x.end : x)).join(', '); this.accessClient.ipRange = result.addresses; }); } @@ -219,6 +201,10 @@ export class AccessPolicyViewComponent implements OnInit, OnDestroy { }); } + onChangeSelectedItems($event: MultiselectModel[]) { + this.accessClient.groups = $event.map((x) => x.id); + } + checkMfaGroups() { this.retrieveGroups(this.mfaGroupsQuery) .pipe(take(1)) @@ -245,9 +231,7 @@ export class AccessPolicyViewComponent implements OnInit, OnDestroy { } return from(this.ldapTreeview.load('')).pipe( take(1), - switchMap((root) => - this.api.search(SearchQueries.findEntities(groupQuery, root?.[0]?.id ?? '', ['group'])), - ), + switchMap((root) => this.api.search(SearchQueries.findEntities(groupQuery, root?.[0]?.id ?? '', ['group']))), map((result) => { return result.search_result.map((x) => { const name = new RegExp(Constants.RegexGetNameFromDn).exec(x.object_name); diff --git a/projects/multidirectory-app/src/app/features/policies/password-policy/password-policy/password-policy.component.html b/projects/multidirectory-app/src/app/features/policies/password-policy/password-policy/password-policy.component.html index fbd23918..bc33c90f 100644 --- a/projects/multidirectory-app/src/app/features/policies/password-policy/password-policy/password-policy.component.html +++ b/projects/multidirectory-app/src/app/features/policies/password-policy/password-policy/password-policy.component.html @@ -102,6 +102,11 @@

{{ 'password-policy.password-complexity-settings' | transloco }}

+ +
+ + +
diff --git a/projects/multidirectory-app/src/app/features/settings/mulifactor-settings/mf-integration-form/mf-integration-form.component.ts b/projects/multidirectory-app/src/app/features/settings/mulifactor-settings/mf-integration-form/mf-integration-form.component.ts index e44080c0..c747cbc1 100644 --- a/projects/multidirectory-app/src/app/features/settings/mulifactor-settings/mf-integration-form/mf-integration-form.component.ts +++ b/projects/multidirectory-app/src/app/features/settings/mulifactor-settings/mf-integration-form/mf-integration-form.component.ts @@ -4,28 +4,15 @@ import { MfKeyValidatorDirective } from '@core/validators/mf-keys-validator.dire import { translate, TranslocoDirective } from '@jsverse/transloco'; import { AppWindowsService } from '@services/app-windows.service'; import { MultidirectoryApiService } from '@services/multidirectory-api.service'; -import { - ButtonComponent, - MdFormComponent, - TextboxComponent, - TooltipComponent, -} from 'multidirectory-ui-kit'; +import { ButtonComponent, MdFormComponent, TextboxComponent, TooltipComponent } from 'multidirectory-ui-kit'; import { ToastrService } from 'ngx-toastr'; -import { catchError } from 'rxjs'; +import { finalize } from 'rxjs'; @Component({ selector: 'app-mf-integration-form', templateUrl: './mf-integration-form.component.html', styleUrls: ['./mf-integration-form.component.scss'], - imports: [ - TranslocoDirective, - MdFormComponent, - TooltipComponent, - TextboxComponent, - FormsModule, - MfKeyValidatorDirective, - ButtonComponent, - ], + imports: [TranslocoDirective, MdFormComponent, TooltipComponent, TextboxComponent, FormsModule, MfKeyValidatorDirective, ButtonComponent], }) export class MfIntegrationFormComponent implements OnInit { private api = inject(MultidirectoryApiService); @@ -49,16 +36,14 @@ export class MfIntegrationFormComponent implements OnInit { this.api .setupMultifactor(this.apiKey, this.apiSecret, this.scope == 'ldap') .pipe( - catchError((err) => { + finalize(() => { this.windows.hideSpinner(); - throw err; }), ) .subscribe((result) => { if (result) { this.toastr.success(translate(`${this.translocoSection}.integration-success`)); } - this.windows.hideSpinner(); }); } @@ -67,16 +52,14 @@ export class MfIntegrationFormComponent implements OnInit { this.api .clearMultifactor(this.scope) .pipe( - catchError((err) => { + finalize(() => { this.windows.hideSpinner(); - throw err; }), ) .subscribe(() => { this.toastr.success(translate(`${this.translocoSection}.clear-success`)); this.apiKey = this.apiSecret = ''; this.form().inputs.forEach((input) => input.reset()); - this.windows.hideSpinner(); }); } } diff --git a/projects/multidirectory-app/src/app/features/setup/setup/setup.component.ts b/projects/multidirectory-app/src/app/features/setup/setup/setup.component.ts index d2ecdf3a..5c127eb7 100644 --- a/projects/multidirectory-app/src/app/features/setup/setup/setup.component.ts +++ b/projects/multidirectory-app/src/app/features/setup/setup/setup.component.ts @@ -1,12 +1,4 @@ -import { - AfterViewInit, - ChangeDetectorRef, - Component, - inject, - OnDestroy, - OnInit, - viewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, inject, OnDestroy, OnInit, viewChild } from '@angular/core'; import { Router } from '@angular/router'; import { PasswordGenerator } from '@core/setup/password-generator'; import { AdminSettingsSecondComponent } from '@features/setup/admin-settings-second/admin-settings-second.component'; @@ -33,7 +25,7 @@ import { StepperComponent, } from 'multidirectory-ui-kit'; import { ToastrService } from 'ngx-toastr'; -import { catchError, Subject, takeUntil } from 'rxjs'; +import { finalize, Subject, takeUntil } from 'rxjs'; @Component({ selector: 'app-setup', @@ -74,13 +66,9 @@ export class SetupComponent implements OnInit, AfterViewInit, OnDestroy { ngOnInit(): void { this.setupRequest.domain = window.location.hostname; - this.setupRequest.setupDnsRequest.domain = window.location.hostname; - this.setupRequest.setupDnsRequest.zone_name = window.location.hostname; - this.setupRequestValidatorService.onStepValid - .pipe(takeUntil(this.unsubscribe)) - .subscribe((valid) => { - this.stepValid = valid; - }); + this.setupRequestValidatorService.onStepValid.pipe(takeUntil(this.unsubscribe)).subscribe((valid) => { + this.stepValid = valid; + }); } ngOnDestroy(): void { @@ -94,10 +82,8 @@ export class SetupComponent implements OnInit, AfterViewInit, OnDestroy { onNext() { if (this.stepper().currentIndex == 1 && this.setupRequest.generateKdcPasswords) { - this.setupRequest.krbadmin_password = this.setupRequest.krbadmin_password_repeat = - PasswordGenerator.generatePassword(); - this.setupRequest.stash_password = this.setupRequest.stash_password_repeat = - PasswordGenerator.generatePassword(); + this.setupRequest.krbadmin_password = this.setupRequest.krbadmin_password_repeat = PasswordGenerator.generatePassword(); + this.setupRequest.stash_password = this.setupRequest.stash_password_repeat = PasswordGenerator.generatePassword(); } this.modal().resizeToContentHeight(); } @@ -111,16 +97,13 @@ export class SetupComponent implements OnInit, AfterViewInit, OnDestroy { this.setup .setup(this.setupRequest) .pipe( - catchError((err) => { + finalize(() => { this.modal().hideSpinner(); this.router.navigate(['/']); - throw err; }), ) .subscribe(() => { - this.modal().hideSpinner(); this.toastr.success(translate('setup.setup-complete')); - this.router.navigate(['/']); }); } diff --git a/projects/multidirectory-app/src/app/models/api/dns/dns-forward-zone.ts b/projects/multidirectory-app/src/app/models/api/dns/dns-forward-zone.ts index b7f59731..cc62bde9 100644 --- a/projects/multidirectory-app/src/app/models/api/dns/dns-forward-zone.ts +++ b/projects/multidirectory-app/src/app/models/api/dns/dns-forward-zone.ts @@ -1,22 +1,23 @@ import { DnsAddZoneRequest, DnsZoneParam } from '@models/dhcp/areas/dhcp-add-areas-response'; export class DnsForwardZone { - name: string = ''; - type: string = 'forward'; - forwarders: string[] = ['127.0.0.1', '127.0.0.2']; + zone_name: string = ''; + servers: string[] = ['127.0.0.1', '127.0.0.2']; constructor(obj: Partial) { Object.assign(this, obj); } - toDnsAddZoneRequest(): DnsAddZoneRequest { - return new DnsAddZoneRequest({ - zone_name: this.name, - zone_type: this.type, - params: [ - new DnsZoneParam({ - name: 'forwarders', - value: this.forwarders, - }), - ], - }); +} + +export class DnsForwardGetData { + id: string = ''; + name: string = ''; + rrsets: string[] = []; + type: string = 'zone'; + servers: string[] = []; + recursion_desired: boolean = false; + kind: string = ''; + + constructor(obj: Partial) { + Object.assign(this, obj); } } diff --git a/projects/multidirectory-app/src/app/models/api/dns/dns-rule.ts b/projects/multidirectory-app/src/app/models/api/dns/dns-rule.ts index 1ea632eb..852a9869 100644 --- a/projects/multidirectory-app/src/app/models/api/dns/dns-rule.ts +++ b/projects/multidirectory-app/src/app/models/api/dns/dns-rule.ts @@ -2,21 +2,11 @@ import { Constants } from '@core/constants'; import { DnsRuleType } from './dns-rule-type'; export class DnsRule { - name = ''; - type: DnsRuleType = DnsRuleType.A; - value = ''; - ttl = Constants.DnsTTL; - zone_name = ''; - - static toRequest(obj: DnsRule): any { - return { - record_name: obj.name, - record_type: obj.type, - record_value: obj.value, - zone_name: obj.zone_name, - ttl: obj.ttl, - }; - } + record_name: string = ''; + record_type: DnsRuleType = DnsRuleType['A']; + record_value: string = '0 100 3268 md.localhost.'; + ttl?: number = Constants.DnsTTL; + content?: string = '0 100 3268 md.localhost.'; constructor(obj: Partial) { Object.assign(this, obj); diff --git a/projects/multidirectory-app/src/app/models/api/dns/dns-setup-request.ts b/projects/multidirectory-app/src/app/models/api/dns/dns-setup-request.ts index 82776f60..b03bc8af 100644 --- a/projects/multidirectory-app/src/app/models/api/dns/dns-setup-request.ts +++ b/projects/multidirectory-app/src/app/models/api/dns/dns-setup-request.ts @@ -2,12 +2,9 @@ import { Constants } from '@core/constants'; import { DnsStatuses } from './dns-statuses'; export class DnsSetupRequest { - zone_name = ''; - domain = ''; dns_ip_address?: string = undefined; - default_ttl = Constants.DnsTTL; + default_ttl? = Constants.DnsTTL; tsig_key?: string = undefined; - dns_status: string = DnsStatuses.SELFHOSTED; constructor(obj: Partial) { Object.assign(this, obj); diff --git a/projects/multidirectory-app/src/app/models/dhcp/areas/dhcp-add-areas-response.ts b/projects/multidirectory-app/src/app/models/dhcp/areas/dhcp-add-areas-response.ts index 95eb293e..fcf92d26 100644 --- a/projects/multidirectory-app/src/app/models/dhcp/areas/dhcp-add-areas-response.ts +++ b/projects/multidirectory-app/src/app/models/dhcp/areas/dhcp-add-areas-response.ts @@ -11,9 +11,8 @@ export class DnsZoneParam { export class DnsAddZoneRequest { zone_name: string = ''; - zone_type: string = ''; - ttl = Constants.DnsTTL; - params: DnsZoneParam[] = []; + nameserver_ip: string = ''; + dnssec: boolean = false; constructor(obj: Partial) { Object.assign(this, obj); } diff --git a/projects/multidirectory-app/src/app/models/dns/zones/dns-zone-response.ts b/projects/multidirectory-app/src/app/models/dns/zones/dns-zone-response.ts index 43ff4b53..abbb25ec 100644 --- a/projects/multidirectory-app/src/app/models/dns/zones/dns-zone-response.ts +++ b/projects/multidirectory-app/src/app/models/dns/zones/dns-zone-response.ts @@ -1,8 +1,11 @@ import { DnsRule } from '@models/api/dns/dns-rule'; export class DnsZoneRecordWithType { - type = ''; + name: string = ''; + type: string = ''; records: DnsRule[] = []; + changetype = null; + ttl: number = 3600; constructor(obj: Partial) { Object.assign(this, obj); } @@ -11,7 +14,12 @@ export class DnsZoneRecordWithType { export class DnsZoneListResponse { name = ''; type = ''; - records: DnsZoneRecordWithType[] = []; + zone_name? = ''; + id: string = ''; + rrsets: DnsZoneRecordWithType[] = []; + dnssec: boolean = false; + nameservers: string[] = []; + kind: string = 'Master'; constructor(obj: Partial) { Object.assign(this, obj); diff --git a/projects/multidirectory-app/src/app/services/dns-api.service.ts b/projects/multidirectory-app/src/app/services/dns-api.service.ts index 5be1db8a..9fd128af 100644 --- a/projects/multidirectory-app/src/app/services/dns-api.service.ts +++ b/projects/multidirectory-app/src/app/services/dns-api.service.ts @@ -1,13 +1,8 @@ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { ApiAdapter } from '@core/api/api-adapter'; import { DnsAdapterSettings } from '@core/api/dns-adapter.settings'; import { MultidirectoryAdapterSettings } from '@core/api/multidirectory-adapter.settings'; -import { DnsForwardZonesComponent } from '@features/dns/dns-forward-zones/dns-forward-zones.component'; -import { - DnsCheckForwardZoneRequest, - DnsCheckForwardZoneResponse, - DnsServerOption, -} from '@models/api/dns/dns-check-forward-zone'; +import { DnsCheckForwardZoneRequest, DnsCheckForwardZoneResponse, DnsServerOption } from '@models/api/dns/dns-check-forward-zone'; import { DnsForwardZone } from '@models/api/dns/dns-forward-zone'; import { DnsRule } from '@models/api/dns/dns-rule'; import { DnsRuleType } from '@models/api/dns/dns-rule-type'; @@ -15,8 +10,8 @@ import { DnsServiceResponse } from '@models/api/dns/dns-service-response'; import { DnsSetupRequest } from '@models/api/dns/dns-setup-request'; import { DnsStatusResponse } from '@models/api/dns/dns-status-response'; import { DnsAddZoneRequest } from '@models/dhcp/areas/dhcp-add-areas-response'; -import { DnsZoneListResponse, DnsZoneRecordWithType } from '@models/dns/zones/dns-zone-response'; -import { map, Observable, of, tap } from 'rxjs'; +import { DnsZoneListResponse } from '@models/dns/zones/dns-zone-response'; +import { BehaviorSubject, concatMap, exhaustMap, map, merge, Observable, switchMap, tap } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -29,35 +24,38 @@ export class DnsApiService { return this.httpClient.get('dns/record').execute(); } - post(rule: DnsRule): Observable { - return this.httpClient - .post('dns/record', DnsRule.toRequest(rule)) - .execute() - .pipe(map(() => rule)); - } - - update(rule: DnsRule): Observable { - return this.httpClient - .patch('dns/record', DnsRule.toRequest(rule)) - .execute() - .pipe(map(() => rule)); + readonly dnsStatusRx: BehaviorSubject = new BehaviorSubject(false); + get $dnsStatus(): Observable { + return this.dnsStatusRx.asObservable(); } - delete(rule: DnsRule): Observable { - return this.httpClient.delete('dns/record', DnsRule.toRequest(rule)).execute(); + set dnsStatus(data: boolean) { + this.dnsStatusRx.next(data); } status(): Observable { - return this.dnsHttpClient.get('dns/status').execute(); + return this.dnsHttpClient + .get('dns/status') + .execute() + .pipe(tap((status: DnsStatusResponse) => (this.dnsStatus = status.dns_status === '1'))); } setup(request: DnsSetupRequest): Observable { + request = !!request.dns_ip_address ? request : {}; + const newState = !!request.dns_ip_address ? '0' : '1'; return this.dnsHttpClient - .post('dns/setup', request) + .post('dns/state', { state: newState }) .execute() - .pipe(map((x) => !!x)); + .pipe( + concatMap((state) => { + if (!!request.dns_ip_address) { + return this.dnsHttpClient.post('dns/setup', request).execute(); + } + return this.dnsHttpClient.post('dns/setup').execute(); + }), + tap(() => this.status()), + ); } - zone(): Observable { return this.dnsHttpClient .get(`dns/zone`) @@ -65,33 +63,54 @@ export class DnsApiService { .pipe( tap((x) => { for (let zone of x) { - for (let type of zone.records) { - for (let record of type.records) { - record.type = type.type as DnsRuleType; - record.zone_name = zone.name; - } + for (let type of zone.rrsets) { + type.records = type.records.map((record) => ({ + record_type: type.type as DnsRuleType, + record_name: type.name, + record_value: String(record.content ?? ''), + ttl: type.ttl, + })); } } }), ); } - addZone(request: DnsAddZoneRequest): Observable { return this.dnsHttpClient.post('dns/zone', request).execute(); } updateZone(request: DnsAddZoneRequest): any { return this.dnsHttpClient.patch('dns/zone', request).execute(); } - deleteZone(zoneNames: string[]) { - return this.dnsHttpClient.delete('dns/zone', { zone_names: zoneNames }).execute(); + deleteZone(zone_ids: string[]) { + return this.dnsHttpClient.delete('dns/zone', { zone_ids: zone_ids }).execute(); + } + + getZone(zone_id: string): any { + return this.dnsHttpClient.patch(`dns/record/${zone_id}`).execute(); + } + addZoneRule(request: DnsRule, zone_id: string): Observable { + return this.dnsHttpClient.post(`dns/record/${zone_id}`, request).execute(); + } + updateZoneRule(request: DnsRule, zone_id: string): any { + return this.dnsHttpClient.patch(`dns/record/${zone_id}`, request).execute(); + } + deleteZoneRule(request: DnsRule, zone_id: string) { + return this.dnsHttpClient.delete(`dns/record/${zone_id}`, request).execute(); } getForwardZones(): Observable { return this.dnsHttpClient.get('dns/zone/forward').execute(); } + addForwardZone(request: DnsForwardZone): Observable { + return this.dnsHttpClient.post('dns/zone/forward', request).execute(); + } + changeForwardZones(request: DnsForwardZone): Observable { + return this.dnsHttpClient.patch('dns/zone/forward', request).execute(); + } + deleteForwardZones(zone_ids: string[]): Observable { + return this.dnsHttpClient.delete('dns/zone/forward', { zone_ids: zone_ids }).execute(); + } checkForwardZone(request: DnsCheckForwardZoneRequest): Observable { - return this.dnsHttpClient - .post('dns/forward_check', request) - .execute(); + return this.dnsHttpClient.post('dns/forward_check', request).execute(); } getServerOptions(): Observable { return this.dnsHttpClient.get('dns/server/options').execute(); diff --git a/projects/multidirectory-app/src/assets/i18n/en-US.json b/projects/multidirectory-app/src/assets/i18n/en-US.json index d30f4c17..286a3786 100644 --- a/projects/multidirectory-app/src/assets/i18n/en-US.json +++ b/projects/multidirectory-app/src/assets/i18n/en-US.json @@ -281,6 +281,7 @@ "constructed-only": "Constructed", "backlinks-only": "Backlinks", "system-only": "System Only", + "dnssec": "DNSSEC", "filter": "Filter" }, "member-of": { @@ -734,7 +735,8 @@ "modal-header": "Add a new zone", "header": "DNS Zone wizard", "domain-input": "Domain", - "allowed-ip-input": "Allowed IP addresses", + "zone_name": "Zone name", + "allowed-ip-input": "Allowed IP address", "ttl-input": "TTL", "add": "Add", "cancel": "Cancel", @@ -996,6 +998,7 @@ "max-repeating-symbols-in-row-count": "Max Repeating Symbols In Row", "max-sequential-keyboard-symbols-count": "Max Sequential Keyboard Symbols", "max-sequential-alphabet-symbols-count": "Max Sequential Alphabet Symbols", + "keep-password-history-items": "Keep password history (items)", "isExactMatch": "Exact match only", "lockout-threshold": "Lockout Threshold", "lockout-duration": "Lockout Duration", diff --git a/projects/multidirectory-app/src/assets/i18n/ru-RU.json b/projects/multidirectory-app/src/assets/i18n/ru-RU.json index 2a0c1521..6faeb79f 100644 --- a/projects/multidirectory-app/src/assets/i18n/ru-RU.json +++ b/projects/multidirectory-app/src/assets/i18n/ru-RU.json @@ -282,6 +282,7 @@ "constructed-only": "Constructed", "backlinks-only": "Backlinks", "system-only": "Только системные", + "dnssec": "DNSSEC", "filter": "Фильтр" }, "member-of": { @@ -730,7 +731,8 @@ "modal-header": "Добавить новую зону", "header": "Мастер добавления зон DNS", "domain-input": "Домен", - "allowed-ip-input": "Разрешенные IP адреса", + "zone_name": "Название зоны", + "allowed-ip-input": "Разрешенный IP адрес", "ttl-input": "TTL", "add": "Добавить", "apply": "Применить", @@ -1008,6 +1010,7 @@ "max-repeating-symbols-in-row-count": "Максимум повторяющихся символов подряд", "max-sequential-keyboard-symbols-count": "Максимум последовательных символов на клавиатуре", "max-sequential-alphabet-symbols-count": "Максимум последовательных букв алфавита", + "keep-password-history-items": "Сохранять историю N паролей", "forbidden-passwords": "Запрещённые пароли", "dowload-forbidden-passwords": "Скачать список запрещённых паролей", "isExactMatch": "Точное совпадение пароля",