La regla de que la red local entra siempre no sirve para el porton, y es
lo que mas despista: una peticion al dominio desde la propia red sale al
router y vuelve reescrita, asi que al proxy le llega con la IP publica.
Sin esa direccion en la lista, uno ve su propia pagina de espera en su
propio ordenador y no hay forma de entender por que.
La IP es fija y contratada, asi que la lista no hay que revisarla.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Con `PROXIMAMENTE=1`, quien no esté en la lista acaba en una página de
espera con el mismo fondo animado que la portada. La red local entra
siempre sin configurar nada: encender el portón y quedarse uno mismo
fuera es la forma más rápida de no poder comprobar si funciona.
Tres decisiones que salieron de probarlo:
- **Redirige, no pinta la espera en la dirección pedida.** Se intentó lo
segundo, que conserva la dirección en la barra, y no vale: el HTML sale
compuesto para /proximamente y el navegador lo hidrata creyendo estar
en otra ruta, así que al terminar de cargar volvía a pintar la cabecera
y el pie que la espera no debe tener.
- **503, no 200.** La web existe pero todavía no atiende. Es lo que
impide que un buscador se lleve el cartel como si fuera el sitio. Y el
robots.txt pasa a cerrar el paso entero mientras dure: si no,
anunciaría un mapa lleno de direcciones que hoy contestan «muy pronto».
- **Rutas de recursos absolutas.** Con las relativas que SvelteKit usa por
defecto, la página servida en otra dirección pedía sus hojas de estilo
en un sitio donde no hay ninguna.
El reconocimiento de direcciones va aparte y con pruebas: una máscara mal
interpretada no da ningún error, solo deja pasar a quien no debía. Ahí
está el caso de `/0`, que en JavaScript se cuela porque el desplazamiento
de 32 bits es un no-op.
Y el portón está abierto mientras nadie diga lo contrario. Una web que se
esconde sola porque falta una variable es peor fallo que una que se ve
antes de tiempo: nadie mira una página que cree no publicada, así que
puede pasar semanas caída sin que se note.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El proxy no lo hace el Nginx de la maquina de la web, sino un Nginx
Proxy Manager en otro sitio de la red. Con eso delante, el Nginx local
sobra: adapter-node ya sirve los estaticos con sus cabeceras de cache, y
un segundo salto solo complica el recuento de IPs.
Y ese recuento importa mas de lo que parece. Sin ADDRESS_HEADER y
XFF_DEPTH, getClientAddress() devuelve la IP del proxy: el limitador del
formulario de acceso cuenta por direccion, asi que con todas las
peticiones pareciendo venir del mismo sitio salta con una sola persona
insistiendo y nadie mas puede pedir su codigo.
El documento incluye que registros de DNS cambiar y, sobre todo, cuales
no tocar: los MX, el SPF y el DKIM son el correo del dominio, y borrar
cualquiera de ellos da un sintoma que aparece dias despues.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cambia las referencias al dominio de trabajo: la URL canónica, los correos
de los textos legales, las direcciones de retorno de Google y Facebook, y
lo que espera la suite.
El `robots.txt` pasa de archivo estático a ruta generada. Llevaba el
dominio escrito a mano y se quedó anunciando el anterior: un mapa del
sitio mal anunciado no da ningún error, simplemente no lo encuentra
nadie. Ahora sale de `site.url`, igual que el mapa y las etiquetas de
cada página. De paso deja fuera del rastreo lo que depende de quién mira
—cuenta, carrito, favoritos, entrar, descargas—, que no es contenido.
Y el Nginx queda listo para el dominio, con los tres pasos que hay que
dar fuera del servidor: los registros A a 89.46.247.16, la redirección de
los puertos 80 y 443 a 192.168.18.171, y Certbot.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Detrás de la cuenta hay compras y facturación, y un código por correo apoya
toda esa puerta en el buzón: quien entre ahí, entra aquí. Delegar la
identidad en un proveedor que ya tiene doble factor la sostiene mejor.
Es el flujo de código de autorización con PKCE, escrito a mano porque son
cien líneas y las partes que importan son tres, mejor a la vista que
detrás de una dependencia: comparar el `state`, mandar el `code_verifier`
y exigir que el correo venga verificado antes de enlazar nada.
Lo que NO hace, a propósito: verificar la firma del `id_token`. La
identidad no se saca de lo que trae el navegador, sino de una llamada
nuestra al proveedor por TLS, así que no hay firma ajena que comprobar.
Y no guarda ningún token: se usa una vez para preguntar quién es y se
tira.
El enlazado por correo verificado es lo delicado, y por eso está separado
en `usuarioParaPerfil` con su explicación: sin exigir la verificación,
cualquiera podría poner la dirección de otro en un perfil suyo y quedarse
con su cuenta y sus compras.
El código por correo se queda debajo, como alternativa. Sin credenciales
puestas, los botones no se pintan y la ruta contesta 503: un botón que
solo lleva a un error es peor que no tenerlo.
Y los textos legales al día, que ahora sí hay terceros: qué se manda a
Google y a Facebook, cuándo, qué se guarda y cómo revocarlo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El usuario ha puesto un servidor con PostgreSQL 16, así que la base pasa allí.
El catálogo no se toca: sigue siendo Markdown del repositorio; lo que se muda es
lo que generan las personas, que es lo único que estaba en la base.
Dos diferencias entre motores que habrían pasado desapercibidas y se cazaron
comprobando con datos de verdad, no compilando:
- `integer` en Postgres son cuatro bytes y en SQLite ocho. El limitador de
envíos guarda su vencimiento en milisegundos desde 1970 —trece dígitos, del
orden del billón— y en `integer` desbordaría, dejando de limitar. Va en
`bigint`.
- `avg()` y `count()` los entrega el driver como cadena para no perder
precisión. Sin convertirlas, `cuantas === 0` nunca se cumpliría y «1 voto»
saldría siempre en plural. Se convierten en la consulta.
Verificado contra el servidor: once tablas creadas, las 113 pruebas de extremo a
extremo y las 224 unitarias en verde, el servidor de producción arrancando, el
limitador escribiendo sus trece dígitos y el borrado en cascada llevándose
valoraciones y favoritos al borrar una cuenta.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>