- Multi-page SPA with React Router β Home, About, Services, Blog, Contact, Legal
- Services Hierarchy β 3-level nested routing (overview β category β detail)
- Blog System β Static blog listing with individual post pages
- Contact Form β Validated with React Hook Form + Zod, submits to backend API
- Animations β Framer Motion page transitions and component animations
- Fully Responsive β Mobile-first design with Tailwind CSS
- Dark/Light Mode Ready β
next-themesintegration - Accessible Components β Built on Radix UI primitives via shadcn/ui
- Scroll Restoration β Automatically scrolls to top on every route change
- Toast Notifications β Sonner for success/error feedback on form submit
- Legal Pages β Privacy Policy, Terms of Service, Cookie Policy
- Contact Form Email Delivery β Sends formatted HTML email to admin on form submission
- Zoho SMTP Integration β Secure email via port 465 with TLS
- Rate Limiting β 100 req/15 min general limiter; strict 3 req/hour on
/contact - Security Hardening β Helmet CSP, X-Frame-Options, X-XSS-Protection headers
- Structured Logging β Winston logger with file rotation + Morgan HTTP logs
- Graceful Shutdown β SIGTERM/SIGINT handlers with 10-second force-exit fallback
- Health Check Endpoint β Returns uptime, memory usage, environment info
- API Documentation Endpoint β Self-documenting
/api/v1/docsroute - Error Handling β Global error middleware + unhandledRejection/uncaughtException handlers
- Response Compression β gzip compression via
compressionmiddleware - CORS β Whitelisted origins for local dev and production domains
Base URL: https://your-render-url.onrender.com
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1 |
Welcome message + status |
GET |
/api/v1/health |
Server health, uptime, memory |
GET |
/api/v1/docs |
API documentation |
| Method | Endpoint | Auth | Rate Limit | Description |
|---|---|---|---|---|
POST |
/api/v1/contact/submit |
None | 3 req/hour | Submit contact form |
GET |
/api/v1/contact/health |
None | General | Contact service status |
Request Body:
{
"name": "John Doe", // required
"email": "john@example.com", // required
"phone": "+91 9876543210", // required
"message": "Hello...", // required
"company": "Acme Corp", // optional
"service": "Compliance", // optional
"serviceSubcategory": "FEMA" // optional
}Success Response (200):
{
"success": true,
"message": "Email sent successfully"
}Error Response (400 β Missing Fields):
{
"success": false,
"error": {
"message": "Missing required fields",
"code": 400
}
}Error Response (429 β Rate Limited):
{
"success": false,
"error": {
"message": "Too many contact form submissions, try again later.",
"code": 429
}
}# Server
NODE_ENV=development
PORT=5000
# Database (optional β not required for current features)
MONGODB_URI=mongodb+srv://<user>:<pass>@cluster.mongodb.net/unfold
# JWT (configured for future admin panel)
JWT_SECRET=your_super_secret_jwt_key
JWT_EXPIRES_IN=7d
# Zoho SMTP Email
SMTP_HOST=smtp.zoho.in
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=aryan@unfoldfinlegsolutions.com
SMTP_PASS=your_zoho_app_password
SMTP_FROM="Unfold Finleg Solutions" <aryan@unfoldfinlegsolutions.com>
ADMIN_EMAIL=aryan@unfoldfinlegsolutions.com
# CORS (comma-separated for multiple origins on Render)
CORS_ORIGIN=https://unfoldfinlegsolutions.com,https://your-vercel-url.vercel.app
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=100
# Logging
LOG_LEVEL=info
LOG_DIR=./logs
β οΈ Important: In production,JWT_SECRET,MONGODB_URI,SMTP_USER,SMTP_PASS, andADMIN_EMAILare required β the server will exit with code 1 if any are missing.
# Backend API URL
VITE_API_BASE_URL=https://your-render-backend.onrender.com- Node.js v18+ (recommended: v20 LTS)
- npm v9+
- A Zoho Mail account (or any SMTP provider)
# 1. Navigate to frontend directory
cd user
# 2. Install dependencies
npm install
# 3. Create environment file
cp .env.example .env
# Set VITE_API_BASE_URL to your backend URL
# 4. Start development server
npm run dev
# Runs on http://localhost:5173# 1. Navigate to backend directory
cd server
# 2. Install dependencies
npm install
# 3. Create environment file
cp .env.example .env
# Fill in all required variables (see Environment Variables section)
# 4. Start development server (with hot reload)
npm run dev
# Runs on http://localhost:5000
# 5. Verify it's running
curl http://localhost:5000/api/v1/healthnpm run dev # Start Vite dev server with HMR
npm run build # TypeScript compile + Vite production build
npm run preview # Preview production build locally
npm run clean # Remove node_modules/.vite and distnpm run dev # ts-node-dev with hot reload (--respawn --transpile-only)
npm run build # Compile TypeScript β dist/
npm run start # Run compiled JS (node dist/index.js) β used in production
npm run lint # ESLint check on .ts files| Route | Component | Description |
|---|---|---|
/ |
Home.tsx |
Hero, services overview, call-to-action |
/about |
About.tsx |
Company info, team, mission |
/services |
Services/1_Services.tsx |
All service categories |
/services/:categorySlug |
Services/2_ServiceCategory.tsx |
Category-level services |
/services/:categorySlug/:serviceSlug |
Services/3_ServiceDetail.tsx |
Individual service detail |
/blog |
Blogs/1_BlogList.tsx |
All blog posts |
/blog/:slug |
Blogs/2_BlogDetail.tsx |
Single blog post |
/contact |
Contact.tsx |
Contact form + company details |
/privacy |
Legal/x_PrivacyPolicy.tsx |
Privacy Policy |
/terms |
Legal/x_TermsOfService.tsx |
Terms of Service |
/cookies |
Legal/x_CookiePolicy.tsx |
Cookie Policy |
* |
NotFound.tsx |
404 fallback |
All routes are wrapped in a persistent
<Navbar />and<Footer />layout.<ScrollToTop />ensures the page scrolls to the top on every route transition.
Request β βββ helmet() β Security headers (CSP, X-Frame-Options, etc.) βββ compression() β gzip response compression βββ morgan() β HTTP access logging (combined/dev) βββ cors() β Whitelist: localhost + unfoldfinlegsolutions.com βββ generalLimiter β 100 req / 15 min per IP on /api/* βββ contactLimiter β 3 req / 15 min per IP on /api/v1/contact βββ express.json() β JSON body parsing (max 10mb) βββ express.urlencoded() β URL-encoded body parsing (max 10mb) βββ Custom Headers β nosniff, DENY framing, XSS protection β βββ Routes βββ GET /api/v1 β Welcome βββ GET /api/v1/health β Health check βββ GET /api/v1/docs β API docs βββ /api/v1/contact β contactRoutes βββ POST /submit β submitContactForm controller
Client POST /api/v1/contact/submit β βββ Rate limiter check (3 req/hr) βββ Body parsed by express.json() β βββ submitContactForm() βββ Validate required fields (name, email, phone, message) βββ Call sendContactEmail() β βββ Create Nodemailer transporter (Zoho SMTP, port 465) β βββ Build HTML email template β βββ Send to ADMIN_EMAIL with replyTo set to visitor's email βββ Return 200 { success: true }
Winston is configured with structured log levels (info, warn, error) and outputs to both console and rotating log files under ./logs/. Morgan streams HTTP logs through Winston's HTTP log stream.
| Feature | Implementation |
|---|---|
| Security Headers | Helmet with full CSP, X-Frame: DENY, X-XSS-Protection |
| Rate Limiting | express-rate-limit (general + strict contact limiter) |
| CORS Whitelisting | Only specific origins allowed, credentials: true |
| Input Parsing Limit | JSON and URL-encoded body max 10MB |
| Trust Proxy | Enabled for accurate IP detection behind Render/Nginx |
| Error Sanitization | Global error handler strips stack traces in production |
| Graceful Shutdown | SIGTERM/SIGINT handling with 10s forced-exit fallback |
# From /user directory
npm run build # Generates /dist
# vercel.json handles SPA routing (rewrites /* β /index.html)Vercel Settings:
- Framework: Vite
- Build Command:
npm run build - Output Directory:
dist - Root Directory:
user - Add
VITE_API_BASE_URLin Vercel Environment Variables
Render Settings:
- Environment: Node
- Root Directory:
server - Build Command:
npm install && npm run build - Start Command:
npm run start(runsnode dist/index.js) - Add all required env variables from the Environment Variables section in Render dashboard
β οΈ SetCORS_ORIGINon Render to your Vercel deployment URL to allow cross-origin requests.
# 1. Fork the repository
# 2. Create a feature branch
git checkout -b feature/your-feature-name
# 3. Make changes, then commit
git commit -m "feat: add your feature description"
# 4. Push and open a Pull Request
git push origin feature/your-feature-nameCode Style:
- Backend: ESLint + Prettier (TypeScript strict mode)
- Frontend: ESLint with react-hooks and react-refresh plugins
- Follow existing naming conventions (PascalCase components, camelCase utils)
Built with β€οΈ by Vansh Sharma