Fix Resend API key error during build

- Move Resend client instantiation from module level to function
- Add validation to skip placeholder API keys
- Set empty RESEND_API_KEY and GOOGLE_SERVICE_ACCOUNT_JSON during build
This commit is contained in:
Marco Gallegos
2026-01-18 15:08:17 -06:00
parent 0351d8ac9d
commit 8bc9c959b5
4 changed files with 51 additions and 157 deletions

View File

@@ -23,6 +23,8 @@ ENV NEXT_PUBLIC_SUPABASE_URL=https://placeholder.supabase.co
ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=placeholder-anon-key
ENV SUPABASE_SERVICE_ROLE_KEY=placeholder-service-role-key
ENV STRIPE_SECRET_KEY=placeholder
ENV RESEND_API_KEY=""
ENV GOOGLE_SERVICE_ACCOUNT_JSON=""
ENV NODE_OPTIONS="--max-old-space-size=16384"
ENV NEXT_ESLINT_IGNORE_DURING_BUILDS=true
ENV NEXT_PRIVATE_WORKERS=1

36
Dockerfile.coolify Normal file
View File

@@ -0,0 +1,36 @@
# Dockerfile simplificado para Coolify
FROM node:20-alpine
WORKDIR /app
# Instalar dependencias
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
# Copiar código fuente
COPY . .
# Variables de entorno
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
ENV NEXT_PUBLIC_SUPABASE_URL=https://placeholder.supabase.co
ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=placeholder-anon-key
# Aumentar memoria para build
ENV NODE_OPTIONS="--max-old-space-size=4096"
# Build
RUN npm run build
# Configurar usuario
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["npm", "start"]

View File

@@ -5,7 +5,13 @@ import { format } from 'date-fns'
import { es } from 'date-fns/locale'
import { Resend } from 'resend'
const resend = new Resend(process.env.RESEND_API_KEY!)
function getResendClient() {
const apiKey = process.env.RESEND_API_KEY
if (!apiKey || apiKey === 'placeholder' || apiKey === '<REDACTED>') {
return null
}
return new Resend(apiKey)
}
/** @description Send receipt email for booking */
export async function POST(
@@ -105,6 +111,12 @@ export async function POST(
</html>
`
const resend = getResendClient()
if (!resend) {
console.error('RESEND_API_KEY not configured')
return NextResponse.json({ error: 'Email service not configured' }, { status: 500 })
}
const { data: emailResult, error: emailError } = await resend.emails.send({
from: 'ANCHOR:23 <noreply@anchor23.mx>',
to: booking.customer.email,

View File

@@ -1,156 +0,0 @@
import Link from 'next/link'
/**
* @description Testing page with links to all domains and API endpoints
* @audit DEBUG: Internal testing page for route validation
*/
export default function TestLinksPage() {
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="max-w-6xl mx-auto px-4">
<h1 className="text-3xl font-bold text-gray-900 mb-8">🚀 AnchorOS Test Links</h1>
<p className="text-gray-600 mb-8">
Testing page for all AnchorOS domains and API endpoints. Click any link to navigate or test.
</p>
{/* anchor23.mx - Frontend Institucional */}
<div className="bg-white rounded-lg shadow-sm border p-6 mb-6">
<h2 className="text-2xl font-semibold text-green-800 mb-4">🌐 anchor23.mx - Frontend Institucional</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Link href="/" className="text-blue-600 hover:text-blue-800 underline">🏠 Landing Page (/)</Link>
<Link href="/servicios" className="text-blue-600 hover:text-blue-800 underline">💅 Servicios (/servicios)</Link>
<Link href="/historia" className="text-blue-600 hover:text-blue-800 underline">📖 Historia (/historia)</Link>
<Link href="/contacto" className="text-blue-600 hover:text-blue-800 underline">📧 Contacto (/contacto)</Link>
<Link href="/franquicias" className="text-blue-600 hover:text-blue-800 underline">🏢 Franquicias (/franquicias)</Link>
<Link href="/membresias" className="text-blue-600 hover:text-blue-800 underline">👑 Membresías (/membresias)</Link>
<Link href="/privacy-policy" className="text-blue-600 hover:text-blue-800 underline">🔒 Privacy Policy</Link>
<Link href="/legal" className="text-blue-600 hover:text-blue-800 underline"> Legal</Link>
</div>
</div>
{/* booking.anchor23.mx - Frontend de Reservas */}
<div className="bg-white rounded-lg shadow-sm border p-6 mb-6">
<h2 className="text-2xl font-semibold text-blue-800 mb-4">📅 booking.anchor23.mx - Frontend de Reservas</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Link href="/booking/servicios" className="text-blue-600 hover:text-blue-800 underline">💅 Selección de Servicios (/booking/servicios)</Link>
<Link href="/booking/cita" className="text-blue-600 hover:text-blue-800 underline">📝 Flujo de Reserva (/booking/cita)</Link>
<Link href="/booking/registro" className="text-blue-600 hover:text-blue-800 underline">👤 Registro de Cliente (/booking/registro)</Link>
<Link href="/booking/login" className="text-blue-600 hover:text-blue-800 underline">🔐 Login (/booking/login)</Link>
<Link href="/booking/perfil" className="text-blue-600 hover:text-blue-800 underline">👤 Perfil (/booking/perfil)</Link>
<Link href="/booking/mis-citas" className="text-blue-600 hover:text-blue-800 underline">📅 Mis Citas (/booking/mis-citas)</Link>
<Link href="/booking/confirmacion" className="text-blue-600 hover:text-blue-800 underline"> Confirmación (/booking/confirmacion)</Link>
</div>
</div>
{/* aperture.anchor23.mx - Backend Administrativo */}
<div className="bg-white rounded-lg shadow-sm border p-6 mb-6">
<h2 className="text-2xl font-semibold text-purple-800 mb-4"> aperture.anchor23.mx - Backend Administrativo</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Link href="/aperture" className="text-blue-600 hover:text-blue-800 underline">📊 Dashboard Home (/aperture)</Link>
<Link href="/aperture/calendar" className="text-blue-600 hover:text-blue-800 underline">📅 Calendario Maestro (/aperture/calendar)</Link>
<Link href="/aperture/staff" className="text-blue-600 hover:text-blue-800 underline">👥 Gestión de Staff (/aperture/staff)</Link>
<Link href="/aperture/staff/payroll" className="text-blue-600 hover:text-blue-800 underline">💰 Nómina (/aperture/staff/payroll)</Link>
<Link href="/aperture/clients" className="text-blue-600 hover:text-blue-800 underline">👥 Clientes (/aperture/clients)</Link>
<Link href="/aperture/loyalty" className="text-blue-600 hover:text-blue-800 underline">🎁 Fidelización (/aperture/loyalty)</Link>
<Link href="/aperture/pos" className="text-blue-600 hover:text-blue-800 underline">🛒 POS (/aperture/pos)</Link>
<Link href="/aperture/finance" className="text-blue-600 hover:text-blue-800 underline">💸 Finanzas (/aperture/finance)</Link>
<Link href="/aperture/login" className="text-blue-600 hover:text-blue-800 underline">🔐 Login Admin (/aperture/login)</Link>
</div>
</div>
{/* kiosk.anchor23.mx - Sistema de Kiosko */}
<div className="bg-white rounded-lg shadow-sm border p-6 mb-6">
<h2 className="text-2xl font-semibold text-orange-800 mb-4">🖥 kiosk.anchor23.mx - Sistema de Kiosko</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="text-gray-600">🔄 Kiosk system requires physical device with API key</div>
<div className="text-gray-600">📱 Touchscreen interface for walk-ins and confirmations</div>
</div>
</div>
{/* API Endpoints */}
<div className="bg-white rounded-lg shadow-sm border p-6 mb-6">
<h2 className="text-2xl font-semibold text-red-800 mb-4">🔌 API Endpoints - api.anchor23.mx</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Public APIs */}
<div className="font-semibold text-gray-800">🌐 Public APIs:</div>
<div className="col-span-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm">
<span className="text-blue-600">GET /api/services</span>
<span className="text-blue-600">GET /api/locations</span>
<span className="text-blue-600">GET /api/public/availability</span>
<span className="text-blue-600">POST /api/customers</span>
<span className="text-blue-600">POST /api/bookings</span>
</div>
</div>
{/* Aperture APIs */}
<div className="font-semibold text-gray-800"> Aperture APIs:</div>
<div className="col-span-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm">
<span className="text-blue-600">GET /api/aperture/dashboard</span>
<span className="text-blue-600">GET /api/aperture/calendar</span>
<span className="text-blue-600">GET /api/aperture/staff</span>
<span className="text-blue-600">GET /api/aperture/resources</span>
<span className="text-blue-600">POST /api/aperture/bookings/[id]/reschedule</span>
<span className="text-blue-600">GET /api/aperture/payroll</span>
<span className="text-blue-600">GET /api/aperture/pos</span>
<span className="text-blue-600">GET /api/aperture/finance</span>
</div>
</div>
{/* Kiosk APIs */}
<div className="font-semibold text-gray-800">🖥 Kiosk APIs:</div>
<div className="col-span-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm">
<span className="text-blue-600">POST /api/kiosk/walkin</span>
<span className="text-blue-600">GET/POST /api/kiosk/bookings</span>
<span className="text-blue-600">POST /api/kiosk/bookings/[shortId]/confirm</span>
<span className="text-blue-600">POST /api/kiosk/authenticate</span>
<span className="text-blue-600">GET /api/kiosk/resources/available</span>
</div>
</div>
{/* Sync APIs (New in FASE 2) */}
<div className="font-semibold text-gray-800">🔄 Sync APIs (FASE 2):</div>
<div className="col-span-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm">
<span className="text-blue-600">GET /api/sync/calendar/test</span>
<span className="text-blue-600">POST /api/sync/calendar/bookings</span>
<span className="text-blue-600">POST /api/sync/calendar</span>
<span className="text-blue-600">POST /api/sync/calendar/webhook</span>
</div>
</div>
{/* Admin APIs */}
<div className="font-semibold text-gray-800">🔧 Admin APIs:</div>
<div className="col-span-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm">
<span className="text-blue-600">GET /api/admin/locations</span>
<span className="text-blue-600">GET /api/admin/kiosks</span>
<span className="text-blue-600">GET /api/admin/users</span>
<span className="text-blue-600">GET /api/availability/blocks</span>
<span className="text-blue-600">GET /api/availability/staff-unavailable</span>
</div>
</div>
</div>
</div>
{/* Environment Info */}
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<h3 className="font-semibold text-yellow-800 mb-2"> Environment Info</h3>
<div className="text-sm text-yellow-700">
<p><strong>Frontend:</strong> {process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:2311'}</p>
<p><strong>API:</strong> {process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:2311'}/api</p>
<p><strong>Status:</strong> FASE 2 Complete - Google Calendar, Dual Artists, Enhanced Availability</p>
</div>
</div>
{/* Footer */}
<div className="text-center text-gray-500 text-sm mt-8">
<p>AnchorOS Test Links - Internal Development Tool</p>
<p>Last updated: Sprint 2 Completion</p>
</div>
</div>
</div>
)
}