el recorrido
Seis fases, meses de separación entre algunas de ellas (auth, luego blog, luego el frontend, el panel de admin, las subidas, luego Caddy y compose atándolo todo junto), y ninguna de ellas había corrido nunca de verdad junta contra el stack en vivo en una sola sesión hasta la comprobación real de lanzamiento: registrar una cuenta real, comentar en un post, darle like, reportarlo, entrar como el admin sembrado, escribir un post con una imagen y un vídeo adjuntos, moderar un comentario, banear a un usuario directamente desde uno de sus propios comentarios. El tipo de recorrido que se supone que es el paso final fácil una vez que cada servicio individual ya tiene su propia batería de pruebas en verde.
En su mayoría fue bien, lo cual fue casi decepcionante después de construir esto durante tanto tiempo medio esperando fuegos artificiales. El registro funcionó, comentar funcionó, el contador de likes se actualizó sin refrescar, reportar un comentario lo puso en cola en el panel de admin exactamente donde debía, y escribir un post con una imagen real y un vídeo real adjuntos pasó por toda la ruta de subida de las últimas entradas sin un solo tropiezo. Banear a un usuario directamente desde uno de sus propios comentarios, algo que antes solo había ejercitado a través de pruebas individuales de manejadores, también funcionó de punta a punta al primer intento real.
la cookie que expira tu sesión en silencio
El único bug real que sacó a la luz el recorrido no estaba dentro de ningún servicio individual, solo apareció una vez que todo corrió junto durante más de unos 15 minutos. El inicio de sesión se supone que te mantiene conectado durante 30 días, ese es todo el sentido del refresh token, pero la lógica de refresco silencioso en hooks.server.ts intenta leer la cookie de refresco en cada carga normal de página:
if (!sessionFromAccessToken) {
const refreshToken = event.cookies.get('refresh_token');
if (refreshToken) {
const response = await event.fetch('/auth/refresh', { method: 'POST' });
...
}
}Excepto que esa cookie está delimitada a Path=/auth, a propósito, para que nunca se envíe al servicio de auth en peticiones que en realidad no la necesitan, manteniéndola fuera, digamos, de los propios registros de acceso de MinIO en una petición de medios no relacionada. Lo cual también significa que el navegador tampoco la envía nunca aquí, en una ruta de página normal, ya que nada de este código corre bajo /auth en absoluto. event.cookies.get('refresh_token') nunca iba a encontrar nada, cada vez, en cualquier página normal del sitio.
El access token por sí solo solo dura 15 minutos. Así que el efecto práctico, inadvertido hasta que de verdad dejé una pestaña abierta y volví a ella más tarde, es que "mantente conectado durante 30 días" significaba en silencio "mantente conectado durante unos 15 minutos", y luego una redirección de aspecto completamente normal de vuelta a la página de inicio de sesión sin ninguna razón que nadie adivinaría sin leer exactamente esta cadena de reglas de delimitación de cookies. Arreglarlo significa o bien ampliar la ruta de esa cookie o hacer la llamada de refresco desde algún sitio que esté de verdad dentro del propio ámbito de /auth, y quiero sentarme un poco con esa disyuntiva antes de comprometerme con una, en lugar de ampliar la ruta solo porque es el diff más pequeño.
el 413 de los comentarios que en realidad nunca pasó, por lo que puedo ver
En algún lugar de una sesión de pruebas anterior me topé con un 413 al publicar un comentario de longitud completamente normal, nada raro en absoluto, y me preocupó lo suficiente como para anotarlo como un bug real que arreglar. Volví más tarde específicamente a perseguirlo y no pude reproducirlo, ni a 100 bytes, ni a 9 kilobytes, ni muy cerca del límite real de 10.000 caracteres de contenido. El propio límite de decodificación JSON de blog para un comentario es de 64 kilobytes, kilómetros por encima de cualquier cosa que un comentario real vaya a alcanzar jamás, y nada más en el stack debería andar cerca de ser tan ajustado tampoco.
Mi mejor suposición, y es genuinamente solo una suposición: lo que fuera que encontré ese día era casi con toda seguridad un resto de un BODY_SIZE_LIMIT anterior, mucho más restrictivo, con el que estaba probando en ese momento, algo transitorio como 3 megabytes o incluso un valor de relleno de cero, no un límite real que llegara a publicarse. Escribí una prueba de regresión que publica un comentario de 9 kilobytes directamente a través de Caddy para dejar fijado que funciona correctamente ahora, y dejé el misterio exactamente igual de sin resolver que eso, un fantasma de una configuración vieja en lugar de un bug vivo escondido todavía en algún sitio. No todo lo que da miedo cuando construyes algo resulta ser algo real cuando de verdad vas a buscarlo como es debido.
lo que todavía queda en la lista
El recorrido en sí pasó. Lo que queda no es un bug, es simplemente el lanzamiento en sí, y lo estoy tratando deliberadamente como su propia decisión separada en lugar de algo que pasa automáticamente en el momento en que las pruebas se ponen en verde: hacer merge de dev a main, hacer push al remoto real de git y ver la pipeline de CI de verdad ponerse en verde contra un registro de contenedores real en vez de mi propia máquina, luego correr el overlay de compose de producción contra un dominio real o uno desechable solo para ver el TLS automático de Caddy de verdad intentar hacer lo suyo por primera vez. Nada de eso ha pasado todavía. Las seis fases están construidas y fusionadas en dev, y cada uno de esos pasos que quedan es explícitamente mío para activar cuando esté listo, no algo que un script decida por mí.
lo que en realidad se me quedó grabado
Nada de esto es complicado por separado. Hashear una contraseña, hacer de proxy de una petición, poner un tope al tamaño de un cuerpo, nada de eso es difícil por sí solo. Lo que en realidad se llevó el tiempo fue todo encontrándose con todo lo demás: una ruta de cookie decidida por las preocupaciones de registro propias de un servicio rompiendo en silencio una suposición del frontend completamente no relacionada tres servicios más allá, un límite de tamaño en el borde eclipsando en silencio uno mucho más generoso dos capas más adentro, una comprobación de rol del lado del cliente que parece exactamente seguridad hasta que de verdad rastreas qué pasa en el momento en que le mientes.
Empecé todo este proyecto sobre todo porque no quería simplemente usar WordPress. Terminé aprendiendo más sobre cómo encajan de verdad las piezas de un servicio web real, y cómo fallan juntas, de lo que cualquier tutorial se molestó jamás en mostrarme. Django y CS50 me enseñaron la sintaxis. Construir esto me enseñó dónde se rompen las cosas de verdad.
Todavía no he fusionado esto a main. Esa parte sigue siendo mía para decidir cuándo.