lrivasvilla95.duckdns.org 3 servicios activos

luis@vps:~$ whoami

Luis Rivas Villa

Desarrollador Full Stack

Hago la app entera y la dejo corriendo.

Diseño la API, monto el cliente y me encargo del servidor donde acaba corriendo. Mi base es Java y Spring Boot, pero los proyectos de aquí están hechos en Angular, React y Android nativo según lo que pedía cada uno.

Disponible para incorporarme — Sevilla o remoto en España

$ cat stack.txt

  • Java 21
  • Spring Boot 3
  • Kotlin
  • Angular 20
  • React 19
  • Ionic · Capacitor
  • Jetpack Compose
  • PostgreSQL
  • Docker
  • Nginx
  • GitHub Actions

luis@vps:~$ ls proyectos/

swap-closet

web + android
running

Qué es

La ropa que ya no te pones no vale lo bastante para venderla, pero tampoco para tirarla. Swap Closet la pone en circulación: publicas tus prendas para intercambio o para préstamo por días, ves las del resto y propones un trato que las dos partes tienen que aceptar.

La decisión técnica

Un intercambio no es una transacción, es una máquina de estados: pendiente → aceptado o rechazado → completado o cancelado, y cada transición tiene que ser válida para ambas partes. Esa lógica vive entera en la capa de servicio, separada de los controladores y del acceso a datos, para poder cambiar las reglas del intercambio sin tocar la API.

Lo que falta

Documentar la API con OpenAPI, paginar el catálogo y ampliar los filtros de búsqueda. Está identificado, no escondido.

La portada de Swap Closet, con el logo, el lema y el QR de descarga de la app Catálogo de prendas de Swap Closet, con tarjetas de anuncios y filtros por categoría Detalle de un anuncio de Swap Closet, con talla, estado, marca, ubicación y precio por día Formulario de publicación de Swap Closet, con tipo de oferta, categoría, talla, estado, color y estilo La portada de Swap Closet en la app Android El catálogo de Swap Closet en la app Android El formulario de publicación de Swap Closet en la app Android Perfil de usuario de Swap Closet en la app Android, con valoración, intercambios y tallas
El catálogo, con prendas reales 1 / 8
backend
Java 21 · Spring Boot 3
datos
PostgreSQL · Spring Data JPA
auth
Spring Security · BCrypt
imágenes
Cloudinary
cliente
Angular 20 · Ionic 8
android
Capacitor 7

private-chat

web + android
running

Qué es

Un chat donde el servidor no puede leer nada aunque quiera. Solo enruta: no almacena mensajes, no conoce ninguna clave privada y no tiene forma de descifrar lo que pasa por él. Hay dos clientes que hablan entre sí, uno Android nativo y otro web.

La decisión técnica

Las dos partes acuerdan la clave por ECDH sobre P-256 y el contenido viaja cifrado con AES-GCM. El alta de contacto se hace escaneando un QR con el ID y la clave pública, para no depender de un directorio central que habría que custodiar. Lo que no se puede entregar espera en una cola en el servidor hasta que el destinatario confirma con un ACK; la conversación se guarda en Room en Android y en IndexedDB en el navegador, nunca en el servidor.

Lo que falta

Conversaciones de grupo. Es el salto grande: acordar una clave entre más de dos dispositivos, y decidir qué pasa cuando alguien entra o sale del grupo.

Pantalla inicial de Private Chat, con el aviso de cifrado de extremo a extremo Conversación en Private Chat con un mensaje de texto, una imagen adjunta y una nota de voz Diálogo de alta de contacto de Private Chat, por QR o por identificador Pantalla de perfil de Private Chat mostrando el código QR de alta de contacto La lista de conversaciones de Private Chat en la app Android La misma conversación de Private Chat en la app Android, con imagen y nota de voz El perfil y el código QR de Private Chat en la app Android
Texto, imagen y audio en la misma conversación 1 / 7
android
Kotlin · Jetpack Compose
web
React 19 · Vite · PWA
transporte
WebSockets
cifrado
ECDH P-256 · AES-GCM
local
Room · IndexedDB
servidor
Spring Boot · Docker · Nginx

essecilme

web
running

Qué es

Las páginas que traducen tu nombre al élfico suelen inventárselo. Essecilmë lo pasa por cuatro pasos: normaliza el nombre, busca su significado, mira si la Quenya Lapseparma ya lo tradujo y, si no, lo forma con palabras de Eldamo. Después enseña de qué piezas está hecho y si cada una está atestiguada en quenya o solo aparece en otra lengua élfica.

La decisión técnica

El transcriptor a tengwar es propio, con modo quenya clásico y general sindarin, y está contrastado carácter a carácter contra Glaemscribe. El nombre se descarga dibujándolo en canvas en vez de rasterizar el DOM, porque al hacerlo se pierde el posicionamiento de los tehtar sobre cada tengwa. Sin framework y sin base de datos: HTML estático generado con Python.

Lo que falta

Ampliar el corpus con más nombres, vocabulario y frases, y completar el sindarin para que tenga el mismo nivel de cobertura que el quenya.

Portada de Essecilmë, con el paisaje élfico generado por código Un nombre transcrito a tengwar en Essecilmë, con el desglose de cada pieza debajo Listado de frases élficas atestiguadas en Essecilmë, con su transcripción a tengwar Essecilmë en móvil, con un nombre ya transcrito a tengwar Diccionario de Essecilmë en móvil, marcando qué piezas están atestiguadas
El nombre en tengwar, pieza a pieza 1 / 5
corpus
1.019 nombres · 3.680 palabras
fuentes
Eldamo · Quenya Lapseparma
front
JavaScript, sin framework
render
Canvas · SVG por código
build
Python → HTML estático
servidor
Nginx

luis@vps:~$ docker compose ps

Servidor propio

VPS Linux que administro yo, con Nginx como proxy inverso y HTTPS.

Contenedores

Un contenedor por servicio, orquestados con Compose sobre una red interna.

Integración continua

GitHub Actions pasa los tests, construye las imágenes y las publica en Docker Hub.

Despliegue

Cada push a la rama principal actualiza los contenedores sin intervención manual.