尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Directorio de negocios locales de Venezuela. Las personas encuentran negocios cerca, ven reseñas reales y escriben directo al dueño; los negocios consiguen visibilidad, contacto y promociones. En producción: appencuentra.com — se despliega solo desde la rama main vía Vercel. El build es la red de seguridad real: npm run dev tolera errores de tipos que build rechaza, y Vercel construye con build.檢視完整 README ↗
Directorio de negocios locales de Venezuela. Las personas encuentran negocios cerca, ven reseñas reales y escriben directo al dueño; los negocios consiguen visibilidad, contacto y promociones.
En producción: appencuentra.com — se despliega
solo desde la rama main vía Vercel.
| Framework | Next.js 15 (App Router) · React 18 · TypeScript |
| Estilos | Tailwind CSS · framer-motion para overlays |
| Datos y sesión | Supabase (Postgres + Auth + Storage), con RLS |
| Correo | Resend |
| Pagos | PayPal, Binance Pay, y comprobantes manuales revisados a mano |
| Instalable | PWA |
npm install
cp .env.example .env.local # y completar los valores
npm run dev # http://localhost:3000
Antes de subir cualquier cambio:
npm run lint
npx tsc --noEmit
npm run build
El build es la red de seguridad real: npm run dev tolera errores de tipos que
build rechaza, y Vercel construye con build.
Las imprescindibles para que arranque:
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY
SUPABASE_SERVICE_ROLE_KEY # SOLO servidor — salta RLS, nunca exponer al cliente
NEXT_PUBLIC_APP_URL
Según lo que necesites: RESEND_API_KEY y RESEND_FROM_* (correo),
PAYPAL_* y BINANCE_PAY_* (cobros), ADMIN_SETUP_SECRET,
ADMIN_MASTER_PIN y ADMIN_PANEL_PASSWORD (panel), CRON_SECRET (limpieza
programada). La lista completa está en .env.example.
SUPABASE_SERVICE_ROLE_KEYsalta todas las políticas RLS. Sólo puede usarse en rutas de servidor (src/app/api/**, server components). Si aparece en un componente"use client", es un incidente de seguridad.
El esquema vive en dos sitios, con reglas distintas:
supabase/migrations/ — lo nuevo. Va numerado y en orden, así que se
sabe qué sigue a qué. Para que además quede registrado —en
supabase_migrations.schema_migrations, que es lo que permite saber qué
corrió sin adivinar— hay que aplicarlo con el CLI, y para eso falta enlazar
el proyecto:
supabase init # crea supabase/config.toml
supabase link --project-ref <ref> # el ref está en la URL del panel
supabase db push
Mientras no se haga, pegar las migraciones a mano en el editor funciona igual pero no deja registro, que es justo lo que se venía a resolver.
scripts/ — lo anterior. Se pega a mano en el SQL Editor y no deja
registro de nada. Está escrito para ser idempotente: correrlo dos veces no
rompe nada.
Todo cambio de esquema nuevo va como migración. scripts/ no se amplía.
De scripts/, los que tienen que estar sí o sí para que la app funcione
completa:
| Script | Para qué |
|---|---|
support-table.sql | Formulario de soporte y su bandeja en el panel |
panel-admin-control.sql | Moderación de reseñas, suspensión, reversión de pagos y auditoría |
ocultar-negocio.sql | Ocultar un negocio del directorio sin eliminarlo |
create-storage-bucket.sql | Bucket payment_receipts — debe quedar privado |
Para saber qué falta en una base concreta:
scripts/verificar-scripts-aplicados.sql. Solo lee, y devuelve una fila por
script con lo que falta cuando falta.
El bucket
payment_receiptscontiene comprobantes de pago: capturas de transferencias con nombres y números de cuenta. Tiene que seguir siendo privado; el panel genera URLs firmadas de una hora para mostrarlos.El script lo crea privado y, si lo encuentra abierto, lo cierra: pone
public = falsetambién cuando el bucket ya existe, y borra la política de lectura pública. Hasta el 25/09 hacía lo contrario —lo creaba conpublic = true— pese a este mismo párrafo tres líneas más arriba. La base de producción estaba privada, así que no llegó a hacer daño, pero quedaba armado para quien rehiciera el bucket.Para comprobar el estado de una base concreta:
scripts/verificar-scripts-aplicados.sql.
profiles.is_admin es la única fuente de verdad. No se lee
user_metadata.is_admin en ninguna decisión de permiso: ese campo lo puede
escribir el propio usuario desde el navegador, así que aceptarlo era una
escalada de privilegios directa.
Para conceder el rol se actualiza la fila directamente en Supabase. Había una
ruta /api/admin/set-admin para esto, sin autenticación y protegida sólo por
un secreto de instalación; se eliminó porque ya hay administrador y una puerta
así no tiene por qué seguir abierta. De todos modos llevaba tiempo sin
funcionar: leía ADMIN_SETUP_SECRET y la variable configurada se llama
ADMIN_SETUP_KEY.
update public.profiles set is_admin = true where email = '[email protected]';
Para comprobarlo:
select id, email, is_admin from public.profiles where email = '[email protected]';
Toda acción del panel que modere, suspenda o revierta queda registrada en
admin_audit_log y se consulta en Registro → Auditoría.
src/
app/
(public)/ páginas públicas anidadas (soporte, términos, privacidad)
app/auth/ registro, acceso, recuperación
app/dashboard/ la app con sesión
app/admin/ panel interno
api/ rutas de servidor
components/ui/ primitivas compartidas — Overlay, SectionHeader, BottomNav
lib/ lógica de dominio (membresías, navegación, auditoría)
scripts/ SQL, se ejecuta a mano en Supabase
docs/ guías de configuración de servicios externos
Dos convenciones que conviene respetar, porque se rompieron antes:
tailwind.config.ts y globals.css, nunca por
pantalla. Los colores de marca salen del logo; purple está remapeado a
magenta (promociones) y el dorado (amber) es el nivel Patrocina.En docs/: configuración de Google OAuth, PWA, cron de limpieza, PayPal,
sistema de afiliados, reclamo de negocios y pagos manuales.
El historial de git es el registro de qué cambió y por qué. Los mensajes de commit explican el motivo, no sólo el qué — antes eso vivía en un centenar de archivos markdown sueltos en la raíz que nadie actualizaba y que terminaron contradiciendo al código.