diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index 8bdcfd5..7af2819 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -130,6 +130,7 @@ import { LoadingComponent } from './shared/loading/loading.component'; import { RepositoryImagesComponent } from './components/repositories/repository-images/repository-images.component'; import { InputDialogComponent } from './components/commands/commands-task/task-logs/input-dialog/input-dialog.component'; import { ManageOrganizationalUnitComponent } from './components/groups/shared/organizational-units/manage-organizational-unit/manage-organizational-unit.component'; +import { BackupImageComponent } from './components/repositories/backup-image/backup-image.component'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http, './locale/', '.json'); } @@ -216,6 +217,7 @@ export function HttpLoaderFactory(http: HttpClient) { RepositoryImagesComponent, InputDialogComponent, ManageOrganizationalUnitComponent, + BackupImageComponent, ], bootstrap: [AppComponent], imports: [BrowserModule, diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html index 8990735..b52093c 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html +++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.html @@ -67,9 +67,9 @@ - + - {{progress}}% + {{trace.progress}}% @@ -116,4 +116,4 @@ - \ No newline at end of file + diff --git a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts index 07be884..2c9e24a 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/task-logs/task-logs.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import {ChangeDetectorRef, Component, OnInit} from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, forkJoin } from 'rxjs'; import { FormControl } from '@angular/forms'; @@ -27,7 +27,7 @@ export class TaskLogsComponent implements OnInit { pageSizeOptions: number[] = [10, 20, 30, 50]; datePipe: DatePipe = new DatePipe('es-ES'); mode: ProgressBarMode = 'buffer'; - progress = 65; + progress = 0; bufferValue = 0; columns = [ @@ -86,14 +86,15 @@ export class TaskLogsComponent implements OnInit { commandControl = new FormControl(); constructor(private http: HttpClient, - private joyrideService: JoyrideService, - private dialog: MatDialog + private joyrideService: JoyrideService, + private dialog: MatDialog, + private cdr: ChangeDetectorRef ) { } ngOnInit(): void { this.loadTraces(); this.loadCommands(); - this.loadClients(); + //this.loadClients(); this.filteredCommands = this.commandControl.valueChanges.pipe( startWith(''), map(value => (typeof value === 'string' ? value : value?.name)), @@ -104,8 +105,39 @@ export class TaskLogsComponent implements OnInit { map(value => (typeof value === 'string' ? value : value?.name)), map(name => (name ? this._filterClients(name) : this.clients.slice())) ); + + const eventSource = new EventSource('http://localhost:3000/.well-known/mercure?topic=' + + encodeURIComponent(`traces`)); + + eventSource.onmessage = (event) => { + const data = JSON.parse(event.data); + if (data && data['@id']) { + this.updateTracesStatus(data['@id'], data.status, data.progress); + } + } } + private updateTracesStatus(clientUuid: string, newStatus: string, progress: Number): void { + const traceIndex = this.traces.findIndex(trace => trace['@id'] === clientUuid); + if (traceIndex !== -1) { + const updatedTraces = [...this.traces]; + + updatedTraces[traceIndex] = { + ...updatedTraces[traceIndex], + status: newStatus, + progress: progress + }; + + this.traces = updatedTraces; + this.cdr.detectChanges(); + + console.log(`Estado actualizado para la traza ${clientUuid}: ${newStatus}`); + } else { + console.warn(`Traza con UUID ${clientUuid} no encontrado en la lista.`); + } + } + + private _filterClients(name: string): any[] { const filterValue = name.toLowerCase(); return this.clients.filter(client => client.name.toLowerCase().includes(filterValue)); diff --git a/ogWebconsole/src/app/components/groups/groups.component.ts b/ogWebconsole/src/app/components/groups/groups.component.ts index eaebf71..dcdc0fd 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.ts +++ b/ogWebconsole/src/app/components/groups/groups.component.ts @@ -126,6 +126,35 @@ export class GroupsComponent implements OnInit, OnDestroy { }; this.arrayClients = this.selectedClients.data; + + const eventSource = new EventSource('http://localhost:3000/.well-known/mercure?topic=' + + encodeURIComponent(`clients`)); + + eventSource.onmessage = (event) => { + const data = JSON.parse(event.data); + if (data && data['@id']) { + this.updateClientStatus(data['@id'], data.status); + } + } + } + + private updateClientStatus(clientUuid: string, newStatus: string): void { + const clientIndex = this.selectedClients.data.findIndex(client => client['@id'] === clientUuid); + + if (clientIndex !== -1) { + const updatedClients = [...this.selectedClients.data]; + + updatedClients[clientIndex] = { + ...updatedClients[clientIndex], + status: newStatus + }; + + this.selectedClients.data = updatedClients; + + console.log(`Estado actualizado para el cliente ${clientUuid}: ${newStatus}`); + } else { + console.warn(`Cliente con UUID ${clientUuid} no encontrado en la lista.`); + } } diff --git a/ogWebconsole/src/app/components/images/images.component.html b/ogWebconsole/src/app/components/images/images.component.html index 5f169b0..44a0a0c 100644 --- a/ogWebconsole/src/app/components/images/images.component.html +++ b/ogWebconsole/src/app/components/images/images.component.html @@ -38,12 +38,10 @@ - Ver repositorios - - - {{ repository.imageRepository.name }} - - + + {{ repository.imageRepository.name }} + + diff --git a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts index 11a1500..648e229 100644 --- a/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts +++ b/ogWebconsole/src/app/components/repositories/backup-image/backup-image.component.spec.ts @@ -1,6 +1,19 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BackupImageComponent } from './backup-image.component'; +import {ImportImageComponent} from "../import-image/import-image.component"; +import {FormBuilder, ReactiveFormsModule} from "@angular/forms"; +import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog"; +import {MatFormFieldModule} from "@angular/material/form-field"; +import {MatInputModule} from "@angular/material/input"; +import {MatButtonModule} from "@angular/material/button"; +import {MatSelectModule} from "@angular/material/select"; +import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; +import {ToastrModule, ToastrService} from "ngx-toastr"; +import {TranslateModule} from "@ngx-translate/core"; +import {provideHttpClient} from "@angular/common/http"; +import {provideHttpClientTesting} from "@angular/common/http/testing"; +import {LoadingComponent} from "../../../shared/loading/loading.component"; describe('BackupImageComponent', () => { let component: BackupImageComponent; @@ -8,9 +21,33 @@ describe('BackupImageComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [BackupImageComponent] + declarations: [BackupImageComponent, LoadingComponent], + imports: [ + ReactiveFormsModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatSelectModule, + BrowserAnimationsModule, + ToastrModule.forRoot(), + TranslateModule.forRoot() + ], + providers: [ + FormBuilder, + ToastrService, + provideHttpClient(), + provideHttpClientTesting(), + { + provide: MatDialogRef, + useValue: {} + }, + { + provide: MAT_DIALOG_DATA, + useValue: {} + }] }) - .compileComponents(); + .compileComponents(); fixture = TestBed.createComponent(BackupImageComponent); component = fixture.componentInstance; diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html index 3bb5b7b..eca724f 100644 --- a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html +++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.html @@ -70,9 +70,10 @@ Obtener ficheros auxiliares - Eliminar imagen + Eliminar permanentemente Recuperar imagen de la papelera Transferir imagen + Realizar backup diff --git a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts index 69fd13f..652ebcc 100644 --- a/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts +++ b/ogWebconsole/src/app/components/repositories/repository-images/repository-images.component.ts @@ -10,6 +10,7 @@ import {Observable} from "rxjs"; import {ServerInfoDialogComponent} from "../../ogdhcp/og-dhcp-subnets/server-info-dialog/server-info-dialog.component"; import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component"; import {ExportImageComponent} from "../../images/export-image/export-image.component"; +import {BackupImageComponent} from "../backup-image/backup-image.component"; @Component({ selector: 'app-repository-images', @@ -209,13 +210,20 @@ export class RepositoryImagesComponent implements OnInit { break; case 'delete-permanent': - this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/delete-permanent`, {}).subscribe({ - next: () => { - this.toastService.success('Petición de eliminación de la papelera temporal enviada'); - this.search() - }, - error: (error) => { - this.toastService.error(error.error['hydra:description']); + this.dialog.open(DeleteModalComponent, { + width: '300px', + data: { name: image.name }, + }).afterClosed().subscribe((result) => { + if (result) { + this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/delete-permanent`, {}).subscribe({ + next: () => { + this.toastService.success('Petición de eliminación de la papelera temporal enviada'); + this.search() + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); } }); break; @@ -246,6 +254,22 @@ export class RepositoryImagesComponent implements OnInit { } }); break; + case 'backup': + this.http.get(`${this.baseUrl}${image.image['@id']}`).subscribe({ + next: (response) => { + this.dialog.open(BackupImageComponent, { + width: '600px', + data: { + image: response, + imageImageRepository: image + } + }); + }, + error: (error) => { + this.toastService.error(error.error['hydra:description']); + } + }); + break; default: console.error('Acción no soportada:', action); break;