No description
  • Python 50.7%
  • HTML 35.9%
  • CSS 12.7%
  • Dockerfile 0.4%
  • JavaScript 0.3%
Find a file
Daniel 936548337e Arreglar el acceso a /admin
- /admin sin barra final lo capturaba la ruta comodin /{locale} del sitio
  publico y devolvia 404: se anade una redireccion explicita a /admin/.
- Sin sesion, require_user lanzaba un HTTPException 307 cuya cabecera
  Location se perdia en el manejador global de errores, asi que el navegador
  recibia una redireccion vacia. Ahora usa la excepcion LoginRequired y una
  redireccion 303 a /admin/login.
2026-09-01 16:22:01 +00:00
app Arreglar el acceso a /admin 2026-09-01 16:22:01 +00:00
.dockerignore Commit inicial: web corporativa de AYRE S.L. Ingenieros 2026-09-01 14:38:38 +00:00
.env.example Commit inicial: web corporativa de AYRE S.L. Ingenieros 2026-09-01 14:38:38 +00:00
.gitignore Commit inicial: web corporativa de AYRE S.L. Ingenieros 2026-09-01 14:38:38 +00:00
AYRE.jpg Commit inicial: web corporativa de AYRE S.L. Ingenieros 2026-09-01 14:38:38 +00:00
docker-compose.yml Conectar ayre-web a la red de Nginx Proxy Manager 2026-09-01 17:08:35 +02:00
Dockerfile Commit inicial: web corporativa de AYRE S.L. Ingenieros 2026-09-01 14:38:38 +00:00
README.md Commit inicial: web corporativa de AYRE S.L. Ingenieros 2026-09-01 14:38:38 +00:00
requirements.txt Commit inicial: web corporativa de AYRE S.L. Ingenieros 2026-09-01 14:38:38 +00:00

Web de AYRE S.L. Ingenieros

Web corporativa multiidioma con panel de administración propio, pensada para desplegarse con Docker detrás de Nginx Proxy Manager.

  • Front público: portada, proyectos (con ficha y filtros por categoría), servicios, sobre nosotros / equipo y contacto.
  • Panel en /admin: alta y edición de proyectos con fotos, servicios, equipo, categorías, datos de contacto, idiomas y bandeja de mensajes.
  • Idiomas: español e inglés de serie; añadir otro no requiere tocar la base de datos (ver más abajo).
  • Formulario de contacto: guarda el mensaje en la base de datos y avisa por correo mediante el SMTP corporativo. Si el correo falla, el mensaje no se pierde.

Stack: FastAPI + Jinja2 + SQLAlchemy + PostgreSQL. Sin frameworks de JavaScript ni dependencias externas en el navegador (todo se sirve desde el propio dominio).


1. Puesta en marcha

cp .env.example .env
nano .env                     # rellena las claves (ver abajo)
docker compose up -d --build

La web queda escuchando en 127.0.0.1:8080 del servidor. El panel está en http://<tu-dominio>/admin.

Valores del .env que hay que tocar sí o sí:

Variable Qué es
SECRET_KEY Firma las cookies de sesión. Genérala con python -c "import secrets; print(secrets.token_urlsafe(48))"
POSTGRES_PASSWORD Contraseña de la base de datos
ADMIN_EMAIL / ADMIN_PASSWORD Usuario inicial del panel (solo se usa la primera vez)
SITE_URL URL pública final, p. ej. https://www.ayreingenieria.com
SMTP_* Datos del correo saliente

2. Nginx Proxy Manager

Crea un Proxy Host con:

  • Domain Names: ayreingenieria.com, www.ayreingenieria.com
  • Scheme: http · Forward Hostname/IP: 127.0.0.1 · Port: 8080
  • Block Common Exploits: sí · Websockets: no hace falta
  • SSL: certificado Let's Encrypt + Force SSL + HTTP/2
  • Advanced → pega esto (si no, las fotos de más de 1 MB darán error 413):
client_max_body_size 25m;

Si tu Nginx Proxy Manager corre en Docker y prefieres que hablen por red interna en lugar de por el puerto del host: en docker-compose.yml comenta la línea de ports, descomenta los bloques networks (poniendo el nombre real de la red de NPM) y en el proxy host apunta a ayre-web puerto 8000.

3. Primeros pasos en el panel

  1. Entra en /admin con el usuario de .env y cambia la contraseña en Mi cuenta.
  2. Datos del sitio: teléfono, dirección, CIF, LinkedIn, horario, mapa y los textos de portada en cada idioma.
  3. Proyectos: borra los cuatro de ejemplo y sube los tuyos.
  4. Cuando ya no quieras contenido de ejemplo, pon SEED_DEMO=false en .env y reinicia (docker compose up -d).

Publicar un proyecto

Cada proyecto tiene una pestaña por idioma con título, resumen (lo que se ve en la tarjeta), descripción (los párrafos se separan con una línea en blanco) y alcance de los trabajos (una línea por punto). La ficha lateral se rellena con cliente, ubicación, año y categoría.

Las fotos se redimensionan y comprimen solas al guardar: sube el original tal cual. La de portada conviene que sea apaisada y de al menos 1200 px de ancho.

4. Añadir un idioma nuevo

  1. Panel → Idiomas → añade el código (fr, pt...) y márcalo como activo.
  2. Copia app/locales/es.json a app/locales/<código>.json y traduce los textos fijos (menús, botones, etiquetas). La clave _paths define los trozos de URL de cada sección en ese idioma.
  3. docker compose up -d --build para que el fichero entre en la imagen.
  4. En cada proyecto/servicio aparecerá una pestaña nueva para traducir el contenido. Lo que dejes vacío se muestra en el idioma por defecto.

5. Correo

SMTP_TLS admite starttls (puerto 587, lo habitual), ssl (puerto 465) o none. Si dejas SMTP_HOST vacío el envío queda desactivado y los mensajes solo se ven en el panel — el escritorio te avisa de ello.

Con SEND_AUTOREPLY=true el visitante recibe además un acuse de recibo.

6. Copias de seguridad

# Base de datos
docker exec ayre-db pg_dump -U ayre ayre > backup_$(date +%F).sql

# Fotos
tar czf uploads_$(date +%F).tar.gz data/uploads

Restaurar: cat backup.sql | docker exec -i ayre-db psql -U ayre ayre

7. Actualizar

git pull                      # o edita los ficheros directamente
docker compose up -d --build

El esquema de la base de datos se crea solo al arrancar. Los datos y las fotos viven en el volumen db_data y en data/uploads, así que sobreviven a las reconstrucciones.

8. Estructura

app/
  main.py            arranque, rutas sueltas (sitemap, robots, 404)
  config.py          variables de entorno
  models.py          tablas; el contenido traducible vive en tablas *_translations
  routers/public.py  web pública
  routers/admin.py   panel
  i18n.py            textos fijos y slugs de las URL por idioma
  utils.py           imágenes, slugs, filtros de plantilla
  mailer.py          envío SMTP
  seed.py            idiomas, usuario inicial y contenido de ejemplo
  locales/           es.json, en.json...
  templates/         plantillas Jinja2 (public/ y admin/)
  static/            CSS, JS y logotipos
data/uploads/        fotos subidas desde el panel (volumen Docker)

9. Notas

  • El panel usa cookies de sesión firmadas y token CSRF en todos los formularios. Las contraseñas se guardan con PBKDF2-SHA256.
  • El formulario de contacto lleva trampa antispam (campo oculto) y validación de servidor; no usa servicios de terceros ni cookies de seguimiento.
  • La web no carga nada desde CDNs externos: tipografía del sistema, iconos SVG en línea y CSS propio. Sin cookies de terceros, sin banner de consentimiento.
  • Rutas por idioma: /es/proyectos/<slug> y /en/projects/<slug>, con hreflang y sitemap.xml generados automáticamente.