diff --git a/CHANGELOG.md b/CHANGELOG.md
index f237a9b..46cb2d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## [1.6.0] - 2026-05-08
+### Added
+- Añadida opción en Administración para importar una base de datos OG 1.1 en la nueva version. Permite subir fichero .sql o .zip o usar parametros de conexión a la base de datos de OG 1.1
+
## [1.5.9] - 2026-05-07
### Fixed
- Asistente de particionado: en discos GPT se elimina el límite de 4 particiones primarias, se oculta la opción de añadir partición lógica/extendida y se suprime el estilo visual de partición lógica (borde amarillo e icono └)
diff --git a/ogWebconsole/src/app/app-routing.module.ts b/ogWebconsole/src/app/app-routing.module.ts
index 0ef750c..6aeffb9 100644
--- a/ogWebconsole/src/app/app-routing.module.ts
+++ b/ogWebconsole/src/app/app-routing.module.ts
@@ -34,6 +34,7 @@ import {
} from "./components/repositories/main-repository-view/main-repository-view.component";
import { EnvVarsComponent } from "./components/admin/env-vars/env-vars.component";
import { MenusComponent } from "./components/menus/menus.component";
+import { ImportLegacyComponent } from './components/admin/import-legacy/import-legacy.component';
import { OgDhcpSubnetsComponent } from "./components/ogdhcp/og-dhcp-subnets.component";
import { DhcpServersComponent } from './components/ogdhcp/dhcp-servers/dhcp-servers.component';
import { BootServersComponent } from './components/ogboot/boot-servers/boot-servers.component';
@@ -55,6 +56,7 @@ const routes: Routes = [
{ path: 'users', component: UsersComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin'] } },
{ path: 'env-vars', component: EnvVarsComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin'] } },
{ path: 'roles', component: RolesComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin'] } },
+ { path: 'import-legacy', component: ImportLegacyComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin'] } },
{ path: 'groups', component: GroupsComponent },
{ path: 'pxe-images', component: PXEimagesComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
{ path: 'pxe', component: PxeComponent, canActivate: [roleGuard], data: { allowedRoles: ['super-admin', 'ou-admin'] } },
diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts
index 744fe72..d6191ac 100644
--- a/ogWebconsole/src/app/app.module.ts
+++ b/ogWebconsole/src/app/app.module.ts
@@ -205,6 +205,7 @@ export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
}
+import { ImportLegacyComponent } from './components/admin/import-legacy/import-legacy.component';
import { PageNotFoundComponent } from './shared/page-not-found/page-not-found.component';
export function initializeApp(configService: ConfigService) {
@@ -343,6 +344,7 @@ registerLocaleData(localeEs, 'es-ES');
GlobalOgbootStatusComponent,
OgrepositoryStatusComponent,
OgdhcpStatusComponent,
+ ImportLegacyComponent,
PageNotFoundComponent
],
bootstrap: [AppComponent],
diff --git a/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.css b/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.css
new file mode 100644
index 0000000..9e686ac
--- /dev/null
+++ b/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.css
@@ -0,0 +1,146 @@
+.main-content {
+ padding: 20px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.radio-group {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 20px;
+ gap: 10px;
+}
+
+.form-container {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ max-width: 400px;
+ margin-top: 15px;
+}
+
+.help-text {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: -5px;
+}
+
+.file-input-container {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+}
+
+.file-name {
+ font-style: italic;
+ color: #666;
+}
+
+.loading-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 40px 20px;
+ text-align: center;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.warning-text {
+ color: #f44336;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+.success-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 40px;
+ text-align: center;
+ gap: 15px;
+}
+
+.success-icon {
+ font-size: 64px;
+ height: 64px;
+ width: 64px;
+ color: #4caf50;
+}
+
+.error-banner {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ text-align: center;
+ background-color: #ffebee;
+ border-radius: 8px;
+ margin-bottom: 20px;
+ border: 1px solid #ef9a9a;
+}
+
+.error-banner h3 {
+ color: #c62828;
+ margin-top: 10px;
+ margin-bottom: 5px;
+}
+
+.error-banner p {
+ color: #c62828;
+ margin: 0;
+}
+
+.error-icon {
+ font-size: 48px;
+ height: 48px;
+ width: 48px;
+}
+
+.loader-box {
+ display: block;
+ text-align: center;
+ margin: 30px 0;
+}
+
+.loader-box mat-spinner {
+ margin: 0 auto;
+}
+
+.unified-log-box {
+ display: block;
+ width: 100%;
+ box-sizing: border-box;
+ text-align: left;
+ margin-top: 30px;
+ background-color: #1e1e1e;
+ color: #e0e0e0;
+ padding: 20px;
+ border-radius: 6px;
+ border: 1px solid #333;
+}
+
+.unified-log-box h4 {
+ margin-top: 0;
+ margin-bottom: 10px;
+ color: #4caf50;
+ border-bottom: 1px solid #333;
+ padding-bottom: 5px;
+}
+
+.unified-log-box pre {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ max-height: 400px;
+ overflow-y: auto;
+ font-family: 'Consolas', 'Monaco', monospace;
+ font-size: 13px;
+ margin: 0;
+}
+
+.full-width-card {
+ width: 100% !important;
+ box-sizing: border-box;
+}
diff --git a/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.html b/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.html
new file mode 100644
index 0000000..150fb64
--- /dev/null
+++ b/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.html
@@ -0,0 +1,95 @@
+
+
+
+
+ {{ 'importLegacyDbDesc' | translate }}
+
+
+ @if (isLoading) {
+
+
+
{{ 'migrationInProgress' | translate }}
+
{{ 'migrationWaitWarning' | translate }}
+
+ } @else if (migrationStatus === 'success') {
+
+
check_circle
+
{{ 'migrationSuccess' | translate }}
+
Los datos han sido migrados correctamente a la nueva versión.
+
+
+ } @else {
+ @if (migrationStatus === 'error') {
+
+
error
+
{{ 'migrationError' | translate }}
+
Revisa el log en la parte inferior para más detalles o corrige los parámetros e inténtalo de nuevo.
+
+ }
+
+
+ {{ 'fromRemoteDb' | translate }}
+ {{ 'fromSqlFile' | translate }}
+
+
+ @if (importMode === 'db') {
+
+ }
+
+ @if (importMode === 'file') {
+
+ }
+ }
+
+ @if (outputLog) {
+
+
Consola en vivo:
+
{{ outputLog }}
+
+ }
+
+
+
diff --git a/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.ts b/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.ts
new file mode 100644
index 0000000..a34747a
--- /dev/null
+++ b/ogWebconsole/src/app/components/admin/import-legacy/import-legacy.component.ts
@@ -0,0 +1,138 @@
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { HttpClient } from '@angular/common/http';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { TranslateService } from '@ngx-translate/core';
+import { ConfigService } from '../../../services/config.service';
+
+@Component({
+ selector: 'app-import-legacy',
+ templateUrl: './import-legacy.component.html',
+ styleUrls: ['./import-legacy.component.css']
+})
+export class ImportLegacyComponent {
+ importMode: 'file' | 'db' = 'db';
+ dbForm: FormGroup;
+ selectedFile: File | null = null;
+ isLoading = false;
+ outputLog = '';
+ migrationStatus: 'idle' | 'success' | 'error' = 'idle';
+
+ constructor(
+ private fb: FormBuilder,
+ private http: HttpClient,
+ private snackBar: MatSnackBar,
+ private translate: TranslateService,
+ private configService: ConfigService
+ ) {
+ this.dbForm = this.fb.group({
+ legacyHost: ['', Validators.required],
+ legacyUser: ['', Validators.required],
+ legacyPassword: [''],
+ legacyDb: ['', Validators.required]
+ });
+ }
+
+ ngOnInit() {
+ this.checkMigrationStatus();
+ }
+
+ checkMigrationStatus() {
+ this.http.get(`${this.configService.apiUrl}/migration/import/status`).subscribe({
+ next: (response) => {
+ if (response.isRunning) {
+ this.isLoading = true;
+ this.outputLog = response.log || 'Recuperando estado de migración...';
+ this.connectToMercure();
+ }
+ },
+ error: (error) => {
+ console.error('Error checking migration status:', error);
+ }
+ });
+ }
+
+ private connectToMercure() {
+ const url = new URL(this.configService.mercureUrl);
+ url.searchParams.append('topic', '/migration');
+ const eventSource = new EventSource(url.toString());
+
+ eventSource.onmessage = (event) => {
+ const data = JSON.parse(event.data);
+
+ if (data.type === 'migration_progress') {
+ this.outputLog = data.output;
+ }
+ else if (data.type === 'migration_result') {
+ this.isLoading = false;
+ this.migrationStatus = data.status;
+ this.outputLog = data.output;
+
+ if (data.status === 'success') {
+ this.snackBar.open(this.translate.instant('migrationSuccess'), 'OK', { duration: 5000 });
+ } else {
+ this.snackBar.open(this.translate.instant('migrationError'), 'OK', { duration: 5000 });
+ }
+ eventSource.close();
+ }
+ };
+
+ eventSource.onerror = (err) => {
+ console.error("Mercure connection error", err);
+ };
+ }
+
+ onFileSelected(event: any) {
+ const file = event.target.files[0];
+ if (file) {
+ this.selectedFile = file;
+ }
+ }
+
+ resetForm() {
+ this.migrationStatus = 'idle';
+ this.outputLog = '';
+ this.selectedFile = null;
+ this.dbForm.reset();
+ }
+
+ onSubmit() {
+ if (this.importMode === 'file' && !this.selectedFile) {
+ this.snackBar.open('Please select a file', 'Close', { duration: 3000 });
+ } else if ((this.importMode === 'db' && this.dbForm.valid) || (this.importMode === 'file' && this.selectedFile)) {
+ this.isLoading = true;
+ this.outputLog = '';
+ this.migrationStatus = 'idle';
+
+ let request;
+ if (this.importMode === 'file') {
+ const formData = new FormData();
+ formData.append('sql_file', this.selectedFile!);
+ request = this.http.post(`${this.configService.apiUrl}/migration/import`, formData);
+ } else {
+ request = this.http.post(`${this.configService.apiUrl}/migration/import`, this.dbForm.value);
+ }
+
+ request.subscribe({
+ next: (response) => {
+ if (response.status === 'started') {
+ this.outputLog = 'Proceso de migración en curso. Esperando respuesta del servidor en segundo plano...';
+ this.connectToMercure();
+ } else {
+ this.isLoading = false;
+ this.migrationStatus = 'success';
+ this.outputLog = response.output || response.message;
+ this.snackBar.open(this.translate.instant('migrationSuccess'), 'OK', { duration: 5000 });
+ }
+ },
+ error: (error) => {
+ this.isLoading = false;
+ this.migrationStatus = 'error';
+ const details = error.error?.error || error.error?.details || error.message || 'Unknown error';
+ this.outputLog = details;
+ this.snackBar.open(this.translate.instant('migrationError'), 'OK', { duration: 5000 });
+ }
+ });
+ }
+ }
+}
diff --git a/ogWebconsole/src/app/layout/header/header.component.html b/ogWebconsole/src/app/layout/header/header.component.html
index 83805e2..0d59409 100644
--- a/ogWebconsole/src/app/layout/header/header.component.html
+++ b/ogWebconsole/src/app/layout/header/header.component.html
@@ -110,5 +110,8 @@
+
diff --git a/ogWebconsole/src/locale/cat.json b/ogWebconsole/src/locale/cat.json
index 0942b70..4f45d79 100644
--- a/ogWebconsole/src/locale/cat.json
+++ b/ogWebconsole/src/locale/cat.json
@@ -1524,5 +1524,20 @@
"youMustSelectAtLeastOne1": "Has de seleccionar almenys un dia de la setmana",
"youMustSelectAtLeastOne2": "Has de seleccionar almenys un mes",
"youMustSpecifyStartDate": "Has d'especificar data d'inici i data de fi",
- "youWantToPropagateTheParametersOf": "Vols propagar els paràmetres de xarxa (ogLive, repositori, menú, plantilla PXE, perfil de hardware) a tots els clients d'aquesta unitat organitzativa?"
-}
+ "youWantToPropagateTheParametersOf": "Vols propagar els paràmetres de xarxa (ogLive, repositori, menú, plantilla PXE, perfil de hardware) a tots els clients d'aquesta unitat organitzativa?",
+ "importLegacyDbTitle": "Importar des d'OG 1.1",
+ "importLegacyDbDesc": "Migra les dades d'una instal·lació OpenGnsys 1.1 a la versió 3.0",
+ "fromSqlFile": "Des de fitxer SQL",
+ "fromRemoteDb": "Des de Base de Dades remota",
+ "importButton": "Importar",
+ "sqlFileLabel": "Fitxer (.sql/.zip)",
+ "hostLabel": "Host de la Base de Dades",
+ "userLabel": "Usuari",
+ "passLabel": "Contrasenya (Opcional)",
+ "dbLabel": "Nom de la BD",
+ "migrationInProgress": "Migració en curs...",
+ "migrationWaitWarning": "Aquest procés pot trigar uns minuts. Pots navegar per altres pestanyes o recarregar la pàgina; la migració continuarà en segon pla.",
+ "migrationSuccess": "Migració completada amb èxit",
+ "migrationError": "Error durant la migració",
+ "uploadSqlOrZip": "Puja el fitxer .sql o comprimit en zip (.zip)"
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/locale/en.json b/ogWebconsole/src/locale/en.json
index 3e2cd7f..11ce961 100644
--- a/ogWebconsole/src/locale/en.json
+++ b/ogWebconsole/src/locale/en.json
@@ -1526,5 +1526,20 @@
"youMustSelectAtLeastOne1": "You must select at least one day of the week",
"youMustSelectAtLeastOne2": "You must select at least one month",
"youMustSpecifyStartDate": "You must specify start date and end date",
- "youWantToPropagateTheParametersOf": "Do you want to propagate the network parameters (ogLive, repository, menu, PXE template, hardware profile) to all clients in this organizational unit?"
-}
+ "youWantToPropagateTheParametersOf": "Do you want to propagate the network parameters (ogLive, repository, menu, PXE template, hardware profile) to all clients in this organizational unit?",
+ "importLegacyDbTitle": "Import from OG 1.1",
+ "importLegacyDbDesc": "Migrates data from an OpenGnsys 1.1 installation to version 3.0",
+ "fromSqlFile": "From SQL File",
+ "fromRemoteDb": "From Remote Database",
+ "importButton": "Import",
+ "sqlFileLabel": "File (.sql/.zip)",
+ "hostLabel": "Database Host",
+ "userLabel": "Username",
+ "passLabel": "Password (Optional)",
+ "dbLabel": "Database Name",
+ "migrationInProgress": "Migration in progress...",
+ "migrationWaitWarning": "This process can take several minutes. You can navigate to other tabs or reload the page; the migration will continue in the background.",
+ "migrationSuccess": "Migration completed successfully",
+ "migrationError": "Error during migration",
+ "uploadSqlOrZip": "Upload the .sql file or compressed in zip (.zip)"
+}
\ No newline at end of file
diff --git a/ogWebconsole/src/locale/es.json b/ogWebconsole/src/locale/es.json
index b2ce53e..ffd8fe7 100644
--- a/ogWebconsole/src/locale/es.json
+++ b/ogWebconsole/src/locale/es.json
@@ -1526,5 +1526,20 @@
"youMustSelectAtLeastOne1": "Debe seleccionar al menos un día de la semana",
"youMustSelectAtLeastOne2": "Debe seleccionar al menos un mes",
"youMustSpecifyStartDate": "Debe especificar fecha de inicio y fecha de fin",
- "youWantToPropagateTheParametersOf": "¿Desea propagar los parámetros de red (ogLive, repositorio, menú, plantilla PXE, perfil de hardware) a todos los clientes de esta unidad organizativa?"
-}
+ "youWantToPropagateTheParametersOf": "¿Desea propagar los parámetros de red (ogLive, repositorio, menú, plantilla PXE, perfil de hardware) a todos los clientes de esta unidad organizativa?",
+ "importLegacyDbTitle": "Importar desde OG 1.1",
+ "importLegacyDbDesc": "Migra los datos de una instalación OpenGnsys 1.1 a la versión 3.0",
+ "fromSqlFile": "Desde fichero SQL",
+ "fromRemoteDb": "Desde Base de Datos remota",
+ "importButton": "Importar",
+ "sqlFileLabel": "Fichero (.sql/.zip)",
+ "hostLabel": "Host de la Base de Datos",
+ "userLabel": "Usuario",
+ "passLabel": "Contraseña (Opcional)",
+ "dbLabel": "Nombre de la BD",
+ "migrationInProgress": "Migración en curso...",
+ "migrationWaitWarning": "Este proceso puede tardar varios minutos. Puedes navegar por otras pestañas o recargar la página; la migración continuará en segundo plano.",
+ "migrationSuccess": "Migración completada con éxito",
+ "migrationError": "Error durante la migración",
+ "uploadSqlOrZip": "Sube el fichero .sql o comprimido en zip (.zip)"
+}
\ No newline at end of file