Commit 235859f8 authored by ajlarrosa's avatar ajlarrosa

hs-12 migracion a node.js 12, ajustes varios para interacción con cluster panel

parent 43415c9f
@echo off
echo ========================================
echo Paso 1: Instalando Statik
echo ========================================
echo.
echo Instalando statik...
go get github.com/rakyll/statik
if %ERRORLEVEL% EQU 0 (
echo.
echo OK: Statik instalado correctamente
echo.
) else (
echo.
echo ERROR: No se pudo instalar statik
echo Verifica que Go este instalado y en el PATH
echo.
pause
exit /b 1
)
pause
@echo off
echo ========================================
echo Paso 2: Ejecutando Statik
echo ========================================
echo.
echo Convirtiendo archivos estaticos de web/build en codigo Go...
echo.
statik --src=web/build
if %ERRORLEVEL% EQU 0 (
echo.
echo OK: Statik ejecutado correctamente
echo Los archivos estaticos fueron embebidos en el codigo Go
echo.
) else (
echo.
echo ERROR: No se pudo ejecutar statik
echo Verifica que:
echo 1. El directorio web/build existe y tiene contenido
echo 2. Statik este instalado (ejecuta 1-instalar-statik.bat primero)
echo.
pause
exit /b 1
)
pause
# Script para compilar web-console completamente
# Autor: Generado automaticamente
# Descripcion: Compila el frontend React, ejecuta statik y compila el servidor Go
$ErrorActionPreference = "Stop"
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Script de Compilacion de Web-Console " -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Variables
$ROOT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
$WEB_DIR = Join-Path $ROOT_DIR "web"
$ENV_FILE = Join-Path $WEB_DIR ".env.local"
Write-Host "[1/5] Verificando directorios..." -ForegroundColor Yellow
if (-not (Test-Path $WEB_DIR)) {
Write-Host "ERROR: Directorio web no encontrado: $WEB_DIR" -ForegroundColor Red
exit 1
}
# Verificar o crear .env.local
Write-Host "[2/5] Configurando variables de entorno..." -ForegroundColor Yellow
if (-not (Test-Path $ENV_FILE)) {
Write-Host " Creando archivo .env.local..." -ForegroundColor Gray
$clusterUrl = Read-Host " Ingresa REACT_APP_CLUSTER_URL (ej: localhost o http://localhost:8080) [default: localhost]"
if ([string]::IsNullOrWhiteSpace($clusterUrl)) {
$clusterUrl = "localhost"
}
Set-Content -Path $ENV_FILE -Value "REACT_APP_CLUSTER_URL=$clusterUrl"
Write-Host " OK: Archivo .env.local creado con REACT_APP_CLUSTER_URL=$clusterUrl" -ForegroundColor Green
} else {
Write-Host " OK: Archivo .env.local ya existe" -ForegroundColor Green
$envContent = Get-Content $ENV_FILE -Raw
if ($envContent -match "REACT_APP_CLUSTER_URL") {
Write-Host " OK: REACT_APP_CLUSTER_URL esta configurado" -ForegroundColor Green
Write-Host " Contenido actual:" -ForegroundColor Gray
Get-Content $ENV_FILE | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }
} else {
Write-Host " ADVERTENCIA: REACT_APP_CLUSTER_URL no encontrado en .env.local" -ForegroundColor Yellow
$clusterUrl = Read-Host " Ingresa REACT_APP_CLUSTER_URL (ej: localhost o http://localhost:8080) [default: localhost]"
if ([string]::IsNullOrWhiteSpace($clusterUrl)) {
$clusterUrl = "localhost"
}
Add-Content -Path $ENV_FILE -Value "`nREACT_APP_CLUSTER_URL=$clusterUrl"
Write-Host " OK: REACT_APP_CLUSTER_URL agregado: $clusterUrl" -ForegroundColor Green
}
}
# Verificar Node.js y yarn
Write-Host "[3/5] Verificando dependencias..." -ForegroundColor Yellow
try {
$nodeVersion = node --version 2>&1
Write-Host " OK: Node.js instalado: $nodeVersion" -ForegroundColor Green
} catch {
Write-Host " ERROR: Node.js no encontrado. Por favor instalalo primero." -ForegroundColor Red
exit 1
}
try {
$yarnVersion = yarn --version 2>&1
Write-Host " OK: Yarn instalado: v$yarnVersion" -ForegroundColor Green
} catch {
Write-Host " ERROR: Yarn no encontrado. Instalando yarn..." -ForegroundColor Yellow
npm install -g yarn
if ($LASTEXITCODE -ne 0) {
Write-Host " ERROR: Error al instalar yarn" -ForegroundColor Red
exit 1
}
Write-Host " OK: Yarn instalado" -ForegroundColor Green
}
try {
$goVersion = go version 2>&1
Write-Host " OK: Go instalado: $goVersion" -ForegroundColor Green
} catch {
Write-Host " ERROR: Go no encontrado. Por favor instalalo primero." -ForegroundColor Red
exit 1
}
# Compilar frontend React
Write-Host "[4/5] Compilando frontend React..." -ForegroundColor Yellow
Push-Location $WEB_DIR
try {
Write-Host " Instalando dependencias de Node.js (si es necesario)..." -ForegroundColor Gray
yarn install --silent 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host " ADVERTENCIA: Algunas advertencias durante yarn install (continuando...)" -ForegroundColor Yellow
}
Write-Host " Compilando con yarn build..." -ForegroundColor Gray
yarn build 2>&1 | ForEach-Object {
if ($_ -match "Compiled|error|Error|Failed|failed") {
$color = if ($_ -match "error|Error|Failed|failed") { "Red" } else { "Gray" }
Write-Host " $_" -ForegroundColor $color
}
}
if ($LASTEXITCODE -eq 0) {
Write-Host " OK: Frontend compilado exitosamente" -ForegroundColor Green
} else {
Write-Host " ERROR: Error al compilar el frontend" -ForegroundColor Red
Pop-Location
exit 1
}
} catch {
Write-Host " ERROR: Error durante la compilacion del frontend: $_" -ForegroundColor Red
Pop-Location
exit 1
}
Pop-Location
# Verificar que web/build existe
$BUILD_DIR = Join-Path $WEB_DIR "build"
if (-not (Test-Path $BUILD_DIR)) {
Write-Host " ERROR: Directorio web/build no encontrado despues de la compilacion" -ForegroundColor Red
exit 1
}
Write-Host " OK: Directorio web/build verificado" -ForegroundColor Green
# Instalar statik si no esta instalado
Write-Host "[5/5] Preparando statik..." -ForegroundColor Yellow
Push-Location $ROOT_DIR
try {
Write-Host " Verificando si statik esta instalado..." -ForegroundColor Gray
$statikInstalled = Get-Command statik -ErrorAction SilentlyContinue
if (-not $statikInstalled) {
Write-Host " Instalando statik..." -ForegroundColor Gray
go get github.com/rakyll/statik 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Host " OK: Statik instalado" -ForegroundColor Green
} else {
Write-Host " ADVERTENCIA: No se pudo instalar statik automaticamente" -ForegroundColor Yellow
Write-Host " Intenta manualmente: go get github.com/rakyll/statik" -ForegroundColor Yellow
}
} else {
Write-Host " OK: Statik ya esta instalado" -ForegroundColor Green
}
Write-Host " Ejecutando statik --src=web/build..." -ForegroundColor Gray
statik --src=web/build 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Host " OK: Statik ejecutado exitosamente" -ForegroundColor Green
} else {
Write-Host " ERROR: Error al ejecutar statik" -ForegroundColor Red
Write-Host " Verifica que el directorio web/build exista y tenga contenido" -ForegroundColor Yellow
Pop-Location
exit 1
}
Write-Host " Compilando servidor Go..." -ForegroundColor Gray
go build -o ssh-web-console.exe 2>&1 | ForEach-Object {
if ($_ -match "error|Error") {
Write-Host " $_" -ForegroundColor Red
}
}
if ($LASTEXITCODE -eq 0) {
Write-Host " OK: Servidor Go compilado exitosamente" -ForegroundColor Green
$exePath = Join-Path $ROOT_DIR "ssh-web-console.exe"
if (Test-Path $exePath) {
Write-Host " OK: Ejecutable creado: $exePath" -ForegroundColor Green
}
} else {
Write-Host " ERROR: Error al compilar el servidor Go" -ForegroundColor Red
Pop-Location
exit 1
}
} catch {
Write-Host " ERROR: Error durante la compilacion: $_" -ForegroundColor Red
Pop-Location
exit 1
}
Pop-Location
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " OK: Compilacion completada exitosamente " -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Para ejecutar web-console:" -ForegroundColor Yellow
Write-Host " cd C:\wamp64\www\web-console" -ForegroundColor Gray
Write-Host " .\ssh-web-console.exe" -ForegroundColor Gray
Write-Host ""
Write-Host "El servidor escuchara en: http://localhost:2222" -ForegroundColor Gray
Write-Host ""
No preview for this file type
This diff is collapsed.
const { override, addLessLoader } = require('customize-cra'); const { override, addLessLoader } = require('customize-cra');
// Función que ajusta PostCSS Loader DESPUÉS de que addLessLoader lo configura
const fixPostCSSLoaderAfterLess = (config) => {
// Función recursiva para encontrar y ajustar PostCSS Loader en toda la configuración
const fixPostCSSInRules = (rules) => {
if (!rules || !Array.isArray(rules)) return;
rules.forEach((rule) => {
if (rule.oneOf) {
fixPostCSSInRules(rule.oneOf);
}
if (rule.use) {
const processUse = (use) => {
if (Array.isArray(use)) {
use.forEach((item) => {
if (typeof item === 'object' && item !== null && item.loader) {
if (item.loader.includes('postcss-loader') && item.options) {
// Si tiene plugins directamente (API antigua), moverlos a postcssOptions
if (item.options.plugins && !item.options.postcssOptions) {
item.options = {
postcssOptions: {
plugins: item.options.plugins
}
};
}
}
}
});
} else if (typeof use === 'object' && use !== null && use.loader) {
if (use.loader.includes('postcss-loader') && use.options) {
if (use.options.plugins && !use.options.postcssOptions) {
use.options = {
postcssOptions: {
plugins: use.options.plugins
}
};
}
}
}
};
processUse(rule.use);
}
});
};
if (config.module && config.module.rules) {
fixPostCSSInRules(config.module.rules);
}
return config;
};
module.exports = override( module.exports = override(
addLessLoader({ addLessLoader({
lessOptions: { lessOptions: {
javascriptEnabled: true, javascriptEnabled: true,
} }
}) }),
fixPostCSSLoaderAfterLess // Aplicar el fix DESPUÉS de addLessLoader
) )
...@@ -11,23 +11,21 @@ ...@@ -11,23 +11,21 @@
"dependencies": { "dependencies": {
"@rottitime/react-hook-message-event": "^1.0.8", "@rottitime/react-hook-message-event": "^1.0.8",
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.15", "@types/jest": "^26.0.15",
"@types/node": "^12.0.0", "@types/node": "^18.17.0",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"axios": "^0.21.1", "axios": "^0.21.1",
"evergreen-ui": "^6.4.0", "evergreen-ui": "^6.4.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"i18next": "^19.8.4", "i18next": "^19.8.4",
"js-base64": "2.5.1", "js-base64": "^3.0.0",
"react-dropzone": "^11.2.4", "react-dropzone": "^11.2.4",
"react-i18next": "^11.7.4", "react-i18next": "^11.7.4",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "4.0.1", "react-scripts": "^5.0.1",
"typescript": "^4.0.3", "typescript": "^4.0.3",
"web-vitals": "^0.2.4", "web-vitals": "^2.1.4",
"workbox-background-sync": "^5.1.3", "workbox-background-sync": "^5.1.3",
"workbox-broadcast-update": "^5.1.3", "workbox-broadcast-update": "^5.1.3",
"workbox-cacheable-response": "^5.1.3", "workbox-cacheable-response": "^5.1.3",
...@@ -46,7 +44,7 @@ ...@@ -46,7 +44,7 @@
}, },
"scripts": { "scripts": {
"start": "react-app-rewired start", "start": "react-app-rewired start",
"build": "react-app-rewired build", "build": "cross-env NODE_OPTIONS=--openssl-legacy-provider react-app-rewired build",
"test": "react-app-rewired test", "test": "react-app-rewired test",
"eject": "react-app-rewired eject", "eject": "react-app-rewired eject",
"format": "prettier --config .prettierrc --write 'src/*'" "format": "prettier --config .prettierrc --write 'src/*'"
...@@ -81,11 +79,13 @@ ...@@ -81,11 +79,13 @@
"@babel/core": "^7.13.0", "@babel/core": "^7.13.0",
"@types/file-saver": "^2.0.1", "@types/file-saver": "^2.0.1",
"@types/js-base64": "^3.0.0", "@types/js-base64": "^3.0.0",
"@types/react-router-dom": "^5.1.6", "@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^4.6.1", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^4.6.1", "@typescript-eslint/parser": "^5.62.0",
"babel-plugin-import": "^1.13.1", "babel-plugin-import": "^1.13.1",
"cross-env": "^7.0.3",
"customize-cra": "^1.0.0", "customize-cra": "^1.0.0",
"react-app-rewired": "^2.2.1",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-prettier": "^6.15.0", "eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
...@@ -95,8 +95,9 @@ ...@@ -95,8 +95,9 @@
"less-loader": "^7.1.0", "less-loader": "^7.1.0",
"lint-staged": "^10.5.1", "lint-staged": "^10.5.1",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"react": "^17.0.2", "react": "^18.2.0",
"react-app-rewired": "^2.1.6", "react-dom": "^18.2.0",
"react-dom": "^17.0.2" "@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0"
} }
} }
...@@ -4,12 +4,18 @@ import Console from './components/Console'; ...@@ -4,12 +4,18 @@ import Console from './components/Console';
import Home from './components/Home'; import Home from './components/Home';
function App() { function App() {
// Type assertions needed for React 18 compatibility with react-router-dom v5 types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const SwitchComponent = Switch as any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const RouteComponent = Route as any;
return ( return (
<div className="App"> <div className="App">
<Switch> <SwitchComponent>
<Route path="/console" exact component={Console} /> <RouteComponent path="/console" exact component={Console} />
<Route path="/" component={Home} /> <RouteComponent path="/" component={Home} />
</Switch> </SwitchComponent>
</div> </div>
); );
} }
......
...@@ -92,7 +92,7 @@ const Console = (props: RouteComponentProps) => { ...@@ -92,7 +92,7 @@ const Console = (props: RouteComponentProps) => {
const { t } = useTranslation(['translation', 'console']); const { t } = useTranslation(['translation', 'console']);
const [fitAddon] = useState<FitAddon>(new FitAddon()); const [fitAddon] = useState<FitAddon>(new FitAddon());
const [webLinksAddon] = useState<WebLinksAddon>(new WebLinksAddon()); const [webLinksAddon] = useState<WebLinksAddon>(new WebLinksAddon());
const [fullscreen, setFullscreen] = useState<boolean>(false); const [fullscreen] = useState<boolean>(false);
const [connecting, setConnecting] = useState<ConnStatus>( const [connecting, setConnecting] = useState<ConnStatus>(
ConnStatus.Connecting, ConnStatus.Connecting,
); );
...@@ -206,7 +206,7 @@ const Console = (props: RouteComponentProps) => { ...@@ -206,7 +206,7 @@ const Console = (props: RouteComponentProps) => {
background="rgba(27,33,47,0.86)"> background="rgba(27,33,47,0.86)">
<Heading padding={18} color="white"> <Heading padding={18} color="white">
{' '} {' '}
{t('title')} {t('title') as string}
</Heading> </Heading>
<Pane <Pane
padding={18} padding={18}
......
import React from 'react'; import React from 'react';
import { NavLink, Route, Switch } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import { Button, Pane, Heading } from 'evergreen-ui';
import { useTranslation } from 'react-i18next';
import Header from './layout/Header'; import Header from './layout/Header';
import Signin from './Signin'; import Signin from './Signin';
import './home.less'; import './home.less';
import headerLogo from '../assets/ssh.png';
const MainPage = () => {
const { t } = useTranslation(['home']);
return (
<>
<Pane
alignItems="center"
justifyContent="center"
display="flex"
flexDirection="column">
<div
style={{
minHeight: '360px',
marginTop: '10rem',
textAlign: 'center',
}}>
<img src={headerLogo} className="App-logo" alt="logo" />
<Heading marginBottom="0.6rem" marginTop="0.6rem" size={700}>
{t('home:welcome')}
</Heading>
<div>
<NavLink to="/signin" className="focus-ring-link">
<Button appearance="primary"> {t('home:goto_signin')} </Button>
</NavLink>
</div>
</div>
</Pane>
</>
);
};
const Home = () => { const Home = () => {
// Type assertions needed for React 18 compatibility with react-router-dom v5 types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const SwitchComponent = Switch as any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const RouteComponent = Route as any;
return ( return (
<div className="home-container"> <div className="home-container">
<header className="home-content-header"> <header className="home-content-header">
<Header /> <Header />
</header> </header>
<main className="home-content-main main-content-container"> <main className="home-content-main main-content-container">
<Switch> <SwitchComponent>
<Route exact path={`/`} component={Signin} /> <RouteComponent exact path={`/`} component={Signin} />
<Route path={`/signin`} component={Signin} /> <RouteComponent path={`/signin`} component={Signin} />
</Switch> </SwitchComponent>
</main> </main>
</div> </div>
); );
......
...@@ -9,28 +9,139 @@ import apiRouters from '../config/api_routers'; ...@@ -9,28 +9,139 @@ import apiRouters from '../config/api_routers';
const Signin = (props: RouteComponentProps) => { const Signin = (props: RouteComponentProps) => {
React.useEffect(() => { React.useEffect(() => {
window.addEventListener('message', (event) => { const handleMessage = (event: MessageEvent) => {
const baseUrl = process.env.REACT_APP_CLUSTER_URL as string; console.log('Signin.tsx - Message received:', event);
console.log(event.origin); console.log('Origin:', event.origin);
console.log(process.env.REACT_APP_CLUSTER_URL); console.log('Data:', event.data);
console.log('-----');
console.log(!event.origin.includes(baseUrl)); // Validar que el mensaje tenga los datos necesarios
console.log('-----'); if (!event.data || typeof event.data !== 'object') {
console.log(event.data); console.log('Signin.tsx - Invalid message format');
console.log('-----'); return;
if (!event.origin.includes(process.env.REACT_APP_CLUSTER_URL!)) return; }
doSignin(event.data);
}); // Validar origen: REACT_APP_CLUSTER_URL debe estar contenido en el origen recibido
const clusterUrl = process.env.REACT_APP_CLUSTER_URL;
if (!clusterUrl) {
console.log(
'Signin.tsx - REACT_APP_CLUSTER_URL not configured, rejecting message',
);
return;
}
// Normalizar origen: quitar protocolo y puerto para comparación flexible
const normalizeOrigin = (url: string) => {
try {
// Si ya es una URL completa, extraer el hostname
if (url.includes('://')) {
const urlObj = new URL(url);
return urlObj.hostname.toLowerCase();
}
// Si es solo hostname, devolverlo en lowercase y limpiar
return url
.toLowerCase()
.replace(/^https?:\/\//, '')
.replace(/:\d+$/, '')
.split('/')[0]
.trim();
} catch {
// Si falla el parsing, intentar limpiar manualmente
return url
.toLowerCase()
.replace(/^https?:\/\//, '')
.replace(/:\d+$/, '')
.split('/')[0]
.trim();
}
};
const originHostname = normalizeOrigin(event.origin);
const clusterHostname = normalizeOrigin(clusterUrl);
console.log('Signin.tsx - Origin validation:', {
originalOrigin: event.origin,
normalizedOrigin: originHostname,
originalClusterUrl: clusterUrl,
normalizedClusterUrl: clusterHostname,
});
if (originHostname !== clusterHostname) {
console.log(
'Signin.tsx - Origin not allowed:',
event.origin,
'(normalized:',
originHostname,
')',
'Expected:',
clusterUrl,
'(normalized:',
clusterHostname,
')',
);
return;
}
console.log(
'Signin.tsx - Origin allowed:',
event.origin,
'Matches:',
clusterUrl,
);
// Verificar que el mensaje tenga los campos necesarios
if (event.data.host && event.data.username && event.data.password) {
console.log('Signin.tsx - Valid message received, processing...');
doSignin(event.data);
} else {
console.log('Signin.tsx - Message missing required fields');
}
};
window.addEventListener('message', handleMessage);
// Cleanup
return () => {
window.removeEventListener('message', handleMessage);
};
}, []); }, []);
const { t } = useTranslation(['signin']); const { t } = useTranslation(['signin']);
const doSignin = (data: Record<string, string>) => { const doSignin = (data: Record<string, string>) => {
console.log('Signin.tsx - Received data:', data);
// Normalizar datos recibidos (lowercase, trim)
const normalizedData = {
host: String(data.host || '')
.toLowerCase()
.trim(),
port: String(data.port || '22').trim(),
username: String(data.username || '').trim(),
passwd: String(data.password || '').trim(),
};
console.log('Signin.tsx - Normalized data:', {
...normalizedData,
passwd: '***',
});
// Validar que todos los campos requeridos estén presentes
if (
!normalizedData.host ||
!normalizedData.username ||
!normalizedData.passwd
) {
console.error('Signin.tsx - Missing required fields:', normalizedData);
toaster.danger(t('signin:form_has_error'));
return;
}
Utils.axiosInstance Utils.axiosInstance
.post(Utils.loadUrl(apiRouters.router.sign_in, null), { .post(Utils.loadUrl(apiRouters.router.sign_in, null), {
host: data.host, host: normalizedData.host,
port: data.port, port: normalizedData.port,
username: data.username, username: normalizedData.username,
passwd: data.password, passwd: normalizedData.passwd,
}) })
.then((response) => { .then((response) => {
console.log(response); console.log(response);
...@@ -76,8 +187,8 @@ const Signin = (props: RouteComponentProps) => { ...@@ -76,8 +187,8 @@ const Signin = (props: RouteComponentProps) => {
props.history.push('/'); props.history.push('/');
} else { } else {
toaster.success(t('signin:signin_success')); toaster.success(t('signin:signin_success'));
localStorage.setItem('user.host', data.host); localStorage.setItem('user.host', normalizedData.host);
localStorage.setItem('user.username', data.username); localStorage.setItem('user.username', normalizedData.username);
sessionStorage.setItem( sessionStorage.setItem(
Config.jwt.tokenName, Config.jwt.tokenName,
response.data.addition, response.data.addition,
...@@ -87,7 +198,8 @@ const Signin = (props: RouteComponentProps) => { ...@@ -87,7 +198,8 @@ const Signin = (props: RouteComponentProps) => {
} }
} }
} catch (e) { } catch (e) {
console.log(e.message); const errorMessage = e instanceof Error ? e.message : String(e);
console.log(errorMessage);
toaster.danger(t('signin:form_error_ssh_login')); toaster.danger(t('signin:form_error_ssh_login'));
if (window && window.parent) { if (window && window.parent) {
console.log('Send Message Error 1'); console.log('Send Message Error 1');
...@@ -101,9 +213,10 @@ const Signin = (props: RouteComponentProps) => { ...@@ -101,9 +213,10 @@ const Signin = (props: RouteComponentProps) => {
props.history.push('/'); props.history.push('/');
} }
}) })
.catch((e: Error) => { .catch((e: unknown) => {
console.log(e.message); const errorMessage = e instanceof Error ? e.message : String(e);
toaster.danger(t('signin:form_error_ssh_login') + ': ' + e.message); console.log(errorMessage);
toaster.danger(t('signin:form_error_ssh_login') + ': ' + errorMessage);
if (window && window.parent) { if (window && window.parent) {
console.log('Send Message Error 2'); console.log('Send Message Error 2');
window.parent.postMessage( window.parent.postMessage(
......
...@@ -17,7 +17,7 @@ const Header = () => { ...@@ -17,7 +17,7 @@ const Header = () => {
<Pane flex={1} alignItems="center" display="flex"> <Pane flex={1} alignItems="center" display="flex">
<Heading size={600}> <Heading size={600}>
<Link to="/" className="focus-ring-link"> <Link to="/" className="focus-ring-link">
{t('title')} {t('title') as string}
</Link> </Link>
</Heading> </Heading>
</Pane> </Pane>
......
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import './index.less'; import './index.less';
import App from './App'; import App from './App';
...@@ -9,13 +9,20 @@ import reportWebVitals from './reportWebVitals'; ...@@ -9,13 +9,20 @@ import reportWebVitals from './reportWebVitals';
import config from './config/config'; import config from './config/config';
import './locales/i18n'; import './locales/i18n';
ReactDOM.render( const container = document.getElementById('root');
if (!container) {
throw new Error('Failed to find the root element');
}
const root = createRoot(container);
// Type assertion needed for React 18 compatibility with react-router-dom v5 types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const RouterComponent = BrowserRouter as any;
root.render(
<React.StrictMode> <React.StrictMode>
<Router basename={config.router.basepath}> <RouterComponent basename={config.router.basepath}>
<App /> <App />
</Router> </RouterComponent>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root'),
); );
// If you want your app to work offline and load faster, you can change // If you want your app to work offline and load faster, you can change
......
...@@ -8,12 +8,19 @@ interface TermSize { ...@@ -8,12 +8,19 @@ interface TermSize {
const resize = { const resize = {
bindTerminalResize: function (term: Terminal, websocket: WebSocket) { bindTerminalResize: function (term: Terminal, websocket: WebSocket) {
const onTermResize = (size: TermSize) => { const onTermResize = (size: TermSize) => {
websocket.send( // Verificar que el WebSocket esté conectado antes de enviar datos
JSON.stringify({ if (websocket.readyState === WebSocket.OPEN) {
type: 'resize', try {
data: { rows: size.rows, cols: size.cols }, websocket.send(
}), JSON.stringify({
); type: 'resize',
data: { rows: size.rows, cols: size.cols },
}),
);
} catch (error) {
console.error('Error sending resize message:', error);
}
}
}; };
// register resize event. // register resize event.
const resizeListener = term.onResize(onTermResize); const resizeListener = term.onResize(onTermResize);
......
...@@ -50,14 +50,15 @@ registerRoute( ...@@ -50,14 +50,15 @@ registerRoute(
// Return true to signal that we want to use the handler. // Return true to signal that we want to use the handler.
return true; return true;
}, },
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html') createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html'),
); );
// An example runtime caching route for requests that aren't handled by the // An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/ // precache, in this case same-origin .png requests like those from in public/
registerRoute( registerRoute(
// Add in any other file extensions or routing criteria as needed. // Add in any other file extensions or routing criteria as needed.
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), ({ url }) =>
url.origin === self.location.origin && url.pathname.endsWith('.png'),
// Customize this strategy as needed, e.g., by changing to CacheFirst. // Customize this strategy as needed, e.g., by changing to CacheFirst.
new StaleWhileRevalidate({ new StaleWhileRevalidate({
cacheName: 'images', cacheName: 'images',
...@@ -66,7 +67,7 @@ registerRoute( ...@@ -66,7 +67,7 @@ registerRoute(
// least-recently used images are removed. // least-recently used images are removed.
new ExpirationPlugin({ maxEntries: 50 }), new ExpirationPlugin({ maxEntries: 50 }),
], ],
}) }),
); );
// This allows the web app to trigger skipWaiting via // This allows the web app to trigger skipWaiting via
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment