Proyecto de aplicación web Flask: Projects Portal.

Contenido

En este proyecto desarrollaremos una aplicación web con Flask que funcionará como el portal principal de nuestro VPS. Su propósito es servir como punto central de acceso a todos los proyectos, aplicaciones y servicios que vayamos desplegando a lo largo del tiempo.

La página estará disponible en el subdominio projects.fernandorioseco.es y actuará como un auténtico escaparate técnico y profesional. Desde ella será posible entrar a las aplicaciones de proyectos realizados. También cada proyecto contará con su propio subdominio dentro del VPS.

En ese subdominio se desplegará la aplicación funcional del proyecto, es decir, la herramienta o servicio que el usuario podrá utilizar directamente, ya sea una aplicación desarrollada con Flask, Streamlit, FastAPI u otra tecnología.

La explicación detallada del proyecto —objetivos, arquitectura, tecnologías empleadas, proceso de desarrollo y aprendizajes obtenidos— no se mostrará dentro de la aplicación, sino en el sitio principal del portfolio, donde cada proyecto tendrá su propia ficha o artículo descriptivo.

De esta forma se separan claramente dos elementos:

  1. La documentación del proyecto, alojada en el sitio principal del portfolio.
  2. La aplicación desplegada, accesible mediante su subdominio correspondiente.

Este enfoque permite mantener el portfolio organizado y profesional, ofreciendo por un lado la explicación técnica del trabajo realizado y, por otro, el acceso directo a la aplicación en funcionamiento.

Estructura general del proyecto

El propósito del proyecto es construir una aplicación web en Flask desplegada en projects.fernandorioseco.es que actuará como portal central del VPS.

Objetivos

  • Centralizar el acceso a proyectos.
  • Mostrar información sobre el VPS.
  • Presentar el stack tecnológico.
  • Servir como portfolio profesional.

Para desarrollar el Projects Portal seguiremos una metodología organizada que abarcará desde el diseño de la aplicación en local hasta su despliegue definitivo en el VPS. El proyecto no solo consistirá en construir una aplicación web con Flask, sino también en integrarla dentro de la infraestructura del servidor y conectarla con el resto de aplicaciones del portfolio.

La estructura general del proyecto se dividirá en las siguientes fases:

1 – Planificación y Diseño: En primer lugar definiremos el objetivo del portal, las secciones que incluirá y la experiencia de usuario que queremos ofrecer. En esta fase estableceremos la arquitectura de la aplicación, el diseño de la interfaz y la información que se mostrará en cada sección.

2Setup de la Aplicación y Control de Versiones con Git y GitHub: Preparar el setup del proyecto con una app mínima, inicializar un repositorio Git y publicar el código en GitHub para mantener un historial de cambios y facilitar el despliegue en el servidor.

3 – Desarrollo Local: Crearemos el proyecto Flask en nuestro entorno local, organizaremos la estructura de carpetas, diseñaremos las plantillas HTML y desarrollaremos los estilos CSS y la lógica necesaria para mostrar los proyectos de forma dinámica.

4 – Configuración del Dominio: Crearemos el subdominio projects.fernandorioseco.es y configuraremos el registro DNS para que apunte a la dirección IP pública del VPS.

5 – Preparación del VPS: Crearemos el directorio del proyecto en el servidor, clonaremos el repositorio desde GitHub, configuraremos el entorno virtual de Python e instalaremos todas las dependencias necesarias.

6 – Configuración del Servicio con Gunicorn y systemd: Configuraremos Gunicorn como servidor WSGI y crearemos un servicio de systemd para que la aplicación se ejecute automáticamente y permanezca activa tras reinicios del sistema.

7 – Configuración de Nginx y HTTPS: Configuraremos Nginx como proxy inverso para publicar la aplicación en Internet y habilitaremos HTTPS mediante certificados SSL de Let’s Encrypt.

8 – Actualización y Mantenimiento: Definiremos el procedimiento para desplegar nuevas versiones del portal y añadir proyectos al catálogo mediante archivos JSON individuales.

9 – Mejoras y Evolución: Una vez desplegado el portal, podremos incorporar nuevas funcionalidades, como filtros por tecnología, estadísticas del VPS en tiempo real o un panel de administración para gestionar los proyectos desde una interfaz web.

1. Planificación y Diseño

Arquitectura de la Aplicación

La aplicación Projects Portal se desarrollará con Flask siguiendo una arquitectura sencilla, modular y escalable. El objetivo es separar claramente la lógica de negocio, la presentación y los datos para facilitar el mantenimiento y la incorporación de nuevos proyectos.

La arquitectura se basará en los siguientes componentes:

  1. Cliente web (navegador).
  2. Servidor web Nginx.
  3. Servidor de aplicaciones Gunicorn.
  4. Aplicación Flask.
  5. Plantillas HTML con Jinja2.
  6. Archivos estáticos (CSS, JavaScript e imágenes).
  7. Archivo de configuración con los datos de los proyectos.

Flujo de Funcionamiento

Usuario
   
Navegador Web
   
Nginx (Reverse Proxy)
   
Gunicorn (WSGI Server)
   
Flask Application
   
Routes (views.py)
   
Data Source (projects.json o Python dictionary)
   
Jinja2 Templates
   
HTML Renderizado
   
Respuesta al Navegador

Componentes de la Arquitectura

  • Navegador del Usuario: Es el cliente que realiza la petición HTTP y renderiza el HTML, CSS y JavaScript enviados por la aplicación.
  • Nginx: Actúa como proxy inverso.
    • Recibe las peticiones HTTPS.
    • Gestiona los certificados SSL/TLS.
    • Redirige el tráfico hacia Gunicorn.
    • Sirve archivos estáticos si se desea.
  • Gunicorn: Servidor WSGI encargado de ejecutar la aplicación Flask.
    • Inicia la aplicación.
    • Gestiona procesos workers.
    • Atiende múltiples peticiones concurrentes.
  • Aplicación Flask: Contiene la lógica principal del portal.
    • Fuente de Datos: Los proyectos pueden almacenarse en:
      • Un archivo projects.json.
    • Plantillas Jinja2: Se encargan de generar el HTML dinámico.
      • base.html
      • index.html
      • Componentes parciales:
        • navbar.html
        • hero.html
        • projects.html
        • footer.html
    • Archivos Estáticos: Recursos utilizados por la interfaz:
      • CSS.
      • JavaScript.
      • Imágenes.
      • Iconos.

Estructura de carpetas y ficheros

projects-portal/
├── app/
   ├── __init__.py
   ├── routes.py
   ├── data/
      └── projects.json
   ├── templates/
      ├── base.html
      ├── index.html
      └── components/
          └── navbar.html
          └── hero.html
          └── vps_info.html
          └── tech_stack.html
          └── projects.html
          └── resources.html
          └── footer.html
   └── static/
       ├── css/
       ├── js/
       └── img/
├── config.py
├── requirements.txt
├── run.py
└── wsgi.py

Descripción de los ficheros

  • app/__init__.py: Crea y configura la aplicación Flask, registra las rutas y devuelve la instancia principal de la aplicación.
  • app/routes.py: Define las rutas de la aplicación y establece que la página principal renderice la plantilla index.html.
  • run.py: Ejecuta la aplicación en modo desarrollo para realizar pruebas en el entorno local.
  • wsgi.py: Expone la aplicación para que pueda ser ejecutada por un servidor WSGI como Gunicorn en producción.
  • app/templates/index.html: Crea la estructura HTML básica de la página principal con el nombre del portal y una breve descripción del laboratorio.
  • app/data/projects.json: Almacenará la información de los proyectos que se mostrarán dinámicamente en el portal.
  • requirements.txt: Guarda el listado de dependencias Python necesarias para ejecutar el proyecto.
  • config.py: Centraliza la configuración general de la aplicación.
  • app/static/css/: Contendrá las hojas de estilo CSS de la aplicación.
  • app/static/js/: Contendrá los archivos JavaScript utilizados en la interfaz.
  • app/static/img/: Almacenará imágenes, iconos y recursos gráficos.
  • app/templates/base.html: Servirá como plantilla base común para todas las páginas del sitio.
  • app/templates/components/: Almacenará componentes reutilizables de la interfaz, como la barra de navegación o el footer.

Patrón Arquitectónico

El portal Projects Portal seguirá una versión simplificada del patrón arquitectónico MVC (Model-View-Controller), uno de los modelos de diseño más utilizados en el desarrollo de aplicaciones web. El objetivo es separar claramente los datos, la lógica de la aplicación, y la presentación visual.

Componente MVCImplementación
Modelapp/data/projects.json
Viewapp/templates/*.html
Controllerapp/routes.py

Escalabilidad

La arquitectura permite evolucionar fácilmente hacia:

  • Base de datos relacional.
  • Panel de administración.
  • API REST.
  • Autenticación.
  • Caché con Redis.
  • Contenerización con Docker.

Estructura de la Interfaz de Usuario (UI)

La aplicación Projects Portal estará compuesta por una única página principal diseñada para presentar el laboratorio técnico y ofrecer acceso directo a todos los proyectos desplegados en el VPS. La interfaz se organizará en varias secciones claramente diferenciadas, cada una con una función específica dentro de la experiencia de usuario.

Boceto de referencia aproximado generado por IA:

  • Barra de Navegación (Navbar): La página comenzará con una barra de navegación fija en la parte superior. Elementos de la navbar:
    • Logotipo o icono del portal.
    • Nombre del sitio: Projects Portal.
    • Enlaces externos:
    • Botón opcional para modo oscuro/claro.
  • Hero Section: Es la sección principal visible al cargar la página. Contenido:
    • Etiqueta superior: Laboratorio Personal en la Nube.
    • Título principal: Projects Portal.
    • Subtítulo con la propuesta de valor.
    • Descripción breve del portal.
    • Botones de acción:
      • Ver Proyectos.
      • GitHub.
    • Ilustración isométrica de un servidor VPS conectado a la nube.
  • Información del VPS: Sección formada por tarjetas con las características principales del servidor.
    • Sistema operativo.
    • CPU.
    • Memoria RAM.
    • Almacenamiento.
    • IP pública.
    • Dominio principal.
  • Stack Tecnológico: Grid con iconos y nombres de las tecnologías utilizadas. Tecnologías representadas:
    • Linux.
    • Docker.
    • Flask.
    • Nginx.
    • PostgreSQL.
    • Redis.
    • Python.
    • Power BI.
    • LangChain.
    • Machine Learning.
  • Proyectos Desplegados: Sección principal del portal, el contenido se distribuye en tarjetas
    • Icono o imagen representativa.
    • Nombre del proyecto.
    • Descripción breve.
    • Tecnologías utilizadas.
    • Botones de acción:
      • Open App.
      • Documentation.
      • Source Code.
      • API Docs (solo si existe API).
  • Recursos y Enlaces: Bloque con accesos a recursos externos.
  • Footer: Pie de página visible al final del sitio.
    • Nombre del autor.
    • Cargo o marca personal.
    • Correo electrónico.
    • Ubicación.
    • Iconos a redes profesionales.
    • Copyright.

Flujo de interacción del usuario

  1. El visitante accede a projects.fernandorioseco.es.
  2. Visualiza la presentación del laboratorio.
  3. Consulta la infraestructura y tecnologías utilizadas.
  4. Explora las tarjetas de proyectos.
  5. Accede a:
    • La aplicación en producción.
    • La documentación del proyecto.
    • El código fuente.
    • La documentación de la API, si existe.
  6. Navega a GitHub, blog o LinkedIn.

Objetivo de la interfaz

La UI debe transmitir una imagen:

  • Profesional.
  • Moderna.
  • Tecnológica.
  • Clara y fácil de navegar.

Al mismo tiempo, debe funcionar como un punto central desde el que cualquier visitante pueda comprender tu infraestructura y acceder rápidamente a todos los proyectos de tu portfolio.

2. Setup de la Aplicación y Control de Versiones

Setup del Proyecto

En esta fase realizaremos la configuración inicial del proyecto en nuestro entorno local. El objetivo es preparar la estructura base de la aplicación, crear un entorno virtual, instalar las dependencias necesarias y verificar que Flask funciona correctamente antes de comenzar con el desarrollo de la interfaz. En esta fase desarrollamos:

  • El directorio raíz del proyecto.
  • Un entorno virtual de Python.
  • Librerías necesarias
  • El archivo requirements.txt.
  • La estructura de carpetas del proyecto.
  • Los archivos base de la aplicación.
  • Implementar una aplicación Flask mínima.
  • Crear el repositorio en GitHub.

Implementar una aplicación Flask mínima

#app/__init__.py
from flask import Flask

def create_app():
    app = Flask(__name__)

    from app.routes import main
    app.register_blueprint(main)

    return app
    
    
# app/routes.py
from flask import Blueprint, render_template

main = Blueprint("main", __name__)

@main.route("/")
def home():
    return render_template("index.html")



# run.py
from app import create_app

app = create_app()

if __name__ == "__main__":
    app.run(debug=True)


#wsgi.py
from app import create_app

app = create_app()


#app/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Projects Portal</title>
</head>
<body>
    <h1>Projects Portal</h1>
    <p>Personal VPS Lab for DevOps, Data Science, MLOps and AI Engineering.</p>
</body>
</html>

Ejecutar la aplicación en local

python run.py

Abrimos el navegador en:

http://127.0.0.1:5000

Si todo ha ido correctamente, veremos la primera versión funcional del portal.

Crear un archivo projects.json de ejemplo

Aunque todavía no lo utilizaremos, podemos dejar preparado el archivo que almacenará la información de los proyectos. en app/data/projects.json

[
  {
    "name": "CO2 Emissions Dashboard",
    "description": "Análisis interactivo de emisiones de vehículos.",
    "technologies": ["Python", "Pandas", "Power BI", "Streamlit"],
    "app_url": "https://co2.projects.fernandorioseco.es",
    "article_url": "https://fernandorioseco.es/co2-emissions-dashboard",
    "github_url": "https://github.com/fernandorioseco/co2-emissions-dashboard",
    "api_docs_url": null
  }
]

Control de versiones con Git y Github

  1. Crear el archivo .gitignore.
  2. Inicializar el repositorio Git.
  3. Realizar el primer commit.
  4. Crear el repositorio remoto en GitHub.
  5. Subir el código al repositorio.
  6. Establecer la rama principal main.

3. Desarrollo local

Arquitectura de Plantillas

La interfaz del Projects Portal se construye siguiendo una arquitectura de plantillas modular basada en el motor de plantillas Jinja, integrado de forma nativa en Flask. Este enfoque permite dividir la interfaz en componentes independientes y reutilizables, facilitando el mantenimiento y la escalabilidad de la aplicación.

La finalidad de esta arquitectura es separar la estructura visual del sitio en piezas con responsabilidades bien definidas:

  • Una plantilla base común.
  • Una página principal.
  • Componentes reutilizables.

Gracias a esta organización, cada sección de la interfaz puede desarrollarse y modificarse de forma aislada. Las plantillas HTML se almacenan en el directorio: app/templates/. La estructura adoptada es la siguiente:

app/templates/
├── base.html
├── index.html
└── components/
    ├── navbar.html
    ├── hero.html
    ├── vps_info.html
    ├── tech_stack.html
    ├── projects.html
    ├── resources.html
    └── footer.html
ElementoDescripción
base.htmlPlantilla base de la aplicación. Define la estructura HTML común del sitio, incluyendo la cabecera del documento, la carga de estilos, la barra de navegación, el bloque de contenido principal y el footer.
index.htmlPágina principal del portal. Ensambla secuencialmente los distintos componentes visuales mediante instrucciones {% include %}. Contiene muy poca lógica y actúa como orquestador de la interfaz.
components/Directorio que contiene las secciones independientes que conforman la página principal. Cada archivo representa un bloque funcional concreto.
navbar.htmlBarra de navegación superior.
hero.htmlSección principal de presentación del portal.
vps_info.htmlInformación técnica del servidor VPS.
tech_stack.htmlTecnologías y herramientas utilizadas en el laboratorio.
projects.htmlListado dinámico de proyectos desplegados.
resources.htmlEnlaces externos a documentación, GitHub, blog y LinkedIn.
footer.htmlPie de página del sitio.

Integración con Flask

La ruta principal devuelve la plantilla:

return render_template("index.html", projects=projects)

Jinja2 se encarga automáticamente de resolver la herencia e inclusiones.

base.html – plantilla base de la aplicación

El archivo base.html constituye la plantilla base del Projects Portal. Su función es definir la estructura HTML común que compartirán todas las páginas del sitio. Este enfoque permite centralizar los elementos globales de la interfaz y evitar la duplicación de código. Ubicación del archivo: app/templates/base.html

Propósito

La plantilla base se encarga de definir la estructura general del documento HTML e incluir aquellos elementos que deben estar presentes en todas las páginas de la aplicación. En concreto, base.html incorpora:

  • Definir la estructura HTML general del documento.
  • Configurar las etiquetas meta necesarias.
  • Establecer un título por defecto.
  • Cargar la hoja de estilos.
  • Incluir la barra de navegación.
  • Definir un bloque de contenido que heredarán las páginas hijas.
  • Incluir el footer.

Estructura conceptual

[ DOCUMENTO HTML ] (Template Maestro)

 ├── [ HEAD ] (Configuración Invisible)
 │    ├── Metadatos (Viewport, Charset)
 │    ├── Título Dinámico {% block title %}
 │    └── Estilos Propios (styles.css)

 └── [ BODY ] (Estructura Visible)

      ├── [ HEADER ] ─── {% include "navbar.html" %}  ── (Componente Reutilizable)

      ├── [ MAIN ] ───── {% block content %} ──────── (Espacio Inyectable/Variable)
      │                                                *Aquí se carga el contenido*
      │                                                *específico de cada ruta*

      └── [ FOOTER ] ─── {% include "footer.html" %}  ── (Componente Reutilizable)

Elementos de Jinja2 utilizados

  • title: este permite definir un título por defecto y sobrescribirlo desde plantillas hijas.
  • content: reserva el espacio donde se insertará el contenido específico de cada página.
  • include: inserta componentes reutilizables como la barra de navegación y el footer.
  • url_for(): genera automáticamente la URL correcta para los archivos estáticos.

Relación con el resto de plantillas

base.html es utilizada por index.html mediante la instrucción: {% extends "base.html" %}. A partir de ese momento, index.html solo necesita definir el contenido correspondiente al bloque content.

Flujo de renderizado

index.html  
└── extends base.html           
        ├── navbar.html           
        ├── content           
        └── footer.html

navbar.html – barra de navegación

La barra de navegación es el componente situado en la parte superior de la página y proporciona acceso a recursos externos relevantes. Al tratarse de un elemento común a todas las páginas del sitio, se implementa como un componente independiente que es incluido desde base.html. Ubicación del archivo: app/templates/components/navbar.html

Propósito del componente

  • Identificar visualmente el sitio mediante el nombre del portal.
  • Proporcionar acceso a recursos externos como GitHub, el blog y LinkedIn.

Elementos:

  • Identidad del sitio: Projects Portal
  • Enlaces externos: GitHub, Blog, LinkedIn

Estructura conceptual

[ NAV: .navbar ] (Marco Principal: Fondo oscuro + Sombra)
 └── [ .container ] (Centrado del contenido)
      ├── [ .navbar-brand ] ─ "Projects Portal" (Logo/Título)

      ├── [ .navbar-toggler ] ─ [ Icono Hamburguesa ] (Solo visible en móviles)

      └── [ .collapse .navbar-collapse ] (Contenedor colapsable)
           └── [ .navbar-nav ] (Lista de enlaces: Alineada a la derecha)
                ├── [ .nav-item ] ── [ .nav-link ] ─ "GitHub" (Externo ↗)
                ├── [ .nav-item ] ── [ .nav-link ] ─ "Blog"   (Externo ↗)
                └── [ .nav-item ] ── [ .nav-link ] ─ "LinkedIn" (Externo ↗)

Enlaces externos

Los enlaces externos se abren en una nueva pestaña mediante target="_blank".

Relación con base.html

La plantilla base incorpora este componente mediante: {% include "components/navbar.html" %}. De esta forma, cualquier modificación realizada en navbar.html se reflejará automáticamente en todas las páginas del sitio.

Diseño visual

La navbar se diseñará con las siguientes características:

  • Posición fija o sticky en la parte superior.
  • Fondo blanco o semitransparente.
  • Sombra ligera.
  • Distribución horizontal.
  • Adaptación responsive para dispositivos móviles.

hero.html – sección principal de presentación

Define la sección superior de la página principal y constituye el primer elemento visual que verá el visitante al acceder al portal. Su objetivo es comunicar de forma inmediata qué es Projects Portal, cuál es su propósito y qué tipo de proyectos alberga. Ubicación del archivo: app/templates/components/hero.html

Elementos: La sección estará compuesta por dos columnas.

  • Columna izquierda
    • Badge superior: Personal VPS Lab.
    • Título principal: Projects Portal.
    • Subtítulo descriptivo.
    • Breve descripción del portal.
    • Botones de acción:
      • View Projects.
      • GitHub.
  • Columna derecha
    • Ilustración isométrica del VPS.

Estructura conceptual

[ SECTION: .hero ] (Contenedor de alto impacto / Fondo)
 └── [ .container .hero-content ] (Alineación Flex/Grid)

      ├── [ .hero-text ] (Bloque de Información - Izquierda/Centro)
      │    ├── [ .hero-badge ] ─ "Personal VPS Lab" (Categorización)
      │    ├── [ .hero-title ] ─ "Projects Portal" (H1 - Mensaje Principal)
      │    ├── [ .hero-subtitle ] ─ (Propuesta técnica: DevOps, IA, etc.)
      │    ├── [ .hero-description ] ─ (Detalle del ecosistema: APIs, Dashboards)
      │    └── [ .hero-actions ] (Llamada a la acción / CTA)
      │         ├── [ .btn-primary ] ─ "View Projects"
      │         └── [ .btn-secondary ] ─ "GitHub"

      └── [ .hero-image ] (Bloque Visual - Derecha)
           └── [ img ] ─ "vps-illustration.png" (Ilustración técnica)

Mensaje principal

La sección presentará el portal como: Un laboratorio personal desplegado en un VPS, orientado a DevOps, Ciencia de Datos, MLOps e Ingeniería de Inteligencia Artificial.

Diseño visual

El Hero Section tendrá las siguientes características:

  • Fondo azul oscuro.
  • Texto en color blanco.
  • Diseño en dos columnas.
  • Botones de llamada a la acción.
  • Imagen ilustrativa a la derecha.
  • Amplio espaciado vertical.

Relación con index.html

La página principal incluirá este componente mediante: {% include "components/hero.html" %}

vps_info.html – infraestructura del VPS

El componente vps_info.html muestra un resumen de las principales características técnicas del servidor donde se alojan el Projects Portal y el resto de aplicaciones del portfolio. Esta sección aporta contexto técnico y refuerza la credibilidad del portal al evidenciar que los proyectos se ejecutan sobre una infraestructura real administrada por el propio autor. Ubicación del archivo: app/templates/components/vps_info.html

Propósito

La sección tiene como objetivo presentar, de forma visual y estructurada, la configuración básica del VPS utilizado como entorno de despliegue. Permite al visitante conocer:

  • El sistema operativo del servidor.
  • Los recursos de CPU y memoria.
  • La capacidad de almacenamiento.
  • La IP pública.
  • El dominio asociado al portal.

Información mostrada

  • Sistema Operativo.
  • CPU.
  • RAM.
  • Disco.
  • IP pública.
  • Dominio.

Diseño visual

  • Un título
  • Una cuadrícula de tarjetas.
  • Un icono SVG por cada métrica.
  • El nombre del atributo.
  • Su valor correspondiente.

Estructura conceptual

[ SECTION: #vps-info ] (Ancla de navegación + Fondo de sección)
 └── [ .container ] (Alineación central)
      ├── [ .section-header ]
      │    └── [ h2 ] ── "Infraestructura del VPS"

      └── [ .vps-grid ] (Contenedor Layout: Probablemente Grid o Flexbox)
           ├── [ .vps-card ] ─ (OS: Ubuntu 24.04)
           ├── [ .vps-card ] ─ (CPU: 4 vCPU)
           ├── [ .vps-card ] ─ (RAM: 8 GB)
           ├── [ .vps-card ] ─ (Disco: 160 GB SSD)
           ├── [ .vps-card ] ─ (IP: 186.xxx...)
           └── [ .vps-card ] ─ (Dominio: projects.fernandorioseco.es)

Recursos gráficos

Los iconos utilizados en esta sección se almacenarán en: app/static/img/icons/

Relación con index.html

La página principal incluirá esta sección mediante: {% include "components/vps_info.html" %}

projects.html – catálogo dinámico de proyectos

El componente projects.html es la sección más importante del portal, ya que muestra las aplicaciones desplegadas en el VPS y proporciona acceso directo a cada una de ellas.

A diferencia del resto de secciones, su contenido se genera dinámicamente a partir de los datos almacenados en projects.json. El archivo se ubica en: app/templates/components/projects.html.

La sección incorpora un buscador dinámico que permite filtrar proyectos por cualquier término relevante, como tecnologías, categorías, nombre o palabras incluidas en la descripción. El buscador permite al usuario encontrar proyectos escribiendo uno o varios términos de búsqueda.

Cada tarjeta de proyecto almacena en el atributo data-search el contenido que será evaluado por JavaScript. Este atributo incluye: nombre del proyecto, descripción y tecnologías utilizadas.

Lógica de filtrado

El archivo main.js:

  1. Captura el contenido del input.
  2. Divide el texto por comas.
  3. Normaliza los términos.
  4. Recorre todas las tarjetas.
  5. Muestra solo las que contienen todos los términos.

Propósito

Esta sección tiene como objetivo presentar cada proyecto del portfolio mediante una tarjeta con información resumida y enlaces relevantes. Cada tarjeta permitirá al visitante acceder a:

  • La aplicación desplegada.
  • El artículo de documentación del proyecto.
  • El repositorio de código fuente en GitHub.
  • La documentación de la API, cuando exista.

Fuente de datos

Los datos de los proyectos se almacenarán en el archivo: app/data/projects.json. Cada registro del archivo contendrá toda la información necesaria para construir una tarjeta.

Información mostrada en cada tarjeta

  • Nombre del proyecto.
  • Descripción breve.
  • Tecnologías utilizadas.
  • Enlaces de acción.

Estructura conceptual de una tarjeta

[ DATOS: Lista de Proyectos ] 

      ▼ (Ciclo for project in projects)
[ ARTICLE: .project-card ]  <──────────────────┐
 │                                             │
 ├── [ .project-title ] ─── {{ project.name }} │
 │                                             │
 ├── [ .project-description ] ─ {{ desc }}     │ (Repite por cada
 │                                             │  proyecto en la lista)
 ├── [ .project-tech ] ────────────────────────┤
 │    └── [ .tech-badge ] ─ {{ tech }} (Bucle) │
 │                                             │
 └── [ .project-links ] ───────────────────────┘
      ├── App URL
      ├── Documentation
      ├── GitHub
      └── [ IF: API URL ] ── (Carga condicional)

Generación dinámica con Jinja2

La plantilla recorrerá la lista projects enviada desde Flask mediante un bucle for. Por cada elemento del JSON se generará automáticamente una nueva tarjeta.

Relación con Flask

La ruta principal cargará los datos y los enviará a la plantilla: return render_template("index.html", projects=projects)

Archivos involucrados

La página principal incluirá esta sección mediante: {% include "components/projects.html" %}, en la acción de búsqueda se involucran :

ArchivoFunción
projects.htmlCampo de búsqueda y atributo data-search.
main.jsLógica de filtrado.
styles.cssEstilos del buscador.
base.htmlCarga del archivo JavaScript.

resources.html – recursos y enlaces externos

El componente resources.html agrupa los enlaces externos más relevantes relacionados con el Projects Portal y con el perfil profesional del autor. Su objetivo es ofrecer al visitante un acceso rápido a la documentación técnica, al código fuente y a los perfiles profesionales asociados al proyecto. Ubicación del archivo: app/templates/components/resources.html

Propósito

Esta sección actúa como un bloque complementario al catálogo de proyectos. Mientras que cada tarjeta de proyecto contiene enlaces específicos, resources.html reúne los recursos generales del portal y del autor.

Recursos que se mostrarán

Información mostrada en cada tarjeta

  • Nombre del recurso.
  • Descripción breve.
  • Botón o enlace de acceso.

Diseño visual

La sección se mostrará como una cuadrícula de tarjetas o como una fila de enlaces destacados.

Elementos visuales

  • Título de la sección.
  • Breve texto descriptivo.
  • Tarjetas con icono SVG.
  • Enlaces externos.

Estructura conceptual

[ SECTION: #resources ] (Contenedor de cierre / Footer-Prep)
 └── [ .container ] (Alineación y consistencia visual)
      ├── [ .section-header ]
      │    ├── [ h2 ] ── "Resources"
      │    └── [ p ] ─── (Resumen de contenido externo)

      └── [ .resources-grid ] (Layout de rejilla para acceso rápido)
           ├── [ .resource-card ] ─ (Documentation ➜ Blog)
           ├── [ .resource-card ] ─ (GitHub Repository ➜ Source Code)
           ├── [ .resource-card ] ─ (LinkedIn ➜ Professional Profile)
           └── [ .resource-card ] ─ (Website ➜ Portfolio)

Relación con index.html

La página principal incluirá esta sección mediante: {% include "components/resources.html" %}

footer.html – pie de página

El componente footer.html define la sección final del sitio y contiene información general sobre el portal y enlaces complementarios. Aunque es un elemento sencillo, cumple una función importante al cerrar visualmente la página y ofrecer referencias adicionales al visitante. Ubicación del archivo: app/templates/components/footer.html

Propósito

  • Mostrar información de copyright.
  • Identificar al autor del portal.
  • Incluir enlaces a recursos relevantes.
  • Cerrar visualmente la página de forma consistente.

Información que incluirá

  • Nombre del portal.
  • Año actual.
  • Nombre del autor.
  • Enlaces a GitHub, Blog y LinkedIn.

Estructura conceptual

[ FOOTER: .footer ] (Bloque de cierre / Estilo de fondo)
 └── [ .container .footer-content ] (Alineación y distribución espacial)

      ├── [ .footer-text ] ─── "© 2026 Projects Portal · Fernando Rioseco"
      │                        (Propiedad Intelectual / Marca Personal)

      └── [ .footer-links ] ── (Navegación Secundaria / Social)
           ├── [ Enlace ] ─ GitHub
           ├── [ Enlace ] ─ Blog
           └── [ Enlace ] ─ LinkedIn

Diseño visual

  • Fondo oscuro o gris claro.
  • Texto de tamaño reducido.
  • Enlaces discretos.
  • Distribución horizontal en escritorio.
  • Apilado vertical en dispositivos móviles.

Relación con base.html

La plantilla base lo incluye mediante: {% include "components/footer.html" %}

index.html – ensamblado de la página principal

El archivo index.html es la plantilla principal del Projects Portal. Su función no es definir el contenido detallado de cada sección, sino ensamblar todos los componentes que conforman la página de inicio. Actúa como un punto de integración donde se combinan la plantilla base y los distintos componentes HTML desarrollados previamente. Ubicación del archivo: app/templates/index.html

Propósito

index.html tiene tres responsabilidades principales:

  1. Heredar la estructura general definida en base.html.
  2. Definir el contenido del bloque principal.
  3. Incluir, en el orden correcto, todos los componentes de la interfaz.

Componentes incluidos

La página principal incorpora las siguientes secciones:

  1. hero.html
  2. vps_info.html
  3. tech_stack.html
  4. projects.html
  5. resources.html

La barra de navegación y el footer no se incluyen directamente aquí, ya que son incorporados automáticamente por base.html.

Relación con base.html

La plantilla comienza con: {% extends "base.html" %}. Esto indica que utilizará la estructura general definida en la plantilla base.

Definición del contenido principal

Todo el contenido de la página se inserta dentro del bloque:

{% block content %}
...
{% endblock %}

Estructura conceptual

[ ARCHIVO: base.html ] (El Cascarón)

 ├── [ BLOQUE: content ] <─── (Inyección desde este archivo)
 │    │
 │    ├── [ Hero.html ] ───────── (Impacto Visual / Intro)
 │    ├── [ vps_info.html ] ───── (Hardware / Especificaciones)
 │    ├── [ tech_stack.html ] ─── (Herramientas / Lenguajes)
 │    ├── [ projects.html ] ───── (Portafolio / Apps)
 │    └── [ resources.html ] ──── (Enlaces / Documentación)

 └── [ Footer ] (Desde base.html)

Flujo de renderizado

Cuando Flask renderiza index.html:

  1. Carga base.html.
  2. Sustituye el bloque content.
  3. Inserta los componentes incluidos.
  4. Genera el HTML final enviado al navegador.

Arquitectura CSS

Una vez definida la estructura HTML de la aplicación, el siguiente paso consiste en desarrollar la capa de presentación mediante hojas de estilo CSS. En el Projects Portal se ha optado por utilizar CSS puro, sin frameworks externos como Bootstrap, con el objetivo de mantener un control total sobre el diseño y comprender en detalle el comportamiento visual de cada componente. Todos los estilos del proyecto se almacenan inicialmente en: app/static/css/styles.css

La arquitectura CSS tiene como finalidad:

  • Centralizar todos los estilos de la aplicación.
  • Mantener una organización clara y escalable.
  • Reutilizar reglas comunes.
  • Facilitar el mantenimiento.
  • Garantizar consistencia visual.

El archivo styles.css se estructurará en bloques claramente diferenciados.

styles.css
├── Variables CSS
├── Reset básico
├── Estilos globales
├── Componentes reutilizables
├── Layout
├── Estilos por sección
└── Media queries

La arquitectura CSS del Projects Portal se basa en un único archivo styles.css, organizado en bloques lógicos que incluyen variables, estilos globales, componentes reutilizables y reglas específicas para cada sección de la interfaz.

El Projects Portal ha sido diseñado para adaptarse correctamente a distintos tamaños de pantalla, garantizando una experiencia de usuario adecuada tanto en ordenadores de escritorio como en tablets y teléfonos móviles.

El objetivo del diseño responsive es que todos los elementos de la interfaz se ajusten automáticamente al espacio disponible sin necesidad de crear versiones separadas de la página.

Breakpoints

La adaptación a diferentes resoluciones se realiza mediante media queries. Los puntos de ruptura utilizados se corresponden con los tamaños habituales de dispositivos:

  • Móviles pequeños.
  • Tablets.
  • Portátiles.
  • Monitores de escritorio.

Técnicas utilizadas

Para lograr el comportamiento responsive se emplean:

  • CSS Grid.
  • Flexbox.
  • Media queries.
  • Unidades relativas (rem, %, fr).
  • Imágenes fluidas (max-width: 100%).

Resultado preliminar de la estructura + css

Haz clic en la imagen para ver.

Incorporación de Recursos Visuales y Ajustes Finales de UI

El propósito es transformar una interfaz funcional en una interfaz visualmente pulida.

  • Descarga de iconos
  • Optimización de imágenes
  • Integración de logos tecnológicos
  • Ilustración del Hero Section
  • Ajustes de CSS
  • Refinamiento de tipografía y colores
  • Corrección de detalles responsive
  • Revisión visual final

Resultado final de la aplicación web

4. Configuración del dominio

Una vez completado el desarrollo local de la aplicación, el siguiente paso consiste en asociarla a un dominio público para que pueda ser accesible desde Internet. En este proyecto, la aplicación Flask se publicará en el subdominio: projects.fernandorioseco.es

La configuración del dominio permite:

  • Asociar la aplicación a una URL pública.
  • Dirigir el tráfico al VPS.
  • Servir la aplicación mediante Nginx.
  • Habilitar HTTPS con certificados SSL.

Arquitectura general

Usuario

projects.fernandorioseco.es

DNS

IP pública del VPS

Nginx

Gunicorn

Flask Application

Crear el registro DNS

En el proveedor donde gestionas tu dominio, debes crear un registro de tipo A.

CampoValor
TipoA
Nombreprojects
ValorIP pública del VPS
TTLAuto

Verificar la propagación DNS

Una vez creado el registro, el subdominio debe resolver hacia la IP del servidor.

nslookup projects.fernandorioseco.es

5. Preparación del VPS

Una vez que la aplicación ha sido desarrollada localmente y el subdominio ya apunta al servidor, el siguiente paso consiste en preparar el VPS para alojar el proyecto.

En esta fase se crea la estructura de directorios en el servidor, se descarga el código fuente desde GitHub, se configura un entorno virtual de Python y se instalan las dependencias necesarias para ejecutar la aplicación.

La preparación del VPS tiene como finalidad:

  • Crear el directorio donde residirá el proyecto: /var/www.
  • Descargar el código fuente desde el repositorio remoto.
  • Configurar un entorno virtual aislado.
  • Instalar las librerías definidas en requirements.txt.
  • Verificar que la aplicación puede ejecutarse en el servidor.
# Crear la carpeta del proyecto
sudo mkdir -p /var/www/projects-portal

# Cambiar propietario y grupo de los archivos del proyecto
sudo chown -R $USER:$USER /var/www/projects-portal

# clonar repositorio desde github
git clone https://github.com/fer78/projects-portal.git /var/www/projects-portal

Crear entorno virtual

# en sistemas Debian/Ubuntu no viene instalado por defecto
sudo apt install python3.12-venv

# Accede al directorio
cd /var/www/projects-portal

# Crea el entorno virtual y activalo
python3 -m venv venv
source venv/bin/activate

# Instalar dependencias
pip install --upgrade pip
pip install -r requirements.txt

# comprobar que las dependencias se instalaron correctamente
pip list

Probar la aplicación Flask.

python run.py

Si todo es correcto, Flask iniciará el servidor de desarrollo.

Probar Gunicorn

gunicorn --bind 127.0.0.1:8000 wsgi:app

Este comando confirma que la aplicación está lista para ejecutarse en producción.

Archivos implicados

ArchivoFunción
requirements.txtLista de dependencias Python.
wsgi.pyPunto de entrada para Gunicorn.
venv/Entorno virtual del proyecto.
run.pyScript de desarrollo local.

6. Configuración del servicio con Gunicorn y systemd

Una vez que la aplicación Flask funciona correctamente en el VPS, el siguiente paso consiste en ejecutarla como un servicio del sistema. Para ello utilizaremos:

  • Gunicorn como servidor WSGI.
  • systemd como gestor de servicios de Linux.

Esta configuración permite que la aplicación se inicie automáticamente al arrancar el servidor y se reinicie en caso de fallo.

La configuración del servicio persigue los siguientes objetivos:

  • Ejecutar la aplicación Flask en segundo plano.
  • Iniciar el servicio automáticamente en cada reinicio.
  • Reiniciar el proceso si se detiene inesperadamente.
  • Centralizar los logs en journalctl.

Arquitectura del servicio

systemd

Gunicorn

wsgi.py

Flask Application

Crear el archivo de servicio

sudo nano /etc/systemd/system/projects-portal.service

Definir la unidad de servicio

[Unit]
Description=Gunicorn service for Projects Portal
After=network.target

[Service]
User=tu usuario
Group=www-data
WorkingDirectory=/var/www/projects-portal
Environment="PATH=/var/www/projects-portal/venv/bin"

ExecStart=/var/www/projects-portal/venv/bin/gunicorn \
    --workers 3 \
    --bind 127.0.0.1:8000 \
    wsgi:app

Restart=always

[Install]
WantedBy=multi-user.target

Poner a punto el servicio

# reiniciar systemd
sudo systemctl daemon-reload

# habilitar el servicio
sudo systemctl start projects-portal

# verificar el estado
sudo systemctl status projects-portal

Comandos de administración

# Consultar logs
sudo journalctl -u projects-portal -f

# Reiniciar
sudo systemctl restart projects-portal

# Detener
sudo systemctl stop projects-portal

# Deshabilitar
sudo systemctl disable projects-portal

Resultado esperado

Al finalizar esta fase:

  • Gunicorn se ejecutará como servicio del sistema.
  • La aplicación arrancará automáticamente tras cada reinicio.
  • Los logs estarán disponibles mediante journalctl.
  • El proceso se reiniciará automáticamente ante fallos.

7. Configuración de Nginx y HTTPS

Una vez que la aplicación Flask se ejecuta correctamente mediante Gunicorn y systemd, el siguiente paso consiste en publicarla en Internet a través de un servidor web. Para ello utilizaremos:

  • Nginx como proxy inverso.
  • Certbot para obtener certificados SSL.
  • Let’s Encrypt como autoridad certificadora.

Esta etapa permite:

  • Asociar el subdominio projects.fernandorioseco.es a la aplicación.
  • Redirigir las peticiones HTTP hacia Gunicorn.
  • Servir archivos estáticos directamente.
  • Habilitar HTTPS.
  • Redirigir automáticamente de HTTP a HTTPS.

Arquitectura final

Usuario
   
https://projects.fernandorioseco.es
   
Nginx (puertos 80 y 443)
   
Gunicorn (127.0.0.1:8000)
   
Flask Application

Instalar y Configurar Nginx

El siguiente paso es crear un archivo de configuración de Nginx para la aplicación Flask. Ese archivo define cómo Nginx debe responder cuando alguien acceda a projects.fernandorioseco.es.

sudo apt install nginx
sudo nano /etc/nginx/sites-available/projects-portal
server {
    listen 80;
    server_name projects.fernandorioseco.es;

    location /static/ {
        alias /var/www/projects-portal/app/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Activar el sitio

sudo ln -s /etc/nginx/sites-available/projects-portal \
           /etc/nginx/sites-enabled/
           
# Validar la configuración
sudo nginx -t

# Recargar Nginx
sudo systemctl reload nginx

Configurar HTTPS con Let’s Encrypt

Generar el certificado SSL con Let’s Encrypt y Certbot.

sudo certbot --nginx -d projects.fernandorioseco.es

Verificar acceso seguro

La aplicación deberá quedar disponible en:

https://projects.fernandorioseco.es

Flujo de resolución del dominio

  1. El usuario accede al subdominio.
  2. DNS devuelve la IP del VPS.
  3. Nginx recibe la petición.
  4. Nginx reenvía la petición a Gunicorn.
  5. Gunicorn ejecuta Flask.
  6. Flask devuelve la página renderizada.

8. Actualización y Mantenimiento

Una vez desplegado el portal, es importante definir un procedimiento claro para actualizar el código, incorporar nuevos proyectos y mantener la aplicación operativa en el tiempo. La arquitectura adoptada, basada en archivos JSON individuales y control de versiones con Git, simplifica enormemente estas tareas de establecer un flujo de trabajo para:

  • Desplegar nuevas versiones del código.
  • Añadir proyectos al catálogo.
  • Corregir errores.
  • Reiniciar servicios.
  • Supervisar el funcionamiento del portal.

Flujo de actualización del código

El proceso habitual consiste en:

  1. Realizar cambios en el entorno local (añadir nuevos proyectos o funcionalidades)
  2. Confirmar los cambios en Git.
  3. Enviar los cambios al repositorio remoto.
  4. Actualizar el VPS mediante git pull.
  5. Reiniciar el servicio.
# ve a la carpeta del proyecto
cd /var/www/projects-portal

# Actualiza los ficheros desde github
git pull origin main

# reinicia el servicio 
sudo systemctl restart projects-portal

# verifica el funcionamiento
sudo systemctl status projects-portal

El mantenimiento del portal se reduce a actualizar el repositorio Git, añadir nuevos archivos JSON y reiniciar el servicio Gunicorn cuando sea necesario.

Script de Actualización del Portal

En vez de estar haciendo secuencias de comandos en cada actualización, creamos un script en Bash que agilice el proceso. Este script descargará los últimos cambios desde GitHub, reiniciará el servicio de Gunicorn y mostrará el estado final de la aplicación.

Crear el Script

nano /var/www/projects-portal/update.sh
#!/bin/bash

set -e

echo "=========================================="
echo " Updating Projects Portal"
echo "=========================================="

cd /var/www/projects-portal

echo "Pulling latest changes from GitHub..."
git pull origin main

echo "Restarting Gunicorn service..."
sudo systemctl restart projects-portal

echo "Checking service status..."
sudo systemctl status projects-portal --no-pager

echo "=========================================="
echo " Update completed successfully"
echo "=========================================="

Permisos de ejecución y añadir al PATH.

# permisos de ejecucion
chmod +x /var/www/projects-portal/update.sh

# añadir al path para ejecutar desde cualquier ubicacion
sudo ln -s /var/www/projects-portal/update.sh /usr/local/bin/update-portal

Ahora el script de actualización es un comando que puede ejecutarse desde cualquier ubicación

update-portal

9. Mejoras y Evolución

El Projects Portal se ha diseñado con una arquitectura modular y escalable que facilita la incorporación de nuevas funcionalidades. A medida que el catálogo crezca, el portal puede evolucionar desde una página estática hacia una plataforma más avanzada de gestión y visualización de proyectos.

Posibles mejoras

  • Buscador avanzado: Filtros por tecnología, por tipo de proyecto u ordenación.
  • Métricas del VPS: CPU, memoria, uso de disco, uptime, etc.
  • Panel de administración: Crear y editar proyectos desde una interfaz web, subir imágenes y recursos.
  • Base de datos: migración desde archivos JSON a PostgreSQL.
  • API REST: Exponer los proyectos mediante una API desarrollada con FastAPI.
  • Integración con GitHub: mostrar automáticamente estrellas, forks y fecha de última actualización.
  • Dashboards embebidos: integrar aplicaciones desarrolladas con Streamlit, Dash o Gradio.

Conclusiones

El desarrollo de Projects Portal ha permitido construir una aplicación web completa utilizando una arquitectura moderna basada en Python y Flask, con despliegue profesional sobre un VPS Linux.

Aunque funcionalmente se trata de un portal sencillo, el proyecto integra todos los componentes fundamentales que intervienen en el ciclo de vida de una aplicación web en producción: desarrollo local, control de versiones, despliegue, automatización de servicios, configuración del servidor web y publicación segura mediante HTTPS.

Conocimientos aplicados

A lo largo del proyecto se han puesto en práctica competencias técnicas de distintas áreas.

Desarrollo Backend

  • Programación en Python.
  • Desarrollo web con Flask.
  • Uso de plantillas con Jinja2.
  • Lectura dinámica de archivos JSON.
  • Diseño modular de componentes.

Frontend

  • HTML5 semántico.
  • CSS3 con diseño responsive.
  • JavaScript para búsqueda y filtrado.
  • Integración de recursos gráficos e iconografía.

DevOps y Linux

  • Administración de Ubuntu.
  • Gestión de permisos y estructura de directorios.
  • Uso de entornos virtuales.
  • Automatización con systemd.
  • Despliegue con Gunicorn.
  • Configuración de Nginx.

Redes e Infraestructura

  • Configuración de DNS.
  • Gestión de subdominios.
  • Resolución de nombres.
  • Configuración de HTTPS.
  • Certificados SSL con Let’s Encrypt.

Control de versiones

  • Uso de Git.
  • Publicación en GitHub.
  • Flujo de despliegue basado en git pull.

Diseño arquitectónico

El proyecto ha sido diseñado siguiendo principios de modularidad y mantenibilidad:

  • Plantillas HTML desacopladas.
  • Archivos JSON individuales por proyecto.
  • Separación entre contenido, lógica y presentación.
  • Servicio persistente gestionado por systemd.
  • Proxy inverso con Nginx.

Valor del proyecto

Projects Portal cumple una doble función.

  • Portfolio técnico: Actúa como punto central para acceder a todos los proyectos desplegados.
  • Laboratorio práctico: Sirve como entorno real para aplicar conocimientos.

Reflexión final

Projects Portal demuestra la capacidad de diseñar, desarrollar y desplegar una aplicación web completa siguiendo prácticas profesionales de ingeniería de software e infraestructura.

El proyecto integra desarrollo backend, frontend, administración de sistemas, redes y automatización, constituyendo una evidencia sólida de competencias en programación, análisis de datos, DevOps y tecnologías modernas de Inteligencia Artificial.