尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
Turnero — La Peluquería de Ariel Enrique Turnero web para una peluquería unisex de un solo peluquero. Reemplaza la gestión de turnos por WhatsApp/papel sin reemplazar WhatsApp como canal de contacto. Proyecto de portfolio (Ingeniería en Sistemas). Documentación completa del diseño en Docs/: Monorepo: cada carpeta es una app independiente, con su propio package.json y su propio README con instrucciones de desarrollo. • Node.js (instalado vía nvm; ver .nvmrc en cada app) • Una base PostgreSQL (Neon o Supabase) para el backend查看完整 README ↗
Turnero web para una peluquería unisex de un solo peluquero. Reemplaza la gestión de turnos por WhatsApp/papel sin reemplazar WhatsApp como canal de contacto.
Proyecto de portfolio (Ingeniería en Sistemas). Documentación completa del diseño en
Docs/:
historias-de-usuario-casos-de-uso.mdarquitectura.mdmodelo-datos.mdespecificacion-api.mdwireframes-ui.mdfrontend/ React + Vite + TypeScript + Tailwind (Vercel)
backend/ Node + Express + TypeScript + Prisma (Render)
Monorepo: cada carpeta es una app independiente, con su propio package.json y su
propio README con instrucciones de desarrollo.
.nvmrc en cada app)# Backend
cd backend
cp .env.example .env # completar DATABASE_URL
npm install
npm run dev
# Frontend (en otra terminal)
cd frontend
cp .env.example .env
npm install
npm run dev
Backend en Render, frontend en Vercel, base en Neon. Los dos servicios apuntan al mismo repo, cada uno con su carpeta como raíz.
| Campo | Valor |
|---|---|
| Root Directory | backend |
| Build Command | npm install && npm run migrate:deploy && npm run seed |
| Start Command | npm start |
Los tres pasos del build, en orden: npm install dispara el postinstall que corre
prisma generate; migrate:deploy aplica las migraciones (sin esto la base queda sin
tablas y, sobre todo, sin el EXCLUDE que impide la doble reserva); y seed crea los
cuatro servicios, el horario laboral y la cuenta de Ariel.
El seed va en el build a propósito, y no como paso manual: el plan gratuito de Render
no da acceso a shell, así que no hay dónde correrlo a mano. Es seguro que se repita en
cada deploy porque prisma/seed.ts es idempotente — cada bloque comprueba si el dato ya
existe antes de crearlo. En particular, el administrador solo se crea si no existe, así
que si Ariel cambia su contraseña desde el panel, un redeploy no se la revierte.
Si preferís no atar el deploy al seed, la alternativa es correrlo una sola vez desde tu máquina apuntando a la base de producción:
DATABASE_URL="<la-url-de-neon>" npm run seeddentro debackend/. Neon es accesible por internet, así que no hace falta shell en Render.
Variables de entorno (las mismas de backend/.env.example):
DATABASE_URL — la connection string de NeonJWT_SECRET — uno propio, largo y aleatorio. No reusar el de desarrolloADMIN_USUARIO / ADMIN_PASSWORD — solo los lee el seed, para crear la cuentaFRONTEND_URL — la URL de Vercel, sin barra final. Si falta, cae al default
http://localhost:5173 y los mails de confirmación salen con links a localhost, que no
le sirven a ningún clienteVAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT — para el push a Ariel. Las
tres van juntas; si faltan, el push queda apagado y el resto anda igualBREVO_API_KEY, MAIL_FROM, MAIL_FROM_NOMBRE — envío de mail. Sin la key, el mail se
imprime por consola en vez de enviarseMAIL_REPLY_TO — a dónde responden los clientes. Conviene el mail de la peluqueríaJWT_SECRET — firma los tokens de sesión del panel. Cualquiera que lo tenga puede
fabricarse un token válido y entrar como Ariel, así que tiene que ser largo y aleatorio,
distinto del de desarrollo, y no salir nunca del panel de variables de Render:
node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))"
Claves VAPID — identifican a este servidor ante los servicios de push de Google y Apple. Se generan de a pares y van juntas:
cd backend && npx web-push generate-vapid-keys
Imprime una pública y una privada: van en VAPID_PUBLIC_KEY y VAPID_PRIVATE_KEY.
VAPID_SUBJECT es un dato de contacto en formato mailto: (por ejemplo
mailto:[email protected]) que el servicio de push usa si necesita reportar un
problema con los envíos.
Generá un par nuevo para producción y no lo cambies después: las suscripciones que Ariel ya tenga están firmadas contra la clave pública vigente, y si la reemplazás el celular deja de recibir avisos hasta volver a activarlos desde "Mi cuenta".
| Campo | Valor |
|---|---|
| Root Directory | frontend |
| Framework Preset | Vite |
Una sola variable: VITE_API_URL, con la URL de Render más /api — por ejemplo
https://turnero-ariel.onrender.com/api. Se lee en tiempo de build, así que después de
cambiarla hay que redeployar.
Como es una SPA con rutas de cliente (/turno/:id, /admin/...), entrar directo a una de
esas URLs tiene que servir index.html. El preset de Vite no alcanza: sin ayuda,
Vercel busca un archivo físico en /admin, no lo encuentra y devuelve 404 antes de que
React Router llegue a intervenir. Solo funcionaría la raíz, y el link único que va en cada
mail (/turno/:id) quedaría roto para todos los clientes.
Por eso está frontend/vercel.json, que reescribe cualquier ruta a index.html:
{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }
No afecta a los assets (/assets/...): Vercel sirve primero los archivos que existen y
solo reescribe lo que no encuentra.
Seed listo: { servicios: 4, franjas: 10, administradores: 1 } — si dice administradores: 0, faltó ADMIN_USUARIO o
ADMIN_PASSWORDOjo con el plan gratuito de Render: el servicio se suspende tras ~15 minutos sin tráfico y el primer request después tarda bastante. Para un cliente que entra a reservar, eso es una pantalla de carga larga.