From dc827bcbf489d2fa6c36fe5167ed7039825ba713 Mon Sep 17 00:00:00 2001 From: Hanzo_dev <2002samudiojohan@gmail.com> Date: Wed, 25 Feb 2026 19:49:29 -0500 Subject: [PATCH] fix: manejar error 401 automaticamente y agregar migracion de columnas routes --- ...a1b2c3d4e5f6_merge_heads_and_fix_routes.py | 38 +++++++++++++++++++ frontend/src/services/apiClient.ts | 16 ++++++++ frontend/src/views/AdminRoutes.vue | 17 ++++++++- frontend/src/views/AuthView.vue | 16 +++++++- 4 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 backend/alembic/versions/a1b2c3d4e5f6_merge_heads_and_fix_routes.py diff --git a/backend/alembic/versions/a1b2c3d4e5f6_merge_heads_and_fix_routes.py b/backend/alembic/versions/a1b2c3d4e5f6_merge_heads_and_fix_routes.py new file mode 100644 index 0000000..d1ff4a7 --- /dev/null +++ b/backend/alembic/versions/a1b2c3d4e5f6_merge_heads_and_fix_routes.py @@ -0,0 +1,38 @@ +"""Fix routes table: ensure color, direction, average_speed_kmh columns exist + +This migration uses IF NOT EXISTS to safely add missing columns regardless +of the current database state in production. + +Revision ID: a1b2c3d4e5f6 +Revises: ffcd1234abcd +Create Date: 2026-02-26 00:30:00.000000 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'a1b2c3d4e5f6' +down_revision: Union[str, None] = 'ffcd1234abcd' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Use raw SQL with IF NOT EXISTS to be safe regardless of prior migration state. + # This ensures the columns exist in production even if the previous migration + # had issues being applied. + with op.get_context().autocommit_block(): + op.execute("ALTER TABLE routes ADD COLUMN IF NOT EXISTS color VARCHAR DEFAULT '#FEE715'") + op.execute("ALTER TABLE routes ADD COLUMN IF NOT EXISTS direction VARCHAR DEFAULT 'outbound'") + op.execute("ALTER TABLE routes ADD COLUMN IF NOT EXISTS average_speed_kmh FLOAT") + # Update any existing NULLs + op.execute("UPDATE routes SET color = '#FEE715' WHERE color IS NULL") + op.execute("UPDATE routes SET direction = 'outbound' WHERE direction IS NULL") + + +def downgrade() -> None: + pass diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts index 9a293ba..39f61bd 100644 --- a/frontend/src/services/apiClient.ts +++ b/frontend/src/services/apiClient.ts @@ -31,6 +31,22 @@ client.interceptors.response.use( (error) => { if (error.response) { console.error('API Error:', error.response.status, error.response.data) + // Si el token expiró o es inválido, limpiar sesión y redirigir al login + if (error.response.status === 401) { + const currentPath = window.location.pathname + // Solo redirigir si no estamos ya en la página de login + if (!currentPath.includes('/auth') && !currentPath.includes('/login')) { + localStorage.removeItem('auth_token') + localStorage.removeItem('user_role') + localStorage.removeItem('user_name') + localStorage.removeItem('profile_photo_url') + // Redirigir al login con mensaje + window.location.href = '/auth?reason=session_expired' + } + } + } else if (error.request) { + // La solicitud fue hecha pero no hubo respuesta (timeout, servidor dormido, etc.) + console.error('Network Error: No response from server', error.message) } return Promise.reject(error) } diff --git a/frontend/src/views/AdminRoutes.vue b/frontend/src/views/AdminRoutes.vue index d80fb6b..258a281 100644 --- a/frontend/src/views/AdminRoutes.vue +++ b/frontend/src/views/AdminRoutes.vue @@ -354,12 +354,25 @@ async function confirmCreateRoute() { isCreating.value = false } catch (err: any) { console.error('Error creating route:', err) + + if (err.response?.status === 401) { + // El interceptor ya redirige al login, pero mostramos aviso + alert('Tu sesión ha expirado. Serás redirigido al inicio de sesión.') + return + } else if (err.response?.status === 403) { + alert('No tienes permisos de administrador para crear rutas.') + return + } else if (!err.response && err.request) { + // Network Error - servidor no respondió + alert('No se pudo conectar al servidor. Si es la primera solicitud del día, el servidor puede tardar ~30 segundos en iniciar. Por favor, intenta de nuevo en un momento.') + return + } + const errorMsg = err.response?.data?.detail || err.response?.data?.message || err.message || 'Error desconocido' - const errorDetail = err.response ? `Status: ${err.response.status}` : 'No hubo respuesta del servidor (Network Error)' - alert(`No se pudo crear la ruta: ${errorMsg}\n\nDetalle: ${errorDetail}`) + alert(`No se pudo crear la ruta: ${errorMsg}`) } } diff --git a/frontend/src/views/AuthView.vue b/frontend/src/views/AuthView.vue index 7715207..7c55700 100644 --- a/frontend/src/views/AuthView.vue +++ b/frontend/src/views/AuthView.vue @@ -1,6 +1,6 @@