尚未偵測到更新。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.