Professional podcast studio booking platform with AWS Lambda email service, CRM, and analytics
A full-stack booking platform for podcast studio management featuring customer booking flow, admin dashboard, AWS Lambda email service, and comprehensive analytics.
Live Site: podspace.vercel.app
- Features
- Tech Stack
- Architecture
- Email System Architecture
- Database Schema
- Getting Started
- Environment Variables
- API Reference
- Admin Dashboard
- AWS Lambda Email Service
- Deployment
- Project Structure
- Multi-step booking flow with real-time pricing calculator
- Interactive calendar with time slot availability
- Service package selection (Audio Recording, Video Podcast, Reels Production)
- Add-on services (cameras, live streaming, editing packages)
- Mobile-responsive design with smooth animations
- Professional email confirmations with studio location
- WhatsApp integration for instant contact
- Real-time booking management with status workflow
- Analytics dashboard with revenue charts and conversion metrics
- Customer relationship management with notes
- Payment tracking (Cash, Card, Apple Pay)
- Contact inquiry management
- Export functionality (CSV)
- Session filtering and search
- AWS Lambda email service (bypasses Vercel SMTP restrictions)
- Intelligent queue system with automatic retry (3 attempts)
- MongoDB audit trail for compliance
- Mobile-responsive email templates
- Rate limiting to avoid spam filters
- Priority queue management
- Dual-mode: Lambda (production) / SMTP (local development)
- Page view tracking and conversion funnels
- Revenue insights by service type
- Peak booking time analysis
- Booking status distribution
- Monthly revenue trends
Next.js 16.0.3 App Router with Server Components
TypeScript 5.x Full type safety
Tailwind CSS v4 Utility-first styling
Framer Motion Smooth animations
Radix UI Accessible components
Lucide React Modern icon library
React Leaflet Interactive maps
Swiper.js Touch carousels
Next.js API Routes Serverless functions
Prisma 6.19.0 Type-safe ORM
MongoDB Atlas Cloud database
AWS Lambda Email service
Nodemailer SMTP delivery
JWT Authentication
Vercel Frontend hosting & CI/CD
AWS Lambda Email service (Node.js 18.x)
AWS API Gateway HTTP endpoint for Lambda
MongoDB Atlas Database hosting
GitHub Version control
┌──────────────────────────────────────────────────────────────────┐
│ CLIENT (Browser) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │
│ │ Landing │ │ Booking │ │ Admin Dashboard │ │
│ │ Page │ │ Flow │ │ /admin/* │ │
│ └─────────────┘ └─────────────┘ └──────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ VERCEL EDGE NETWORK │
│ (CDN + Load Balancer) │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ NEXT.JS SERVER (Vercel Serverless) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │
│ │ Server │ │ API Routes │ │ Middleware │ │
│ │ Components │ │ /api/* │ │ (Auth/CORS) │ │
│ └─────────────┘ └─────────────┘ └──────────────────────┘ │
│ │ │
│ │ (Email Service URL) │
│ ├────────────────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ Prisma ORM │ │ AWS Lambda │ │
│ │ (MongoDB) │ │ Email Service │ │
│ └─────────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────────────────────┐ ┌──────────────────────────┐
│ MONGODB ATLAS │ │ GMAIL SMTP │
│ │ │ smtp.gmail.com:587 │
│ ┌────────────────────────────┐ │ │ │
│ │ Bookings Collection │ │ │ ✅ No Port Blocking │
│ │ EmailQueue Collection │ │ │ ✅ Full SMTP Access │
│ │ EmailLog (Audit Trail) │ │ │ ✅ Reliable Delivery │
│ │ Analytics Collection │ │ │ │
│ └────────────────────────────┘ │ └──────────────────────────┘
└──────────────────────────────────┘
Problem: Vercel blocks outbound SMTP connections on ports 587/465, preventing direct email delivery.
Solution: AWS Lambda HTTP endpoint that bypasses Vercel's restrictions:
┌─────────────────────────────────────────────────────────────────┐
│ BOOKING CREATED (Vercel Serverless Function) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Email Queue System (src/lib/email.ts) │
│ │
│ • Detects EMAIL_SERVICE_URL env variable │
│ • Routes to Lambda if configured, else direct SMTP │
│ • Synchronous processing (await) to prevent timeout │
└─────────────────────────────────────────────────────────────────┘
│
┌──────────────┴──────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────────┐
│ LOCAL DEVELOPMENT │ │ PRODUCTION (Vercel) │
│ Direct SMTP │ │ AWS Lambda │
│ │ │ │
│ smtp.gmail.com:587 │ │ HTTP POST to Lambda │
│ ✅ Works fine │ │ API Gateway Endpoint │
└─────────────────────┘ └─────────────────────────┘
│
▼
┌──────────────────────────────────┐
│ AWS Lambda Function │
│ (podcast-email-service) │
│ │
│ • Receives JSON payload │
│ • Uses Nodemailer internally │
│ • Sends via Gmail SMTP │
│ • Returns success/failure │
│ │
│ Runtime: Node.js 18.x │
│ Memory: 256 MB │
│ Timeout: 30 seconds │
│ Cost: FREE (1M req/month) │
└──────────────────────────────────┘
│
▼
┌──────────────────────────────────┐
│ Gmail SMTP Server │
│ ✉️ Email Delivered │
└──────────────────────────────────┘
All templates are mobile-responsive with consistent styling:
| Template | Trigger | Recipient | Purpose |
|---|---|---|---|
booking_confirmation |
New booking | Customer | Booking details with studio location |
admin_booking |
New booking | Admin | New booking notification with customer info |
contact_admin |
Contact form | Admin | Inquiry notification |
contact_ack |
Contact form | Customer | Acknowledgment with response time |
status_update |
Status change | Customer | Confirmed/Cancelled/Completed notifications |
test |
Manual trigger | Admin | Configuration test |
1. Booking Created
↓
2. queueEmail() → Add to EmailQueue (status: PENDING)
↓
3. await processEmailQueue() → MUST WAIT (Vercel requirement)
↓
4. sendViaLambda() or sendViaSMTP()
↓
├─ Lambda: HTTP POST to API Gateway endpoint
│ ↓
│ Lambda sends via Gmail SMTP
│ ↓
│ Returns { success: true, messageId: "..." }
│
└─ SMTP: Direct connection (localhost only)
↓
Nodemailer.sendMail()
↓
5. Update EmailQueue (status: SENT/FAILED)
↓
6. Log to EmailLog (permanent audit trail)Key Features:
- Synchronous processing -
awaitensures Vercel waits for completion - Automatic retry - 3 attempts for failed emails
- Rate limiting - 1 email/second to avoid spam filters
- Priority queue - High-priority emails sent first
- Audit trail - All emails logged to MongoDB
Primary collection for all studio bookings.
| Field | Type | Description |
|---|---|---|
id |
ObjectId | Unique identifier |
customerName |
String | Full name |
customerEmail |
String | Email address |
customerPhone |
String | Phone number |
selectedDate |
DateTime | Booking date |
selectedTime |
String | Time slot (e.g., "10:00 AM") |
sessionDuration |
Int | Hours (1-8) |
peopleCount |
Int | Number of guests |
selectedSetup |
String | Setup type (standard/video/premium) |
selectedService |
JSON | Service package { id, name, price } |
additionalServices |
String[] | Add-on service IDs |
basePrice |
Float | Base package price (AED) |
addonsTotal |
Float | Total add-ons cost (AED) |
totalPrice |
Float | Final total (AED) |
status |
Enum | PENDING, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED, NO_SHOW |
paymentStatus |
Enum | UNPAID, PAID, REFUNDED, PARTIAL |
paymentMethod |
String | cash, card, apple_pay |
specialRequests |
String | Customer notes |
adminNotes |
String | Internal admin notes |
createdAt |
DateTime | Booking creation timestamp |
confirmedAt |
DateTime | Admin confirmation timestamp |
completedAt |
DateTime | Session completion timestamp |
Indexes: customerEmail, selectedDate, status, createdAt
Intelligent queue for outbound emails with retry logic.
| Field | Type | Description |
|---|---|---|
id |
ObjectId | Queue entry ID |
to |
String | Recipient email |
subject |
String | Email subject |
templateType |
String | Template identifier |
templateData |
JSON | Dynamic data for template |
status |
Enum | PENDING, PROCESSING, SENT, FAILED, CANCELLED |
priority |
Int | Queue priority (0-10, higher = first) |
attempts |
Int | Send attempt counter |
maxAttempts |
Int | Retry limit (default: 3) |
sentAt |
DateTime | Successful send timestamp |
scheduledFor |
DateTime | Scheduled send time |
errorMessage |
String | Failure reason |
createdAt |
DateTime | Queue entry timestamp |
processedAt |
DateTime | Last processing timestamp |
Indexes: status, scheduledFor, priority, createdAt
Permanent audit trail for compliance and debugging.
| Field | Type | Description |
|---|---|---|
id |
ObjectId | Log entry ID |
to |
String | Recipient email |
subject |
String | Email subject |
templateType |
String | Template used |
status |
String | sent, failed |
messageId |
String | SMTP message ID |
errorMessage |
String | Error details (if failed) |
metadata |
JSON | Context (bookingId, contactId) |
createdAt |
DateTime | Log timestamp |
Indexes: to, templateType, status, createdAt
- ServicePackage - Available studio packages (Audio-only, Video, Reels)
- AddOnService - Optional extras (cameras, editing, live streaming)
- BlockedSlot - Unavailable dates/times
- AdminUser - Admin accounts with JWT authentication
- ContactSubmission - Contact form inquiries
- AnalyticsEvent - User behavior tracking
Full schema: prisma/schema.prisma
- Node.js 18+ and npm 9+
- MongoDB Atlas account (free tier)
- AWS account (for Lambda email service)
- Gmail account with app password
# Clone repository
git clone https://github.com/reachvivek/podcast-landing.git
cd podcast-landing
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env with your credentials
# Generate Prisma client
npx prisma generate
# Seed database
npm run seed
# Start development server
npm run devVisit http://localhost:3000
- URL: http://localhost:3000/admin/login
- Credentials: Set via
ADMIN_USERNAMEandADMIN_PASSWORDin.env
# Database
DATABASE_URL="mongodb+srv://username:password@cluster.mongodb.net/podcast-bookings"
# Application
NODE_ENV="production"
NEXT_PUBLIC_APP_URL="https://podspace.vercel.app"
# Email Service (Choose One)
# Option 1: AWS Lambda (Production - Recommended)
EMAIL_SERVICE_URL="https://your-api-id.execute-api.us-east-1.amazonaws.com/"
# Option 2: Direct SMTP (Local Development Only)
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_USER="your-email@gmail.com"
SMTP_PASSWORD="your-gmail-app-password"
FROM_EMAIL="your-email@gmail.com"
ADMIN_EMAIL="admin@example.com"
# Admin Authentication
ADMIN_USERNAME="your-admin-username"
ADMIN_PASSWORD="your-secure-password"
JWT_SECRET="your-secret-key-minimum-32-characters"For Production (Vercel):
Set EMAIL_SERVICE_URL to your AWS Lambda endpoint. Direct SMTP won't work due to Vercel port restrictions.
For Local Development: Use SMTP credentials. Lambda endpoint is optional.
Security Notes:
- Never commit
.envto Git - Use Vercel dashboard to set production env vars
- Rotate secrets before deployment
- Use app-specific passwords for Gmail
The email service runs on AWS Lambda to bypass Vercel's SMTP port blocking.
Lambda Function Details:
- Name:
podcast-email-service - Runtime: Node.js 18.x
- Memory: 256 MB
- Timeout: 30 seconds
- Region: us-east-1
- Cost: $0 (within 1M requests/month free tier)
API Gateway Endpoint:
POST https://your-api-id.execute-api.us-east-1.amazonaws.com/
Content-Type: application/json
{
"to": "customer@example.com",
"subject": "Booking Confirmation",
"html": "<html>...</html>"
}
Response:
{
"success": true,
"messageId": "<abc123@gmail.com>",
"response": "250 2.0.0 OK"
}Located in ../email-lambda/:
index.js- Lambda handler with Nodemailerpackage.json- Dependencies (nodemailer@6.9.8)
The Lambda function is already deployed. To redeploy:
cd ../email-lambda
npm install
zip -r function.zip index.js node_modules package.json
aws lambda update-function-code \
--function-name podcast-email-service \
--zip-file fileb://function.zipCloudWatch Logs:
https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logsV2:log-groups/log-group/$252Faws$252Flambda$252Fpodcast-email-service
Metrics:
- Invocations count
- Error rate
- Duration (avg ~2 seconds)
- Throttles (should be 0)
POST /api/bookings
Content-Type: application/json
{
"customerName": "John Doe",
"customerEmail": "john@example.com",
"customerPhone": "+971501234567",
"selectedDate": "2025-11-25T00:00:00.000Z",
"selectedTime": "10:00 AM",
"sessionDuration": 2,
"peopleCount": 2,
"selectedSetup": "video-2cam",
"selectedService": {
"id": "pkg-2",
"name": "Video Podcast",
"price": 750
},
"additionalServices": ["addon-live"],
"basePrice": 750,
"addonsTotal": 200,
"totalPrice": 950,
"specialRequests": "Need parking"
}Response: 201 Created
GET /api/bookings?status=PENDING&date=2025-11-25&page=1&limit=20
Authorization: Bearer <JWT_TOKEN>Query Parameters:
status- Filter by status (PENDING, CONFIRMED, etc.)date- Filter by date (YYYY-MM-DD)page- Page number (default: 1)limit- Items per page (default: 20)
PATCH /api/bookings/:id
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
{
"status": "CONFIRMED",
"paymentStatus": "PAID",
"paymentMethod": "card",
"adminNotes": "Customer confirmed via WhatsApp"
}GET /api/analytics?period=30
Authorization: Bearer <JWT_TOKEN>Returns:
- Total bookings, revenue, conversion rate
- Popular services, revenue by month
- Booking distribution by status
Dashboard (/admin)
- Today's bookings count
- Weekly revenue (AED)
- Monthly bookings total
- Pending follow-ups
- Recent bookings table
- Quick stats (conversion, popular service)
Bookings (/admin/bookings)
- Filterable table (status, service, date)
- Search (name, phone, email)
- Status workflow management
- Payment tracking
- Export to CSV
Analytics (/admin/analytics)
- Revenue trends (7/30/90 days)
- Booking funnel analysis
- Service popularity charts
- Peak booking times
Messages (/admin/messages)
- Contact form submissions
- Inquiry management
- Response tracking
- JWT-based authentication
- HttpOnly cookies
- Role-based access control
- Protected API routes with middleware
- Session timeout (24 hours)
- Push to GitHub
git add .
git commit -m "Deploy to production"
git push origin master-
Configure Vercel
- Import GitHub repository
- Set environment variables (see
.envsection) - Add
EMAIL_SERVICE_URLfrom Lambda deployment
-
Database Setup
npx prisma generate
npx prisma db push
npm run seed- Post-Deployment Checklist
- Update MongoDB Atlas IP whitelist (0.0.0.0/0 for Vercel)
- Test booking flow end-to-end
- Verify email delivery (check inbox)
- Confirm Lambda is being used (check Vercel logs)
- Set up custom domain
- Enable Vercel analytics
- Create cluster (M0 free tier)
- Create database user
- Allow access from anywhere (0.0.0.0/0)
- Copy connection string to
DATABASE_URL
- Enable 2FA in Google Account
- Generate App Password: Google App Passwords
- Use app password in Lambda environment variables
podcast-landing/
├── prisma/
│ ├── schema.prisma # Database models
│ └── seed.ts # Initial data
│
├── src/
│ ├── app/
│ │ ├── page.tsx # Landing page
│ │ ├── book/page.tsx # Booking flow
│ │ ├── checkout/page.tsx # Confirmation
│ │ ├── admin/ # Admin dashboard
│ │ │ ├── login/page.tsx # Auth
│ │ │ ├── page.tsx # Dashboard
│ │ │ ├── bookings/ # Management
│ │ │ ├── analytics/ # Charts
│ │ │ └── messages/ # Inquiries
│ │ └── api/ # API routes
│ │ ├── bookings/ # CRUD
│ │ ├── analytics/ # Metrics
│ │ ├── contact/ # Forms
│ │ └── auth/ # JWT
│ │
│ ├── components/
│ │ ├── booking/ # Booking steps
│ │ ├── sections/ # Landing sections
│ │ └── ui/ # Reusable UI
│ │
│ ├── contexts/
│ │ └── BookingContext.tsx # Global state
│ │
│ └── lib/
│ ├── prisma.ts # DB client
│ ├── email.ts # Email system (Lambda/SMTP)
│ ├── validations.ts # Zod schemas
│ └── utils.ts # Helpers
│
├── public/images/ # Studio photos
├── .env # Environment vars (gitignored)
├── package.json # Dependencies
└── README.md # This file
# Development
npm run dev # Start dev server (localhost:3000)
npm run build # Production build
npm start # Start production server
# Database
npm run seed # Seed initial data
npx prisma studio # Open Prisma GUI (localhost:5555)
npx prisma generate # Generate Prisma client
npx prisma db push # Push schema to MongoDB
# Code Quality
npm run lint # Run ESLint
npm run type-check # TypeScript validationPodcast EcoSpace Dubai
- 📍 Dubai World Trade Center (DWTC), Dubai, UAE
- 📞 +971 50 206 0674
- ✉️ podcastecospace@gmail.com
- 📸 @podcast.ecospace
Developer
- Vivek Kumar Singh
- 📧 rogerthatvivek@gmail.com
Proprietary - All rights reserved © 2024 Podcast EcoSpace Dubai
Built with ❤️ in Dubai | Powered by Next.js, AWS Lambda, MongoDB & Vercel