diff --git a/.gitignore b/.gitignore
index ac68a18..a601f62 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@ debian/*.substvars
debian/*.log
debian/.debhelper/
debian/files
+.venv/
+.vscode/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2893fdd..2b9ed3b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,27 @@
# Changelog
+## [1.2.5] - 2026-03-27
+### Fixed
+- Corregido error 401 Unauthorized en la edición de menús con nombres que contienen la palabra "login". Se ha refinado el interceptor para aplicar el prefijo "Bearer" de forma selectiva mediante validación exacta del path `/auth/login`.
+
+## [1.2.4] - 2026-03-20
+### Added
+- Se ha mejorado el editor de menus para poder editar directamente la plantilla twig en lugar del html renderizado.
+
+## [1.2.3] - 2026-03-17
+### Added
+- Se ha creado una nueva opción para gestionar assets en la pantalla de menus.
+
+## [1.2.2] - 2026-03-17
+### Added
+- Se ha integrado un editor interactivo en texto plano para los menús HTML directamente en la pantalla de edición, permitiendo previsualizar y modificar el código en vivo sin necesidad de resubir archivos. Utiliza la sintaxis moderna `@if/@else` de Angular 17+ para su lógica de renderizado.
+
+## [1.2.1] - 2026-03-11
+### Added
+- Se ha añadido la funcionalidad para poder adjuntar un fichero HTML local en la creación/edición de menús, codificándolo y enviándolo en Base64 de forma transparente.
+
+### Improved
+- Se ha mejorado la UX integrando el campo de subida de ficheros en el formulario de menús (`create-menu`) utilizando la estética nativa de Angular Material.
+
## [1.2.0] - 2025-10-21
### Added
- Se ha añadido una nueva funcionalidad la cual permite poder borrar varios equipos a la vez.
diff --git a/debian/oggui.postinst b/debian/oggui.postinst
index 0346f22..996fe05 100644
--- a/debian/oggui.postinst
+++ b/debian/oggui.postinst
@@ -9,6 +9,8 @@ db_get opengnsys/oggui_ogcoreUrl
OGCORE_URL="$RET"
db_get opengnsys/oggui_ogmercureUrl
OGMERCURE_URL="$RET"
+db_get opengnsys/oggui_oglogUrl
+OGLOG_URL="$RET"
# Asegurarse de que el usuario exista
USER="opengnsys"
@@ -35,7 +37,7 @@ restore_config_if_modified() {
# Detectar si es una instalación nueva o una actualización
if [ "$1" = "configure" ] && [ -z "$2" ]; then
- jq --arg apiUrl "$OGCORE_URL" --arg mercureUrl "$OGMERCURE_URL" '.apiUrl = $apiUrl | .mercureUrl = $mercureUrl' "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
+ jq --arg apiUrl "$OGCORE_URL" --arg mercureUrl "$OGMERCURE_URL" --arg logUrl "$OGLOG_URL" '.apiUrl = $apiUrl | .mercureUrl = $mercureUrl | .logUrl = $logUrl' "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
ln -s /opt/opengnsys/oggui/etc/nginx/oggui.conf /etc/nginx/sites-enabled/oggui.conf
ln -s $CONFIG_FILE /opt/opengnsys/oggui/etc/config.json
mkdir -p /etc/nginx/certs/
diff --git a/debian/oggui.templates b/debian/oggui.templates
index dc0d172..21f5cd0 100644
--- a/debian/oggui.templates
+++ b/debian/oggui.templates
@@ -7,3 +7,8 @@ Template: opengnsys/oggui_ogmercureUrl
Type: string
Default: https://127.0.0.1:3000/.well-known/mercure
Description: Introduzca el endpoint de mercure
+
+Template: opengnsys/oggui_oglogUrl
+Type: string
+Default: https://127.0.0.1:3030/
+Description: Oglog Grafana URL
\ No newline at end of file
diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts
index ab4e07b..e2712a2 100644
--- a/ogWebconsole/src/app/app.module.ts
+++ b/ogWebconsole/src/app/app.module.ts
@@ -1,4 +1,20 @@
-import { NgModule, CUSTOM_ELEMENTS_SCHEMA, LOCALE_ID, APP_INITIALIZER } from '@angular/core';
+import { NgModule, CUSTOM_ELEMENTS_SCHEMA, LOCALE_ID, APP_INITIALIZER, Injectable } from '@angular/core';
+import { MatPaginatorIntl } from '@angular/material/paginator';
+
+@Injectable()
+class SpanishPaginatorIntl extends MatPaginatorIntl {
+ override itemsPerPageLabel = 'Elementos por página:';
+ override nextPageLabel = 'Siguiente';
+ override previousPageLabel = 'Anterior';
+ override firstPageLabel = 'Primera página';
+ override lastPageLabel = 'Última página';
+ override getRangeLabel = (page: number, pageSize: number, length: number) => {
+ if (length === 0 || pageSize === 0) return `0 de ${length}`;
+ const startIndex = page * pageSize;
+ const endIndex = Math.min(startIndex + pageSize, length);
+ return `${startIndex + 1} – ${endIndex} de ${length}`;
+ };
+}
import { ConfigService } from './services/config.service';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
@@ -106,6 +122,7 @@ import { RepositoriesComponent } from './components/repositories/repositories.co
import { ManageRepositoryComponent } from './components/repositories/manage-repository/manage-repository.component';
import { ExecuteCommandComponent } from './components/commands/main-commands/execute-command/execute-command.component';
import { DeployImageComponent } from './components/groups/components/client-main-view/deploy-image/deploy-image.component';
+import { DeployMethodConfigDialogComponent } from './components/groups/components/client-main-view/deploy-image/deploy-method-config-dialog/deploy-method-config-dialog.component';
import { MainRepositoryViewComponent } from './components/repositories/main-repository-view/main-repository-view.component';
import { ExecuteCommandOuComponent } from './components/groups/shared/execute-command-ou/execute-command-ou.component';
import { JoyrideModule } from 'ngx-joyride';
@@ -114,6 +131,7 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component';
import { MatSortModule } from '@angular/material/sort';
import { MenusComponent } from './components/menus/menus.component';
+import { AssetManagerModalComponent } from './components/menus/asset-manager-modal/asset-manager-modal.component';
import { CreateMenuComponent } from './components/menus/create-menu/create-menu.component';
import { CreateMultipleClientComponent } from './components/groups/shared/clients/create-multiple-client/create-multiple-client.component';
import { ExportImageComponent } from './components/images/export-image/export-image.component';
@@ -171,7 +189,6 @@ import { HardwareProfileComponent } from './components/hardware-profile/hardware
import { HardwareCollectionModalComponent } from './components/hardware-profile/hardware-collection-modal/hardware-collection-modal.component';
import { HardwareTypeComponent } from './components/hardware-type/hardware-type.component';
import { ShowDetailsComponent } from './components/hardware-type/show-details/show-details.component';
-
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './locale/', '.json');
}
@@ -249,6 +266,7 @@ registerLocaleData(localeEs, 'es-ES');
ExecuteCommandComponent,
ExecuteCommandOuComponent,
DeployImageComponent,
+ DeployMethodConfigDialogComponent,
MainRepositoryViewComponent,
EnvVarsComponent,
MenusComponent,
@@ -298,7 +316,8 @@ registerLocaleData(localeEs, 'es-ES');
HardwareProfileComponent,
HardwareCollectionModalComponent,
HardwareTypeComponent,
- ShowDetailsComponent
+ ShowDetailsComponent,
+ AssetManagerModalComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule,
@@ -359,6 +378,7 @@ registerLocaleData(localeEs, 'es-ES');
multi: true
},
{ provide: LOCALE_ID, useValue: 'es-ES' },
+ { provide: MatPaginatorIntl, useClass: SpanishPaginatorIntl },
provideAnimationsAsync(),
provideHttpClient(withInterceptorsFromDi()),
ConfigService,
diff --git a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.css b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.css
index b215f90..25e66c5 100644
--- a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.css
+++ b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.css
@@ -78,7 +78,7 @@ table {
}
/* Estilos para los botones de gestión */
-.schedule-btn, .script-btn {
+.schedule-btn, .script-btn, .traces-btn {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -114,7 +114,17 @@ table {
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
}
-.schedule-btn mat-icon, .script-btn mat-icon {
+.traces-btn {
+ background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);
+ color: white;
+}
+
+.traces-btn:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(255, 152, 0, 0.3);
+}
+
+.schedule-btn mat-icon, .script-btn mat-icon, .traces-btn mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
@@ -123,15 +133,58 @@ table {
justify-content: center;
}
-.schedule-btn span, .script-btn span {
+.schedule-btn span, .script-btn span, .traces-btn span {
display: inline-block;
vertical-align: middle;
line-height: 1.2;
}
+/* Indicadores de estado de trazas */
+.trace-stats {
+ display: flex;
+ gap: 4px;
+ align-items: center;
+}
+
+.trace-badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 24px;
+ height: 24px;
+ border-radius: 12px;
+ font-size: 12px;
+ font-weight: 600;
+ color: white;
+ padding: 0 6px;
+ cursor: default;
+}
+
+.trace-success {
+ background-color: #4caf50;
+}
+
+.trace-in-progress {
+ background-color: #2196f3;
+}
+
+.trace-pending {
+ background-color: #ff9800;
+}
+
+.trace-failed {
+ background-color: #f44336;
+}
+
+.trace-no-data {
+ color: #999;
+ font-style: italic;
+ font-size: 13px;
+}
+
/* Responsive para los botones */
@media (max-width: 768px) {
- .schedule-btn, .script-btn {
+ .schedule-btn, .script-btn, .traces-btn {
width: 100%;
justify-content: center;
margin: 4px 0;
diff --git a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.html b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.html
index 993c225..54f469d 100644
--- a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.html
+++ b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.html
@@ -46,11 +46,11 @@