Merge branch 'develop' of ssh://ognproject.evlt.uma.es:21987/opengnsys/oggui into develop
commit
df67445b53
|
@ -0,0 +1,14 @@
|
||||||
|
FROM node:22.1.0
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN npm install -g npm@latest
|
||||||
|
|
||||||
|
RUN npm install -g @angular/cli@^12.0.0
|
||||||
|
|
||||||
|
COPY . /app
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
EXPOSE 4200
|
||||||
|
|
||||||
|
CMD ["ng", "serve", "--host", "0.0.0.0", "--disable-host-check"]
|
|
@ -20,8 +20,7 @@ import { CommandsComponent } from './components/commands/main-commands/commands.
|
||||||
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
|
import { CommandsGroupsComponent } from './components/commands/commands-groups/commands-groups.component';
|
||||||
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
|
import { CommandsTaskComponent } from './components/commands/commands-task/commands-task.component';
|
||||||
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
||||||
import { PartitionAssistantComponent } from './components/commands/commands-modals/partition-assistant/partition-assistant.component';
|
import { StatusComponent } from "./components/ogdhcp/og-dhcp-subnets/status/status.component";
|
||||||
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
||||||
{
|
{
|
||||||
|
@ -38,14 +37,13 @@ const routes: Routes = [
|
||||||
{ path: 'pxe-boot-file', component: PxeBootFilesComponent },
|
{ path: 'pxe-boot-file', component: PxeBootFilesComponent },
|
||||||
{ path: 'ogboot-status', component: OgbootStatusComponent },
|
{ path: 'ogboot-status', component: OgbootStatusComponent },
|
||||||
{ path: 'dhcp', component: OgdhcpComponent },
|
{ path: 'dhcp', component: OgdhcpComponent },
|
||||||
{ path: 'dhcp-subnets', component: OgDhcpSubnetsComponent },
|
{ path: 'subnets', component: OgDhcpSubnetsComponent },
|
||||||
|
{ path: 'ogdhcp-status', component: StatusComponent },
|
||||||
{ path: 'commands', component: CommandsComponent },
|
{ path: 'commands', component: CommandsComponent },
|
||||||
{ path: 'commands-groups', component: CommandsGroupsComponent },
|
{ path: 'commands-groups', component: CommandsGroupsComponent },
|
||||||
{ path: 'commands-task', component: CommandsTaskComponent },
|
{ path: 'commands-task', component: CommandsTaskComponent },
|
||||||
{ path: 'commands-logs', component: TaskLogsComponent },
|
{ path: 'commands-logs', component: TaskLogsComponent },
|
||||||
{ path: 'calendars', component: CalendarComponent },
|
{ path: 'calendars', component: CalendarComponent },
|
||||||
{ path: 'partitionAssistant', component: PartitionAssistantComponent},
|
|
||||||
{ path: 'client/:id', component: ClientMainViewComponent },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -100,8 +100,8 @@ import { ClientTabViewComponent } from './components/groups/components/client-ta
|
||||||
import { AdvancedSearchComponent } from './components/groups/components/advanced-search/advanced-search.component';
|
import { AdvancedSearchComponent } from './components/groups/components/advanced-search/advanced-search.component';
|
||||||
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
import { TaskLogsComponent } from './components/commands/commands-task/task-logs/task-logs.component';
|
||||||
import { OrganizationalUnitTabViewComponent } from './components/groups/components/organizational-unit-tab-view/organizational-unit-tab-view.component';
|
import { OrganizationalUnitTabViewComponent } from './components/groups/components/organizational-unit-tab-view/organizational-unit-tab-view.component';
|
||||||
import { PartitionAssistantComponent } from './components/commands/commands-modals/partition-assistant/partition-assistant.component';
|
import { ServerInfoDialogComponent } from './components/ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component';
|
||||||
import { ClientMainViewComponent } from './components/groups/components/client-main-view/client-main-view.component';
|
import { StatusComponent } from './components/ogdhcp/og-dhcp-subnets/status/status.component';
|
||||||
import {MatSliderModule} from '@angular/material/slider';
|
import {MatSliderModule} from '@angular/material/slider';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -160,8 +160,8 @@ import {MatSliderModule} from '@angular/material/slider';
|
||||||
AdvancedSearchComponent,
|
AdvancedSearchComponent,
|
||||||
TaskLogsComponent,
|
TaskLogsComponent,
|
||||||
OrganizationalUnitTabViewComponent,
|
OrganizationalUnitTabViewComponent,
|
||||||
PartitionAssistantComponent,
|
ServerInfoDialogComponent,
|
||||||
ClientMainViewComponent
|
StatusComponent
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
imports: [BrowserModule,
|
imports: [BrowserModule,
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
.title {
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-button-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lists-container {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imagesLists-container {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.unidad-card {
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-string {
|
|
||||||
flex: 2;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-boolean {
|
|
||||||
flex: 1;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
|
||||||
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.paginator-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: end;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-chip-readonly-true {
|
|
||||||
background-color: #4CAF50 !important;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-chip-readonly-false {
|
|
||||||
background-color: #F44336 !important;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
<div class="header-container">
|
|
||||||
<h2 class="title">Asistente de particionado</h2>
|
|
||||||
</div>
|
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
|
|
||||||
<div class="client" *ngIf="clientInfo && clientInfo.name">
|
|
||||||
<mat-card class="result-card">
|
|
||||||
<mat-card-title>Cliente: {{clientInfo.name}}</mat-card-title>
|
|
||||||
<mat-card-subtitle>Tipo: {{clientInfo.type}}</mat-card-subtitle>
|
|
||||||
<mat-card-content>
|
|
||||||
<p><strong>IP:</strong> {{clientInfo.ip}}</p>
|
|
||||||
<p><strong>MAC:</strong> {{clientInfo.mac}}</p>
|
|
||||||
<p><strong>Número de serie:</strong> {{clientInfo.serialNumber}}</p>
|
|
||||||
<p><strong>Interfaz de red:</strong> {{clientInfo.netiface}}</p>
|
|
||||||
<p><strong>Driver de red:</strong> {{clientInfo.netDriver}}</p>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Component, Inject } from '@angular/core';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
|
|
||||||
import { ToastrService } from 'ngx-toastr';
|
|
||||||
import { DataService } from '../../main-commands/data.service';
|
|
||||||
|
|
||||||
interface ClientInfo {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
ip: string;
|
|
||||||
mac: string;
|
|
||||||
serialNumber: string;
|
|
||||||
netiface: string;
|
|
||||||
netDriver: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-partition-assistant',
|
|
||||||
templateUrl: './partition-assistant.component.html',
|
|
||||||
styleUrl: './partition-assistant.component.css'
|
|
||||||
})
|
|
||||||
export class PartitionAssistantComponent {
|
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
|
||||||
client: string[] = [];
|
|
||||||
|
|
||||||
clientInfo: ClientInfo | undefined;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private dataService: DataService,
|
|
||||||
public dialog: MatDialog,
|
|
||||||
private toastService: ToastrService,
|
|
||||||
private http: HttpClient,
|
|
||||||
@Inject(MAT_DIALOG_DATA) public data: { clients: string[] , command?: any }
|
|
||||||
) {
|
|
||||||
console.log('clients', data.clients[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.getClientInfo(this.data.clients[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
getClientInfo(uuid: string): void {
|
|
||||||
this.http.get<ClientInfo>(`${this.baseUrl}/clients/${uuid}`)
|
|
||||||
.subscribe(
|
|
||||||
(response: ClientInfo) => {
|
|
||||||
this.clientInfo = response;
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.error('Error fetching client info:', error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { CreatePxeBootFileComponent } from '../../../ogboot/pxe-boot-files/creat
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { CommandDetailComponent } from '../../../commands/main-commands/detail-command/command-detail.component';
|
import { CommandDetailComponent } from '../../../commands/main-commands/detail-command/command-detail.component';
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
import { PartitionAssistantComponent } from '../../../commands/commands-modals/partition-assistant/partition-assistant.component';
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-acctions-modal',
|
selector: 'app-acctions-modal',
|
||||||
templateUrl: './acctions-modal.component.html',
|
templateUrl: './acctions-modal.component.html',
|
||||||
|
@ -56,32 +55,6 @@ export class AcctionsModalComponent {
|
||||||
|
|
||||||
onCommandClick(command: any): void {
|
onCommandClick(command: any): void {
|
||||||
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
clients: this.selectedElements.map((uuid: any) => `/clients/${uuid}`)
|
|
||||||
};
|
|
||||||
|
|
||||||
const apiUrl = `${this.baseUrl}/commands/${command.uuid}/execute`;
|
|
||||||
console.log(this.selectedElements.length)
|
|
||||||
if (command.uuid === '01924d28-5880-734f-9187-f6b0f6c0c8d7' && this.selectedElements.length == 1) {
|
|
||||||
const dialogRef = this.dialog.open(PartitionAssistantComponent, { data: { clients: this.selectedElements }, width: '150%', height: '100%' });
|
|
||||||
}
|
|
||||||
if (command.uuid === '01924d28-5880-734f-9187-f6b0f6c0c8d7' && this.selectedElements.length != 1) {
|
|
||||||
this.toastService.error('Please select only one client to execute this command');
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
this.http.post(apiUrl, payload).subscribe({
|
|
||||||
next: () => {
|
|
||||||
console.log('Command executed successfully');
|
|
||||||
this.loadCommands();
|
|
||||||
this.toastService.success('Command executed successfully');
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error executing command:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkArray(arr: any[], chunkSize: number): any[] {
|
chunkArray(arr: any[], chunkSize: number): any[] {
|
||||||
|
|
|
@ -58,6 +58,6 @@
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions align="end">
|
||||||
<button mat-button mat-dialog-close (click)="onNoClick()" i18n="@@close-button">Cerrar</button>
|
<button mat-button mat-dialog-close (click)="onNoClick()" i18n="@@close-button">Cerrar</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|
|
@ -26,12 +26,12 @@ export class ClientViewComponent {
|
||||||
networkData = [
|
networkData = [
|
||||||
{property: 'Menú', value: this.data.client.menu ? this.data.client.menu.description : ''},
|
{property: 'Menú', value: this.data.client.menu ? this.data.client.menu.description : ''},
|
||||||
{property: 'Perfil hardware', value: this.data.client.hardwareProfile ? this.data.client.hardwareProfile.description : ''},
|
{property: 'Perfil hardware', value: this.data.client.hardwareProfile ? this.data.client.hardwareProfile.description : ''},
|
||||||
|
{property: 'Subred', value: this.data.client.subnet},
|
||||||
{property: 'OGlive', value: ''},
|
{property: 'OGlive', value: ''},
|
||||||
{property: 'Autoexec', value: ''},
|
{property: 'Autoexec', value: ''},
|
||||||
{property: 'Repositorio', value: ''},
|
{property: 'Repositorio', value: ''},
|
||||||
{property: 'Validacion', value: ''},
|
{property: 'Validacion', value: ''},
|
||||||
{property: 'Página login', value: ''},
|
{property: 'Página login', value: ''},
|
||||||
{property: 'Página validacion', value: ''},
|
|
||||||
{property: 'Fecha de creación', value: this.data.client.createdAt},
|
{property: 'Fecha de creación', value: this.data.client.createdAt},
|
||||||
{property: 'Creado por', value: this.data.client.createdBy}
|
{property: 'Creado por', value: this.data.client.createdBy}
|
||||||
];
|
];
|
||||||
|
|
|
@ -2,15 +2,12 @@
|
||||||
<mat-expansion-panel hideToggle>
|
<mat-expansion-panel hideToggle>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title> Sincronización ogBoot </mat-panel-title>
|
<mat-panel-title> Sincronización ogBoot </mat-panel-title>
|
||||||
<mat-panel-description>
|
|
||||||
<mat-icon [style.color]="getIcon().color">{{ getIcon().name }}</mat-icon>
|
|
||||||
</mat-panel-description>
|
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<p *ngIf="alertMessage">Oglives creados en servidor ogBoot: {{ alertMessage }}</p>
|
<p *ngIf="alertMessage">Oglives creados en servidor ogBoot: {{ alertMessage }}</p>
|
||||||
<p *ngIf="alertMessage">Oglives creados en servidor ogCore (base de datos): {{ length }}</p>
|
<p *ngIf="alertMessage">Oglives creados en servidor ogCore (base de datos): {{ length }}</p>
|
||||||
|
|
||||||
<div class="example-button-row">
|
<div class="example-button-row">
|
||||||
<button mat-flat-button color="primary" (click)="syncOgCore()"> Sincronizar OgCore</button>
|
<button mat-flat-button color="primary" (click)="syncOgBoot()"> Sincronizar OgCore</button>
|
||||||
</div>
|
</div>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
|
|
|
@ -199,15 +199,7 @@ export class ImagesComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getIcon(): { name: string, color: string } {
|
syncOgBoot(): void {
|
||||||
if (this.alertMessage) {
|
|
||||||
return { name: 'check_circle', color: 'green' };
|
|
||||||
} else {
|
|
||||||
return { name: 'cancel', color: 'red' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
syncOgCore(): void {
|
|
||||||
this.http.post(`${this.apiUrl}/sync`, {})
|
this.http.post(`${this.apiUrl}/sync`, {})
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.toastService.success('Sincronización completada');
|
this.toastService.success('Sincronización completada');
|
||||||
|
|
|
@ -1,18 +1,26 @@
|
||||||
<h2 mat-dialog-title>Añade clientes a a {{data.subnetName}}</h2>
|
<h2 mat-dialog-title>Añade clientes a {{data.subnetName}}</h2>
|
||||||
|
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div *ngIf="loading" class="loading-container">
|
<mat-form-field appearance="fill" class="search-select">
|
||||||
<mat-spinner></mat-spinner>
|
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" placeholder="Seleccione un cliente">
|
||||||
<p>Cargando clientes...</p>
|
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient" (optionSelected)="onOptionClientSelected($event.option.value)">
|
||||||
</div>
|
<mat-option *ngFor="let client of filteredClients | async" [value]="client">
|
||||||
<mat-form-field appearance="fill" *ngIf="!loading" style="width: 100%;">
|
{{ client.name }}
|
||||||
<mat-label>Añadir clientes</mat-label>
|
|
||||||
<mat-select [(value)]="selectedClients" multiple>
|
|
||||||
<mat-option *ngFor="let client of clients" [value]="client">
|
|
||||||
{{ client.name }} (IP: {{ client.ip }}, MAC: {{ client.mac }})
|
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-autocomplete>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div *ngIf="selectedClients.length > 0">
|
||||||
|
<h3>Clientes seleccionados:</h3>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let client of selectedClients">
|
||||||
|
{{ client.name }}
|
||||||
|
<button mat-icon-button color="warn" (click)="removeClient(client)">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { Component, OnInit, Inject } from '@angular/core';
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
|
import {Observable, startWith} from "rxjs";
|
||||||
|
import {map} from "rxjs/operators";
|
||||||
|
import {FormControl} from "@angular/forms";
|
||||||
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-add-clients-to-subnet',
|
selector: 'app-add-clients-to-subnet',
|
||||||
|
@ -12,10 +16,14 @@ export class AddClientsToSubnetComponent implements OnInit {
|
||||||
clients: any[] = [];
|
clients: any[] = [];
|
||||||
selectedClients: any[] = [];
|
selectedClients: any[] = [];
|
||||||
loading: boolean = true;
|
loading: boolean = true;
|
||||||
|
filters: { [key: string]: string } = {};
|
||||||
|
filteredClients!: Observable<any[]>;
|
||||||
|
clientControl = new FormControl();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
public dialogRef: MatDialogRef<AddClientsToSubnetComponent>,
|
public dialogRef: MatDialogRef<AddClientsToSubnetComponent>,
|
||||||
|
private toastService: ToastrService,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: { subnetUuid: string, subnetName: string }
|
@Inject(MAT_DIALOG_DATA) public data: { subnetUuid: string, subnetName: string }
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -23,35 +31,72 @@ export class AddClientsToSubnetComponent implements OnInit {
|
||||||
console.log('Selected subnet UUID:', this.data);
|
console.log('Selected subnet UUID:', this.data);
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
this.http.get<any>(`${this.baseUrl}/clients?page=1&itemsPerPage=30`).subscribe(
|
this.loadClients();
|
||||||
|
|
||||||
|
this.filteredClients = this.clientControl.valueChanges.pipe(
|
||||||
|
startWith(''),
|
||||||
|
map(value => (typeof value === 'string' ? value : value?.name)),
|
||||||
|
map(name => (name ? this._filterClients(name) : this.clients.slice()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadClients() {
|
||||||
|
this.http.get<any>( `${this.baseUrl}/clients?&page=1&itemsPerPage=10000&exists[subnet]=false`).subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.clients = response['hydra:member'];
|
this.clients = response['hydra:member'];
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.error('Error fetching clients:', error);
|
console.error('Error fetching parent units:', error);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
|
this.selectedClients.forEach(client => {
|
||||||
const postData = {
|
const postData = {
|
||||||
clients: JSON.stringify(this.selectedClients.map(client => client.uuid))
|
client: client['@id']
|
||||||
};
|
};
|
||||||
|
|
||||||
this.http.post(`${this.baseUrl}/og-dhcp/server/${this.data.subnetUuid}/post-host`, postData).subscribe(
|
this.http.post(`${this.baseUrl}/og-dhcp/server/${this.data.subnetUuid}/post-host`, postData).subscribe(
|
||||||
response => {
|
response => {
|
||||||
console.log('Clients assigned successfully:', response);
|
this.toastService.success(`Cliente ${client.name} asignado correctamente`);
|
||||||
this.dialogRef.close(this.selectedClients);
|
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.error('Error assigning clients:', error);
|
console.error(`Error al asignar el cliente ${client.name}:`, error);
|
||||||
|
this.toastService.error(`Error al asignar el cliente ${client.name}: ${error.error['hydra:description']}`);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close(this.selectedClients);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeClient(client: any) {
|
||||||
|
const index = this.selectedClients.indexOf(client);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.selectedClients.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterClients(name: string): any[] {
|
||||||
|
const filterValue = name.toLowerCase();
|
||||||
|
return this.clients.filter(client => client.name.toLowerCase().includes(filterValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
displayFnClient(client: any): string {
|
||||||
|
return client && client.name ? client.name : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
onOptionClientSelected(client: any) {
|
||||||
|
if (!this.selectedClients.includes(client)) {
|
||||||
|
this.selectedClients.push(client);
|
||||||
|
}
|
||||||
|
this.clientControl.setValue('');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,34 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
form{
|
form{
|
||||||
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spacing-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start; /* Alinea el contenido al inicio */
|
||||||
|
justify-content: space-between; /* Espacio entre los textos y los íconos */
|
||||||
|
width: 100%; /* Asegúrate de que el contenido ocupe todo el ancho */
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-icon {
|
||||||
|
margin-left: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<h2 mat-dialog-title>Añadir configuración de red</h2>
|
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} subred</h2>
|
||||||
|
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab label="Subred">
|
||||||
|
<div class="spacing-container">
|
||||||
<mat-form-field appearance="fill" class="full-width">
|
<mat-form-field appearance="fill" class="full-width">
|
||||||
<mat-label>Nombre</mat-label>
|
<mat-label>Nombre</mat-label>
|
||||||
<input matInput [(ngModel)]="name" placeholder="Nombre de la subred" required>
|
<input matInput [(ngModel)]="name" placeholder="Nombre de la subred" required>
|
||||||
|
@ -20,8 +24,33 @@
|
||||||
<mat-label>Boot File Name</mat-label>
|
<mat-label>Boot File Name</mat-label>
|
||||||
<input matInput [(ngModel)]="bootFileName" placeholder="Boot File Name" required>
|
<input matInput [(ngModel)]="bootFileName" placeholder="Boot File Name" required>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
<mat-tab *ngIf="isEditMode" label="Clientes">
|
||||||
|
<mat-list>
|
||||||
|
<ng-container *ngFor="let client of clients">
|
||||||
|
<mat-list-item >
|
||||||
|
<div class="list-item-content">
|
||||||
|
<mat-icon matListItemIcon>computer</mat-icon>
|
||||||
|
<div class="text-content">
|
||||||
|
<div matListItemTitle>{{ client.name }}</div>
|
||||||
|
<div matListItemLine>{{ client.mac }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="icon-container">
|
||||||
|
<button mat-icon-button color="warn" class="right-icon" (click)="deleteClient(client)">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-list-item>
|
||||||
|
</ng-container>
|
||||||
|
</mat-list>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions align="end">
|
||||||
<button mat-button (click)="onNoClick()">Cancelar</button>
|
<button mat-button (click)="onNoClick()">Cancelar</button>
|
||||||
<button mat-button (click)="addNetworkConfig()" cdkFocusInitial>Añadir</button>
|
<button mat-button (click)="addNetworkConfig()" cdkFocusInitial>Guardar</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import {DeleteModalComponent} from "../../../../shared/delete_modal/delete-modal/delete-modal.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-subnet',
|
selector: 'app-create-subnet',
|
||||||
|
@ -10,21 +11,42 @@ import { ToastrService } from 'ngx-toastr';
|
||||||
})
|
})
|
||||||
export class CreateSubnetComponent implements OnInit {
|
export class CreateSubnetComponent implements OnInit {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||||
|
subnetId: string | null = null;
|
||||||
name: string = '';
|
name: string = '';
|
||||||
netmask: string = '';
|
netmask: string = '';
|
||||||
ipAddress: string = '';
|
ipAddress: string = '';
|
||||||
nextServer: string = '';
|
nextServer: string = '';
|
||||||
bootFileName: string = '';
|
bootFileName: string = '';
|
||||||
|
syncronized: boolean = false;
|
||||||
|
serverId: number = 0;
|
||||||
|
clients: any[] = [];
|
||||||
|
isEditMode: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private toastService: ToastrService,
|
private toastService: ToastrService,
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
public dialogRef: MatDialogRef<CreateSubnetComponent>,
|
public dialogRef: MatDialogRef<CreateSubnetComponent>,
|
||||||
|
public dialog: MatDialog,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (this.data) {
|
||||||
|
this.loadData();
|
||||||
|
this.isEditMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData() {
|
||||||
|
this.subnetId = this.data.uuid;
|
||||||
|
this.name = this.data.name;
|
||||||
|
this.netmask = this.data.netmask;
|
||||||
|
this.ipAddress = this.data.ipAddress;
|
||||||
|
this.nextServer = this.data.nextServer;
|
||||||
|
this.bootFileName = this.data.bootFileName;
|
||||||
|
this.syncronized = this.data.syncronized;
|
||||||
|
this.serverId = this.data.serverId;
|
||||||
|
this.clients = this.data.clients
|
||||||
}
|
}
|
||||||
|
|
||||||
onNoClick(): void {
|
onNoClick(): void {
|
||||||
|
@ -40,6 +62,7 @@ export class CreateSubnetComponent implements OnInit {
|
||||||
bootFileName: this.bootFileName
|
bootFileName: this.bootFileName
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!this.data){
|
||||||
this.http.post(`${this.baseUrl}/subnets`, payload)
|
this.http.post(`${this.baseUrl}/subnets`, payload)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
|
@ -52,5 +75,39 @@ export class CreateSubnetComponent implements OnInit {
|
||||||
this.toastService.error(error.error['hydra:description']);
|
this.toastService.error(error.error['hydra:description']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.http.patch(`${this.baseUrl}/subnets/${this.subnetId}`, payload)
|
||||||
|
.subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
console.log('Success:', response);
|
||||||
|
this.toastService.success('Configuración de red actualizada exitosamente');
|
||||||
|
this.dialogRef.close();
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteClient(client: any): void {
|
||||||
|
const dialogRef = this.dialog.open(DeleteModalComponent, {
|
||||||
|
width: '300px',
|
||||||
|
data: { name: client.name }
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.http.delete(`${this.baseUrl}/og-dhcp/server/${this.subnetId}/delete-host/${client.uuid}`, {}).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toastService.success('Cliente eliminado exitosamente');
|
||||||
|
this.dialogRef.close();
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
<mat-accordion class="example-headers-align">
|
<mat-accordion class="example-headers-align">
|
||||||
<mat-expansion-panel hideToggle>
|
<mat-expansion-panel hideToggle>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title> Información de Subnets </mat-panel-title>
|
<mat-panel-title> Información en servidor ogDHCP </mat-panel-title>
|
||||||
<mat-panel-description>
|
|
||||||
<mat-icon [style.color]="getIcon().color">{{ getIcon().name }}</mat-icon>
|
|
||||||
</mat-panel-description>
|
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<p *ngIf="alertMessage">Subnets sincronizadas: {{ alertMessage }}</p>
|
|
||||||
<p *ngIf="alertMessage">Número de subnets en base de datos: {{ length }}</p>
|
|
||||||
|
|
||||||
<div class="example-button-row">
|
<div class="example-button-row">
|
||||||
<button mat-flat-button color="primary" (click)="syncSubnets()"> Sincronizar Subnets</button>
|
<button mat-flat-button color="primary" (click)="syncSubnets()"> Sincronizar base de datos</button>
|
||||||
|
</div>
|
||||||
|
<div class="example-button-row">
|
||||||
|
<button mat-flat-button color="accent" (click)="openSubnetInfoDialog()">Ver Información</button>
|
||||||
</div>
|
</div>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
|
@ -25,7 +22,7 @@
|
||||||
<mat-divider class="divider"></mat-divider>
|
<mat-divider class="divider"></mat-divider>
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<mat-form-field appearance="fill" class="search-string">
|
<mat-form-field appearance="fill" class="search-string">
|
||||||
<mat-label i18n="@@searchLabel">Buscar nombre de imagen</mat-label>
|
<mat-label i18n="@@searchLabel">Buscar nombre de la subred</mat-label>
|
||||||
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" i18n-placeholder="@@searchPlaceholder">
|
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" i18n-placeholder="@@searchPlaceholder">
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
|
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
|
||||||
|
@ -42,13 +39,35 @@
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
|
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="fill" class="search-string">
|
||||||
|
<mat-label i18n="@@searchLabel">Buscar Boot file name</mat-label>
|
||||||
|
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['bootFileName']" i18n-placeholder="@@searchPlaceholder">
|
||||||
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
|
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider class="divider"></mat-divider>
|
<mat-divider class="divider"></mat-divider>
|
||||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
|
||||||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||||
<td mat-cell *matCellDef="let subnet">
|
<td mat-cell *matCellDef="let subnet">
|
||||||
<ng-container >
|
|
||||||
|
<ng-container *ngIf="column.columnDef === 'synchronized'">
|
||||||
|
<mat-icon [color]="subnet[column.columnDef] ? 'primary' : 'warn'">
|
||||||
|
{{ subnet[column.columnDef] ? 'check_circle' : 'cancel' }}
|
||||||
|
</mat-icon>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="column.columnDef === 'clients'">
|
||||||
|
<button mat-button [matMenuTriggerFor]="menu">Ver clientes</button>
|
||||||
|
<mat-menu #menu="matMenu">
|
||||||
|
<button mat-menu-item *ngFor="let client of subnet.clients">
|
||||||
|
{{ client.name }}
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="column.columnDef !== 'synchronized' && column.columnDef !== 'clients'">
|
||||||
{{ column.cell(subnet) }}
|
{{ column.cell(subnet) }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
|
@ -59,10 +78,15 @@
|
||||||
<td mat-cell *matCellDef="let subnet" style="text-align: center;">
|
<td mat-cell *matCellDef="let subnet" style="text-align: center;">
|
||||||
<button mat-icon-button color="primary" (click)="editSubnet(subnet)" i18n="@@editSubnet">
|
<button mat-icon-button color="primary" (click)="editSubnet(subnet)" i18n="@@editSubnet">
|
||||||
<mat-icon>edit</mat-icon></button>
|
<mat-icon>edit</mat-icon></button>
|
||||||
<button mat-icon-button color="warn" (click)="deleteSubnet(subnet)"><mat-icon>delete</mat-icon></button>
|
<button mat-icon-button color="warn" (click)="toggleAction(subnet, 'delete')"><mat-icon>delete</mat-icon></button>
|
||||||
<button mat-icon-button (click)="addClientsToSubnet(subnet)">
|
<button mat-icon-button [matMenuTriggerFor]="menu">
|
||||||
<mat-icon>computer</mat-icon>
|
<mat-icon>menu</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
<mat-menu #menu="matMenu">
|
||||||
|
<button mat-menu-item (click)="toggleAction(subnet, 'post')">Crear en og-dhcp</button>
|
||||||
|
<button mat-menu-item (click)="addClientsToSubnet(subnet)">Añadir cliente</button>
|
||||||
|
<button mat-menu-item (click)="toggleAction(subnet, 'put')">Actualizar datos en og-dhcp</button>
|
||||||
|
</mat-menu>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { HttpClient } from '@angular/common/http';
|
||||||
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
|
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { AddClientsToSubnetComponent } from './add-clients-to-subnet/add-clients-to-subnet.component';
|
import { AddClientsToSubnetComponent } from './add-clients-to-subnet/add-clients-to-subnet.component';
|
||||||
|
import {ServerInfoDialogComponent} from "./server-info-dialog/server-info-dialog.component";
|
||||||
|
|
||||||
export interface Subnet {
|
export interface Subnet {
|
||||||
'@id': string;
|
'@id': string;
|
||||||
|
@ -16,6 +17,9 @@ export interface Subnet {
|
||||||
ipAddress: string;
|
ipAddress: string;
|
||||||
nextServer: string;
|
nextServer: string;
|
||||||
bootFileName: string;
|
bootFileName: string;
|
||||||
|
synchronized: boolean;
|
||||||
|
serverId: number;
|
||||||
|
clients: [];
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
uuid: string;
|
uuid: string;
|
||||||
|
@ -29,7 +33,7 @@ export interface Subnet {
|
||||||
})
|
})
|
||||||
export class OgDhcpSubnetsComponent {
|
export class OgDhcpSubnetsComponent {
|
||||||
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||||
displayedColumns: string[] = ['id', 'name', 'netmask', 'ipAddress', 'nextServer', 'bootFileName', 'actions'];
|
displayedColumns: string[] = ['id', 'name', 'netmask', 'ipAddress', 'nextServer', 'bootFileName', 'synchronized', 'serverId', 'clients', 'actions'];
|
||||||
dataSource = new MatTableDataSource<Subnet>([]);
|
dataSource = new MatTableDataSource<Subnet>([]);
|
||||||
length = 0;
|
length = 0;
|
||||||
itemsPerPage: number = 10;
|
itemsPerPage: number = 10;
|
||||||
|
@ -47,16 +51,22 @@ export class OgDhcpSubnetsComponent {
|
||||||
{ columnDef: 'ipAddress', header: 'IP Address', cell: (subnet: Subnet) => subnet.ipAddress },
|
{ columnDef: 'ipAddress', header: 'IP Address', cell: (subnet: Subnet) => subnet.ipAddress },
|
||||||
{ columnDef: 'nextServer', header: 'Next Server', cell: (subnet: Subnet) => subnet.nextServer },
|
{ columnDef: 'nextServer', header: 'Next Server', cell: (subnet: Subnet) => subnet.nextServer },
|
||||||
{ columnDef: 'bootFileName', header: 'Boot File Name', cell: (subnet: Subnet) => subnet.bootFileName },
|
{ columnDef: 'bootFileName', header: 'Boot File Name', cell: (subnet: Subnet) => subnet.bootFileName },
|
||||||
|
{ columnDef: 'synchronized', header: 'Sincronizado', cell: (subnet: Subnet) => `${subnet.synchronized}`},
|
||||||
|
{ columnDef: 'serverId', header: 'IP Servidor DHCP', cell: (subnet: Subnet) => subnet.serverId },
|
||||||
|
{ columnDef: 'clients', header: 'Lista de clientes', cell: (subnet: Subnet) => `${subnet.clients}`},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private apiUrl = `${this.baseUrl}/subnets`;
|
||||||
|
|
||||||
constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService) {}
|
constructor(public dialog: MatDialog, private http: HttpClient, private toastService: ToastrService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.loadSubnets();
|
this.loadSubnets();
|
||||||
|
this.loadAlert()
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSubnets() {
|
loadSubnets() {
|
||||||
this.http.get<any>(`${this.baseUrl}/subnets?page=1&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
this.http.get<any>(`${this.baseUrl}/subnets?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.dataSource.data = response['hydra:member'];
|
this.dataSource.data = response['hydra:member'];
|
||||||
this.length = response['hydra:totalItems'];
|
this.length = response['hydra:totalItems'];
|
||||||
|
@ -72,36 +82,55 @@ export class OgDhcpSubnetsComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
syncSubnets() {
|
syncSubnets() {
|
||||||
console.log('Sincronizando subnets...');
|
this.http.post(`${this.apiUrl}/sync`, {})
|
||||||
}
|
.subscribe(response => {
|
||||||
|
this.toastService.success('Sincronización completada');
|
||||||
addSubnet() {
|
|
||||||
console.log('Añadiendo nueva subnet...');
|
|
||||||
const dialogRef = this.dialog.open(CreateSubnetComponent, {
|
|
||||||
width: '400px'
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
|
||||||
console.log('The dialog was closed');
|
|
||||||
this.loadSubnets()
|
this.loadSubnets()
|
||||||
|
}, error => {
|
||||||
|
console.error('Error al sincronizar', error);
|
||||||
|
this.toastService.error('Error al sincronizar');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
editSubnet(subnet: Subnet) {
|
toggleAction(subnet: any, action:string): void {
|
||||||
console.log('Editando subnet:', subnet);
|
switch (action) {
|
||||||
const dialogRef = this.dialog.open(CreateSubnetComponent, {
|
case 'get':
|
||||||
width: '400px',
|
this.http.post(`${this.baseUrl}/og-dhcp/server/${subnet.uuid}/get`, {}).subscribe({
|
||||||
data: { subnet }
|
next: () => {
|
||||||
});
|
this.toastService.success('Subred sincronizada con servidor');
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
|
||||||
console.log('The dialog was closed');
|
|
||||||
this.loadSubnets()
|
this.loadSubnets()
|
||||||
});
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Error al crear subred en servidor', error);
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
deleteSubnet(subnet: Subnet): void {
|
break;
|
||||||
console.log(subnet);
|
case 'post':
|
||||||
|
this.http.post(`${this.baseUrl}/og-dhcp/server/${subnet.uuid}/post`, {}).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toastService.success('Petición de instalación enviada');
|
||||||
|
this.loadSubnets()
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Error al crear subred en servidor', error);
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'put':
|
||||||
|
this.http.put(`${this.baseUrl}/og-dhcp/server/${subnet.uuid}/put`, {}).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toastService.success('Petición de actualizacion enviada');
|
||||||
|
this.loadSubnets();
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Error al actualizar la subred en el servidor', error);
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
const dialogRef = this.dialog.open(DeleteModalComponent, {
|
const dialogRef = this.dialog.open(DeleteModalComponent, {
|
||||||
width: '300px',
|
width: '300px',
|
||||||
data: { name: subnet.name }
|
data: { name: subnet.name }
|
||||||
|
@ -109,38 +138,73 @@ export class OgDhcpSubnetsComponent {
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
const apiUrl = `${this.baseUrl}${subnet['@id']}`;
|
this.http.delete(`${this.baseUrl}/og-dhcp/server/${subnet.uuid}/delete`, {}).subscribe({
|
||||||
|
|
||||||
this.http.delete(apiUrl).subscribe({
|
|
||||||
next: () => {
|
next: () => {
|
||||||
console.log('Subnet deleted successfully');
|
this.toastService.success('Subred eliminada exitosamente');
|
||||||
this.dataSource.data = this.dataSource.data.filter(s => s !== subnet);
|
this.loadSubnets();
|
||||||
this.length = this.dataSource.data.length;
|
|
||||||
this.toastService.success('Subnet deleted successfully');
|
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
console.error('Error deleting subnet:', error);
|
console.error('Error al eliminar la subred', error);
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
}})
|
||||||
console.log('Subnet deletion cancelled');
|
break;
|
||||||
|
default:
|
||||||
|
console.error('Acción no soportada:', action);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addSubnet() {
|
||||||
|
const dialogRef = this.dialog.open(CreateSubnetComponent, {
|
||||||
|
width: '600px'
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
this.loadSubnets()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSubnet() {
|
editSubnet(subnet: Subnet) {
|
||||||
console.log('Update Subnet action selected');
|
const dialogRef = this.dialog.open(CreateSubnetComponent, {
|
||||||
|
width: '600px',
|
||||||
|
data: subnet
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
this.loadSubnets()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAlert() {
|
||||||
|
this.http.get(`${this.baseUrl}/og-dhcp/server/get-collection`)
|
||||||
|
.subscribe(response => {
|
||||||
|
// @ts-ignore
|
||||||
|
this.alertMessage = response.message
|
||||||
|
}, error => {
|
||||||
|
console.error('Error al cargar la información del alert', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openSubnetInfoDialog() {
|
||||||
|
this.loadAlert()
|
||||||
|
this.dialog.open(ServerInfoDialogComponent, {
|
||||||
|
width: '600px',
|
||||||
|
data: {
|
||||||
|
alertMessage: this.alertMessage,
|
||||||
|
length: this.length
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addClientsToSubnet(subnet: Subnet) {
|
addClientsToSubnet(subnet: Subnet) {
|
||||||
|
|
||||||
const dialogRef = this.dialog.open(AddClientsToSubnetComponent, {
|
const dialogRef = this.dialog.open(AddClientsToSubnetComponent, {
|
||||||
width: '600px',
|
width: '600px',
|
||||||
data: { subnetUuid: subnet.uuid, subnetName: subnet.name }
|
data: { subnetUuid: subnet.uuid, subnetName: subnet.name }
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
console.log('The dialog was closed');
|
|
||||||
this.loadSubnets();
|
this.loadSubnets();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -148,13 +212,6 @@ export class OgDhcpSubnetsComponent {
|
||||||
onPageChange(event: any) {
|
onPageChange(event: any) {
|
||||||
this.page = event.pageIndex;
|
this.page = event.pageIndex;
|
||||||
this.itemsPerPage = event.pageSize;
|
this.itemsPerPage = event.pageSize;
|
||||||
}
|
this.loadSubnets();
|
||||||
|
|
||||||
getIcon(): { name: string, color: string } {
|
|
||||||
if (this.alertMessage) {
|
|
||||||
return { name: 'check_circle', color: 'green' };
|
|
||||||
} else {
|
|
||||||
return { name: 'cancel', color: 'red' };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<h2 mat-dialog-title>Información de Subnets</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<pre>{{ data | json }}</pre>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button mat-dialog-close>Cerrar</button>
|
||||||
|
</mat-dialog-actions>
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ServerInfoDialogComponent } from './server-info-dialog.component';
|
||||||
|
|
||||||
|
describe('ServerInfoDialogComponent', () => {
|
||||||
|
let component: ServerInfoDialogComponent;
|
||||||
|
let fixture: ComponentFixture<ServerInfoDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ServerInfoDialogComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ServerInfoDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
import {Component, Inject} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA} from "@angular/material/dialog";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-server-info-dialog',
|
||||||
|
templateUrl: './server-info-dialog.component.html',
|
||||||
|
styleUrl: './server-info-dialog.component.css'
|
||||||
|
})
|
||||||
|
export class ServerInfoDialogComponent {
|
||||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: any) {}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
|
||||||
|
.disk-usage-info{
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-left: 15px;
|
||||||
|
}.dashboard {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disk-usage-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disk-usage {
|
||||||
|
flex: 2;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services-status {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services-status ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services-status li {
|
||||||
|
justify-content: left;
|
||||||
|
margin: 5px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-led {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-led.active {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-led.inactive {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.installed-oglives {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px; /* Espacio entre botones */
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.btn:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
<div class="dashboard">
|
||||||
|
<h2>OgBoot server Status</h2>
|
||||||
|
|
||||||
|
<div class="disk-usage-container">
|
||||||
|
<div class="disk-usage">
|
||||||
|
<h3>Uso de disco</h3>
|
||||||
|
<ngx-charts-pie-chart
|
||||||
|
[view]="view"
|
||||||
|
[scheme]="colorScheme"
|
||||||
|
[results]="diskUsageChartData"
|
||||||
|
[gradient]="gradient"
|
||||||
|
[doughnut]="isDoughnut"
|
||||||
|
[labels]="showLabels"
|
||||||
|
[legend]="showLegend">
|
||||||
|
</ngx-charts-pie-chart>
|
||||||
|
<div class="disk-usage-info">
|
||||||
|
<p>Total: {{ diskUsage.total }}</p>
|
||||||
|
<p>Ocupado: {{ diskUsage.used }}</p>
|
||||||
|
<p>Disponible: {{ diskUsage.available }}</p>
|
||||||
|
<p>Libre: {{ diskUsage.percentage }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="services-status">
|
||||||
|
<h3>Servicios</h3>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let service of getServices()">
|
||||||
|
<span
|
||||||
|
class="status-led"
|
||||||
|
[ngClass]="{ 'active': service.status === 'active', 'inactive': service.status !== 'active' }"
|
||||||
|
></span>
|
||||||
|
{{ service.name }}: {{ service.status }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="installed-oglives">
|
||||||
|
<h3>Subredes</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Boot file name</th>
|
||||||
|
<th>Next server</th>
|
||||||
|
<th>Ip</th>
|
||||||
|
<th>Ordenadores</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let subnet of subnets">
|
||||||
|
<td>{{ subnet.id }}</td>
|
||||||
|
<td>{{ subnet['boot-file-name'] }}</td>
|
||||||
|
<td>{{ subnet['next-server'] }}</td>
|
||||||
|
<td>{{ subnet.subnet }}</td>
|
||||||
|
<td>{{ subnet.reservations.length }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { StatusComponent } from './status.component';
|
||||||
|
|
||||||
|
describe('StatusComponent', () => {
|
||||||
|
let component: StatusComponent;
|
||||||
|
let fixture: ComponentFixture<StatusComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [StatusComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(StatusComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-status',
|
||||||
|
templateUrl: './status.component.html',
|
||||||
|
styleUrl: './status.component.css'
|
||||||
|
})
|
||||||
|
export class StatusComponent {
|
||||||
|
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
|
||||||
|
diskUsage: any = {};
|
||||||
|
servicesStatus: any = {};
|
||||||
|
subnets: any[] = [];
|
||||||
|
diskUsageChartData: any[] = [];
|
||||||
|
|
||||||
|
view: [number, number] = [1100, 500];
|
||||||
|
|
||||||
|
gradient: boolean = true;
|
||||||
|
showLegend: boolean = true;
|
||||||
|
showLabels: boolean = true;
|
||||||
|
isDoughnut: boolean = true;
|
||||||
|
colorScheme: any = {
|
||||||
|
domain: ['#FF6384', '#3f51b5']
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.loadStatus();
|
||||||
|
}
|
||||||
|
loadStatus(): void {
|
||||||
|
this.http.get<any>(`${this.baseUrl}/og-dhcp/status`).subscribe(data => {
|
||||||
|
this.diskUsage = data.message.disk_usage;
|
||||||
|
this.servicesStatus = data.message.services_status;
|
||||||
|
this.subnets = data.message.subnets;
|
||||||
|
|
||||||
|
this.diskUsageChartData = [
|
||||||
|
{
|
||||||
|
name: 'Usado',
|
||||||
|
value: parseFloat(this.diskUsage.used)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Disponible',
|
||||||
|
value: parseFloat(this.diskUsage.available)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, error => {
|
||||||
|
console.error('Error fetching status', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getServices(): { name: string, status: string }[] {
|
||||||
|
return Object.keys(this.servicesStatus).map(key => ({
|
||||||
|
name: key,
|
||||||
|
status: this.servicesStatus[key]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,13 @@
|
||||||
|
|
||||||
<!-- Submenu items ogdhcp -->
|
<!-- Submenu items ogdhcp -->
|
||||||
<mat-nav-list *ngIf="showOgDhcpSub" style="padding-left: 20px;">
|
<mat-nav-list *ngIf="showOgDhcpSub" style="padding-left: 20px;">
|
||||||
<mat-list-item routerLink="/dhcp-subnets">
|
<mat-list-item routerLink="/ogdhcp-status">
|
||||||
|
<span class="entry">
|
||||||
|
<mat-icon class="icon">analytics</mat-icon>
|
||||||
|
<span i18n="@@gallery">Estado</span>
|
||||||
|
</span>
|
||||||
|
</mat-list-item>
|
||||||
|
<mat-list-item routerLink="/subnets">
|
||||||
<span class="entry">
|
<span class="entry">
|
||||||
<mat-icon class="icon">lan</mat-icon>
|
<mat-icon class="icon">lan</mat-icon>
|
||||||
<span i18n="@@gallery">Subnets</span>
|
<span i18n="@@gallery">Subnets</span>
|
||||||
|
|
|
@ -16,11 +16,9 @@ export class SidebarComponent {
|
||||||
showCommandSub: boolean = false;
|
showCommandSub: boolean = false;
|
||||||
|
|
||||||
toggleOgBootSub() {
|
toggleOgBootSub() {
|
||||||
alert('El correcto funcionamiento de este componente está sujeto a la disponibilidad de la API de ogBoot');
|
|
||||||
this.showOgBootSub = !this.showOgBootSub;
|
this.showOgBootSub = !this.showOgBootSub;
|
||||||
}
|
}
|
||||||
toggleOgDhcpSub() {
|
toggleOgDhcpSub() {
|
||||||
alert('El correcto funcionamiento de este componente está sujeto a la disponibilidad de la API de ogDHCP');
|
|
||||||
this.showOgDhcpSub = !this.showOgDhcpSub;
|
this.showOgDhcpSub = !this.showOgDhcpSub;
|
||||||
}
|
}
|
||||||
toggleCommandSub() {
|
toggleCommandSub() {
|
||||||
|
|
Loading…
Reference in New Issue